Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for external storages on lock, unlock, update, get, and delete #654

Merged

Conversation

mszostok
Copy link
Member

@mszostok mszostok commented Mar 6, 2022

Description

Changes proposed in this pull request:

  • Support external storage backends on:
    • update + add option to update backend.context field
    • get
    • lock
    • unlock
    • delete
  • Store data in built-in storage only if required

New scope:

Testing

The test that I wrote, proves that it works e2e (Go client -> GraphQL -> Local Hub -> External Storage).

  1. Run neo4j:

    docker run -d \
      -p 7687:7687 -p 7474:7474 \
      -e "NEO4J_AUTH=neo4j/okon" \
      -e "NEO4JLABS_PLUGINS=[\"apoc\"]" \
      --name hub-neo4j-instance \
      ghcr.io/capactio/neo4j:4.2.13-apoc
  2. Run modified storage server:

    APP_LOGGER_LEVEL="debug" APP_LOGGER_DEV_MODE=true APP_SUPPORTED_PROVIDERS="dotenv" go run ./cmd/secret-storage-backend/main.go
  3. Run Local Hub:

    cd hub-js; APP_NEO4J_ENDPOINT=bolt://localhost:7687 APP_NEO4J_PASSWORD=okon APP_HUB_MODE=local npm run dev; cd ..
  4. Check that no data is stored: ls /tmp/capact

  5. Run test:

    GRPC_SECRET_STORAGE_BACKEND_ADDR="0.0.0.0:50051" go test ./pkg/hub/client/local/ -v -count 1

    You should output that describe Local Hub state after each operation.

  6. Check that no data is stored: ls /tmp/capactas the above test also cleans up all created TypeInstances and gRPC call is also executed against external backends.

Related issue(s)

Notes

Sometimes I got unpredictable result from dotenv:

#1: graphql: /storage_backend.StorageBackend/GetValue NOT_FOUND: TypeInstance "85e25a0e-76ef-4c26-a4ea-110873aea215" in revision 1 was not found

and in file:

$ /bin/cat /tmp/capact/85e25a0e-76ef-4c26-a4ea-110873aea215
2="{\"updated-value\":\"context 0\"}"
locked_by="demo/testing"%

I'm not sure how this can happen, as it's not possible via exposed API. Probably there is some "race" directly in used library with Put implementation. However, we decided that backends don't need to be thread safe. To resolve that issue I used the mutex lib to prevent concurrent execution of value field resolver.

@mszostok mszostok added enhancement New feature or request area/hub Relates to Hub labels Mar 6, 2022
@capactio capactio deleted a comment from github-actions bot Mar 7, 2022
@capactio capactio deleted a comment from github-actions bot Mar 7, 2022
@capactio capactio deleted a comment from github-actions bot Mar 7, 2022
@capactio capactio deleted a comment from github-actions bot Mar 7, 2022
@capactio capactio deleted a comment from github-actions bot Mar 7, 2022
@capactio capactio deleted a comment from github-actions bot Mar 7, 2022
@capactio capactio deleted a comment from github-actions bot Mar 7, 2022
@capactio capactio deleted a comment from github-actions bot Mar 7, 2022
@capactio capactio deleted a comment from github-actions bot Mar 7, 2022
@capactio capactio deleted a comment from github-actions bot Mar 7, 2022
@mszostok mszostok force-pushed the hub-grpc/lock-unlock-update-get-delete branch from 7e26750 to d13ea72 Compare March 7, 2022 11:49
hub-js/package-lock.json Outdated Show resolved Hide resolved
hub-js/package-lock.json Outdated Show resolved Hide resolved
hub-js/package-lock.json Outdated Show resolved Hide resolved
hub-js/package-lock.json Outdated Show resolved Hide resolved
hub-js/package-lock.json Outdated Show resolved Hide resolved
@mszostok mszostok force-pushed the hub-grpc/lock-unlock-update-get-delete branch from d13ea72 to d8e8eab Compare March 7, 2022 12:30
@mszostok mszostok force-pushed the hub-grpc/lock-unlock-update-get-delete branch from a3819e5 to c845697 Compare March 7, 2022 21:24
@pkosiec pkosiec self-assigned this Mar 8, 2022
Copy link
Member

@pkosiec pkosiec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the provided test works, I wasn't able to test it manually, as I run into some problems:

When I wanted to create TI with Backend ID created during tests (I commented cleanup so I made sure there is such TI), all my mutations failed:

1. Create TI without context

query:

mutation CreateTypeInstances {
  createTypeInstances(
    in: {
     typeInstances: [
      {
        alias: "helm-release"
        typeRef: { path: "cap.type.helm.chart.release", revision: "0.1.0" }
        value: {
          key: "test" # same as it was
        }
        backend: {
          id: "1cd21f38-49f0-42d9-b2f5-c3e69e00c896" # it's like that already.
        }
      }  
      {
        alias: "helm-release2"
        typeRef: { path: "cap.type.helm.chart.release", revision: "0.1.0" }
        value: {
          key: "test" # same as it was
        }
        # backend: {
        #   id: "123" # it's like that already.
        #   context: { # new property of type `Any!`
        #     name: "release-name",
        #     namespace: "release-namespace",
        #   }
        # }
      }    
    ]
    usesRelations: [ # All the TIs exist
      {from: "helm-release", to: "helm-release2"}
      {from: "54f39b4d-280b-406f-afa9-5645328fae70", to: "helm-release2"}
      {from: "61d48278-3c5c-4eb5-8b6b-de4ce4582128", to: "helm-release2"}
    ]
  }
  ) {
    id
    alias
  }
}

logs:

2022-03-09T12:44:58.298+0100	INFO	secret-storage-backend	secret-storage-backend/server.go:314	getting whole secret	{"path": "/tmp/capact/f2863a93-ccc6-46c8-a535-d55836f348e5", "provider": "dotenv"}

output:

{
  "errors": [
    {
      "message": "/storage_backend.StorageBackend/OnDelete NOT_FOUND: path \"/tmp/capact/f2863a93-ccc6-46c8-a535-d55836f348e5\" in provider \"dotenv\" not found",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "createTypeInstances"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "exception": {
          "path": "/storage_backend.StorageBackend/OnDelete",
          "code": 5,
          "details": "path \"/tmp/capact/f2863a93-ccc6-46c8-a535-d55836f348e5\" in provider \"dotenv\" not found",
          "name": "ClientError",
          "stacktrace": [
            "ClientError: /storage_backend.StorageBackend/OnDelete NOT_FOUND: path \"/tmp/capact/f2863a93-ccc6-46c8-a535-d55836f348e5\" in provider \"dotenv\" not found",
            "    at wrapClientError (/Users/pkosiec/repositories/capact/hub-js/node_modules/nice-grpc/lib/client/wrapClientError.js:9:16)",
            "    at Object.callback (/Users/pkosiec/repositories/capact/hub-js/node_modules/nice-grpc/lib/client/createUnaryMethod.js:31:66)",
            "    at Object.onReceiveStatus (/Users/pkosiec/repositories/capact/hub-js/node_modules/@grpc/grpc-js/build/src/client.js:180:36)",
            "    at Object.onReceiveStatus (/Users/pkosiec/repositories/capact/hub-js/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:365:141)",
            "    at Object.onReceiveStatus (/Users/pkosiec/repositories/capact/hub-js/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:328:181)",
            "    at /Users/pkosiec/repositories/capact/hub-js/node_modules/@grpc/grpc-js/build/src/call-stream.js:182:78",
            "    at processTicksAndRejections (node:internal/process/task_queues:78:11)"
          ]
        }
      }
    }
  ],
  "data": null
}

I believe this is "OnDelete" because the transaction tried to do rollback, but it would be good to know the root cause.

2. Create TI with context

This returns a different error:

mutation CreateTypeInstances {
  createTypeInstances(
    in: {
     typeInstances: [
      {
        alias: "helm-release"
        typeRef: { path: "cap.type.helm.chart.release", revision: "0.1.0" }
        value: {
          key: "test" # same as it was
        }
        backend: {
          id: "1cd21f38-49f0-42d9-b2f5-c3e69e00c896"
          context: {
            provider: "mock-me" # same with dotenv
          }
        }
      }  

    ]
    usesRelations: []
  }
  ) {
    id
    alias
  }
}

result:

{
  "errors": [
    {
      "message": "Cannot convert object to primitive value",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "createTypeInstances"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "exception": {
          "stacktrace": [
            "TypeError: Cannot convert object to primitive value",
            "    at TextEncoder.encode (node:internal/encoding:333:32)",
            "    at DelegatedStorageService.encode (/Users/pkosiec/repositories/capact/hub-js/dist/local/storage/service.js:261:34)",
            "    at DelegatedStorageService.Delete (/Users/pkosiec/repositories/capact/hub-js/dist/local/storage/service.js:147:31)",
            "    at processTicksAndRejections (node:internal/process/task_queues:96:5)",
            "    at async createTypeInstances (/Users/pkosiec/repositories/capact/hub-js/dist/local/mutation/create-type-instances.js:44:9)"
          ]
        }
      }
    }
  ],
  "data": null
}

The /tmp/capact is empty in both cases after running the mutation.

Additional data - Get query for backend TI:

query Get($typeInstanceId: ID!) {
  typeInstance(id: $typeInstanceId) {
    resourceVersions {
       spec {
         value
       }
    }
    id
    backend {
       abstract
       id
    }
    usedBy {
      id
    }
  }
}

output:

{
  "data": {
    "typeInstance": {
      "resourceVersions": [
        {
          "spec": {
            "value": {
              "acceptValue": true,
              "contextSchema": "{\n\t\"$id\": \"#/properties/contextSchema\",\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"provider\": {\n\t\t\t\"$id\": \"#/properties/contextSchema/properties/name\",\n\t\t\t\"type\": \"string\",\n\t\t\t\"const\": \"dotenv\"\n\t\t}\n\t},\n\t\"additionalProperties\": false\n}",
              "url": "0.0.0.0:50051"
            }
          }
        }
      ],
      "id": "1cd21f38-49f0-42d9-b2f5-c3e69e00c896",
      "backend": {
        "abstract": true,
        "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
      },
      "usedBy": []
    }
  }
}

@mszostok could you please try by your own and see whether you also encounter such issues? Thanks!

hub-js/src/local/query/spec-value-field.ts Outdated Show resolved Hide resolved
hub-js/src/local/storage/service.ts Outdated Show resolved Hide resolved
hub-js/src/local/storage/service.ts Outdated Show resolved Hide resolved
hub-js/src/local/storage/service.ts Outdated Show resolved Hide resolved
hub-js/src/local/query/spec-value-field.ts Outdated Show resolved Hide resolved
hub-js/src/local/storage/service.ts Outdated Show resolved Hide resolved
});
const cli = await this.getClient(input.backend.id);
if (!cli) {
// TODO(https://github.com/capactio/capact/issues/604): remove after using a real backend in e2e tests.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this is done in #634, let's make sure @mkuziemko is aware of that (that's why I'm mentioning Mati) 🙂

hub-js/src/local/storage/update-args-context.ts Outdated Show resolved Hide resolved
hub-js/src/local/mutation/delete-type-instance.ts Outdated Show resolved Hide resolved
hub-js/src/local/storage/service.ts Outdated Show resolved Hide resolved
Copy link
Member

@pkosiec pkosiec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During manual testing I spotted two bugs. Once they are resolved, the PR is ready to be merged 🙂

Copy link
Member

@pkosiec pkosiec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, great work! 🚀

hub-js/src/local/resolver/field/spec-value-field.ts Outdated Show resolved Hide resolved
@mszostok mszostok force-pushed the hub-grpc/lock-unlock-update-get-delete branch 2 times, most recently from 6a77832 to ebe69db Compare March 10, 2022 17:46
@mszostok mszostok force-pushed the hub-grpc/lock-unlock-update-get-delete branch from ebe69db to 048f86f Compare March 10, 2022 18:10
@mszostok mszostok merged commit d47c527 into capactio:main Mar 11, 2022
@mszostok mszostok deleted the hub-grpc/lock-unlock-update-get-delete branch March 11, 2022 08:16
@mszostok mszostok removed the WIP Work in progress label Mar 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/hub Relates to Hub enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Prevent TypeInstance deletion when it's used by others
2 participants