diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 37456700..ef9d9b09 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -44,20 +44,8 @@ jobs: go vet . golint . - # https://github.com/aws/aws-cli/issues/6920 there's a bug with v1.24 - - uses: azure/setup-kubectl@v2.0 - with: - version: 'v1.23.6' - id: install - - - id: install-aws-cli - uses: unfor19/install-aws-cli-action@v1.0.3 - with: - version: 2.7.6 - arch: amd64 - - name: Test - run: go test ./test/e2e -v -tags=e2e -timeout=180m -parallel 4 + run: go test ./test/e2e -tags=e2e -timeout=180m -parallel 4 env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID_E2E }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_E2E }} diff --git a/.github/workflows/pr-e2e.yml b/.github/workflows/pr-e2e.yml index eb9ca900..2a4d591c 100644 --- a/.github/workflows/pr-e2e.yml +++ b/.github/workflows/pr-e2e.yml @@ -2,7 +2,7 @@ name: "e2e-pr" on: pull_request: branches: [ main ] - types: [ labeled, unlabeled ] + types: [ labeled, unlabeled, opened, synchronize, reopened] jobs: test: @@ -46,21 +46,8 @@ jobs: go vet . golint . - # https://github.com/aws/aws-cli/issues/6920 there's a bug with v1.24 - # https://github.com/aws/aws-cli/issues/6920 there's a bug with v1.24 - - uses: azure/setup-kubectl@v2.0 - with: - version: 'v1.23.6' - id: install - - - id: install-aws-cli - uses: unfor19/install-aws-cli-action@v1.0.3 - with: - version: 2.7.6 - arch: amd64 - - name: Test - run: go test ./test/e2e -v -tags=e2e -timeout=180m -parallel 4 + run: go test ./test/e2e -tags=e2e -timeout=180m -parallel 4 env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID_E2E }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_E2E }} diff --git a/api/deploy/deploy.go b/api/deploy/deploy.go index 232bd328..fb4bf5f6 100644 --- a/api/deploy/deploy.go +++ b/api/deploy/deploy.go @@ -7,7 +7,7 @@ import ( "github.com/multycloud/multy/flags" "github.com/multycloud/multy/resources" "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/types" + "github.com/multycloud/multy/resources/types/metadata" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "log" @@ -63,7 +63,7 @@ func (d DeploymentExecutor) Deploy(ctx context.Context, c *resources.MultyConfig // rollback if something goes wrong if err != nil { log.Println("[ERROR] Something went wrong, rolling back") - originalC, err2 := c.GetOriginalConfig(types.Metadatas) + originalC, err2 := c.GetOriginalConfig(metadata.Metadatas) if err2 != nil { log.Printf("[ERROR] Rollback unsuccessful: %s\n", err2) return diff --git a/api/errors/errors.go b/api/errors/errors.go index c40ab479..a3e7374d 100644 --- a/api/errors/errors.go +++ b/api/errors/errors.go @@ -70,6 +70,10 @@ func ValidationErrors(errs []validate.ValidationError) error { return st.Err() } +func ValidationError(err validate.ValidationError) error { + return ValidationErrors([]validate.ValidationError{err}) +} + func ResourceNotFound(resourceId string) error { st := status.New(codes.NotFound, fmt.Sprintf("resource with id %s not found", resourceId)) st, _ = st.WithDetails(&pberr.ResourceNotFoundDetails{ResourceId: resourceId}) diff --git a/api/proto/multy_service.pb.go b/api/proto/multy_service.pb.go index 86ff1093..c1ae771d 100644 --- a/api/proto/multy_service.pb.go +++ b/api/proto/multy_service.pb.go @@ -91,163 +91,136 @@ var file_api_proto_multy_service_proto_rawDesc = []byte{ 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x25, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x5f, 0x69, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x61, 0x70, 0x69, 0x2f, 0x70, + 0x5f, 0x69, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, - 0x2f, 0x6c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x61, - 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x70, 0x62, 0x2f, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x61, - 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x70, 0x62, 0x2f, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, - 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x2a, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x31, 0x61, 0x70, 0x69, + 0x2f, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x61, 0x70, 0x69, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, + 0x2f, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, - 0x65, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x24, - 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x32, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x27, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x1a, 0x33, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x6e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x48, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x73, - 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x61, 0x73, - 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x22, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x75, 0x73, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0x38, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x32, 0xe2, 0x43, 0x0a, 0x14, - 0x4d, 0x75, 0x6c, 0x74, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, - 0x62, 0x6e, 0x65, 0x74, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, - 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0a, 0x52, 0x65, 0x61, 0x64, 0x53, 0x75, 0x62, - 0x6e, 0x65, 0x74, 0x12, 0x26, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x53, 0x75, - 0x62, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, 0x65, - 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6e, - 0x65, 0x74, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, - 0x75, 0x62, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, - 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2e, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, - 0x6e, 0x65, 0x74, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x12, 0x30, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x69, 0x72, - 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, - 0x00, 0x12, 0x73, 0x0a, 0x12, 0x52, 0x65, 0x61, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x2e, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, - 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, - 0x61, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, - 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x69, - 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x30, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x31, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x24, 0x61, 0x70, 0x69, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, + 0x62, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x32, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, + 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x27, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x33, 0x61, + 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x70, 0x62, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x48, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, + 0x69, 0x74, 0x79, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x61, 0x70, 0x69, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x70, 0x62, 0x2f, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x1b, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x75, 0x73, 0x65, 0x72, + 0x70, 0x62, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x38, 0x0a, + 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x32, 0xee, 0x40, 0x0a, 0x14, 0x4d, 0x75, 0x6c, 0x74, + 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x12, 0x5f, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, + 0x12, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, + 0x6e, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, + 0x00, 0x12, 0x5b, 0x0a, 0x0a, 0x52, 0x65, 0x61, 0x64, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x12, + 0x26, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, + 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x75, + 0x62, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5f, + 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, - 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, - 0x63, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x30, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, - 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6e, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, + 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x53, + 0x75, 0x62, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, + 0x53, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x12, + 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6e, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x32, + 0x74, 0x79, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x69, + 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x30, 0x2e, 0x64, + 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x22, 0x00, 0x12, 0x79, 0x0a, 0x14, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x30, 0x2e, 0x64, 0x65, - 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, - 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x7d, - 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x32, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, - 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x64, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, + 0x12, 0x52, 0x65, 0x61, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x12, 0x2e, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x69, + 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, + 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x22, 0x00, 0x12, 0x77, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, + 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x30, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, - 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, - 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x32, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, - 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, - 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0xc5, 0x01, 0x0a, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, - 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, - 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4a, 0x2e, 0x64, 0x65, 0x76, 0x2e, - 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x45, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, - 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, - 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0xc1, - 0x01, 0x0a, 0x2c, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x48, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, - 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x45, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x65, 0x73, 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x63, 0x0a, 0x14, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x12, 0x30, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, + 0x12, 0x7d, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x32, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, + 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, + 0x79, 0x0a, 0x14, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x30, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, + 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, + 0x61, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, - 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, - 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x22, 0x00, 0x12, 0xc5, 0x01, 0x0a, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x16, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x12, 0x32, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, + 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, 0x16, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, + 0x61, 0x63, 0x65, 0x12, 0x32, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, + 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x22, 0x00, 0x12, 0xc5, 0x01, 0x0a, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, - 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, + 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, @@ -255,439 +228,441 @@ var file_api_proto_multy_service_proto_rawDesc = []byte{ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x97, 0x01, 0x0a, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4a, 0x2e, - 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, - 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, - 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2c, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, - 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, - 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, - 0x00, 0x12, 0x67, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x27, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x10, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2c, - 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x64, - 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2c, 0x2e, 0x64, 0x65, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0xc1, 0x01, 0x0a, 0x2c, 0x52, + 0x65, 0x61, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, + 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, - 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x22, 0x00, 0x12, 0x8c, 0x01, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, - 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, + 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x45, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, + 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, + 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0xc5, + 0x01, 0x0a, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, + 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x4a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, + 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, + 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x45, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, - 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x22, 0x00, 0x12, 0x88, 0x01, 0x0a, 0x19, 0x52, 0x65, 0x61, 0x64, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x35, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, + 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x97, 0x01, 0x0a, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4a, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, + 0x12, 0x6b, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, + 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2c, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, + 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, + 0x0e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, + 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, + 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x64, 0x65, + 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2c, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x8c, - 0x01, 0x0a, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, - 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, - 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, - 0x1b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x2e, 0x64, - 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2c, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, + 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, - 0x12, 0x89, 0x01, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x12, 0x8c, 0x01, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x37, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, + 0x88, 0x01, 0x0a, 0x19, 0x52, 0x65, 0x61, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x2e, + 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x8c, 0x01, 0x0a, 0x1b, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, + 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, + 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x1b, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x73, 0x73, + 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, + 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x41, + 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x89, 0x01, 0x0a, + 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, + 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x36, 0x2e, 0x64, 0x65, + 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, + 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x85, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x61, + 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x34, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, + 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x64, 0x65, + 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, + 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, + 0x12, 0x89, 0x01, 0x0a, 0x1a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x36, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x85, 0x01, 0x0a, - 0x18, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x63, 0x75, - 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x34, 0x2e, 0x64, 0x65, 0x76, 0x2e, - 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, - 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, - 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x31, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x63, - 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x22, 0x00, 0x12, 0x89, 0x01, 0x0a, 0x1a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x12, 0x36, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x64, 0x65, + 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x1a, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x63, + 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x36, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, + 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x65, 0x0a, + 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, + 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, - 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, - 0x12, 0x6f, 0x0a, 0x1a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x36, + 0x73, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, + 0x62, 0x61, 0x73, 0x65, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x44, + 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, - 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, - 0x00, 0x12, 0x65, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, - 0x61, 0x73, 0x65, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x25, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, - 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, - 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, - 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, - 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x2a, 0x2e, - 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, - 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, - 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, - 0x62, 0x61, 0x73, 0x65, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x12, 0x2f, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, - 0x00, 0x12, 0x70, 0x0a, 0x11, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x2d, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, - 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, - 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, - 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x2f, 0x2e, 0x64, 0x65, 0x76, - 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, - 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x64, 0x65, - 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x13, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, - 0x12, 0x2f, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, + 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x61, 0x74, 0x61, + 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x57, + 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, + 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, + 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x2f, + 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x70, 0x0a, + 0x11, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x12, 0x2d, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x86, 0x01, 0x0a, - 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, - 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x35, 0x2e, 0x64, 0x65, 0x76, - 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, - 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x30, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, + 0x74, 0x1a, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x12, 0x33, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, - 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x86, 0x01, 0x0a, 0x19, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x35, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, - 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x30, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x22, 0x00, 0x12, 0x6d, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x12, 0x35, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, - 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x49, 0x70, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x25, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x0c, 0x52, 0x65, 0x61, - 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, - 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, - 0x52, 0x65, 0x61, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x49, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x12, 0x2a, - 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x49, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x64, 0x65, 0x76, - 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x49, 0x70, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, - 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x80, 0x01, 0x0a, - 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, - 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x33, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, - 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, + 0x74, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x2f, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, + 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, + 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x2f, 0x2e, 0x64, + 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x86, 0x01, 0x0a, 0x19, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x35, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, + 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, - 0x7c, 0x0a, 0x15, 0x52, 0x65, 0x61, 0x64, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, - 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x31, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, - 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, - 0x65, 0x61, 0x64, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, 0x75, - 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x64, 0x65, + 0x63, 0x65, 0x73, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, + 0x00, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x33, 0x2e, + 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x86, 0x01, 0x0a, 0x19, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x12, 0x35, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x80, 0x01, - 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, - 0x65, 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x33, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x73, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, + 0x6d, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x35, 0x2e, 0x64, + 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x65, + 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, + 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x49, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x64, + 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x49, 0x70, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, + 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x25, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, + 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x49, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, + 0x57, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, + 0x70, 0x12, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x12, 0x33, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, - 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, - 0x12, 0x69, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, - 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x33, 0x2e, 0x64, 0x65, - 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, - 0x65, 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x83, 0x01, 0x0a, 0x18, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, - 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x34, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, - 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x4e, - 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, - 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x4e, - 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, - 0x00, 0x12, 0x7f, 0x0a, 0x16, 0x52, 0x65, 0x61, 0x64, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, - 0x74, 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x32, 0x2e, 0x64, 0x65, - 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, - 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2f, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, - 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x22, 0x00, 0x12, 0x83, 0x01, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, - 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x12, - 0x34, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, - 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4b, 0x75, 0x62, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, + 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x7c, 0x0a, 0x15, 0x52, + 0x65, 0x61, 0x64, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x12, 0x31, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4b, + 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, + 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4b, 0x75, + 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x33, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, + 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x69, 0x0a, 0x17, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x33, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, + 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, + 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x83, 0x01, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x34, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x50, - 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, - 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, - 0x61, 0x6d, 0x62, 0x64, 0x61, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, - 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x4c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x23, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0a, 0x52, 0x65, 0x61, 0x64, 0x4c, 0x61, - 0x6d, 0x62, 0x64, 0x61, 0x12, 0x26, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4c, - 0x61, 0x6d, 0x62, 0x64, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, + 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x50, + 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x7f, 0x0a, + 0x16, 0x52, 0x65, 0x61, 0x64, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x4e, + 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x32, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, + 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, + 0x61, 0x64, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, + 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x64, 0x65, + 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, + 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x83, + 0x01, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x34, 0x2e, 0x64, 0x65, + 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, + 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2f, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, + 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x75, + 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, + 0x12, 0x34, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x75, 0x62, + 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x6f, 0x6f, 0x6c, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, + 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, + 0x00, 0x12, 0x5c, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, + 0x12, 0x27, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, + 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, + 0x56, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, + 0x58, 0x0a, 0x09, 0x52, 0x65, 0x61, 0x64, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x2e, 0x4c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x6d, - 0x62, 0x64, 0x61, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x4c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, - 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x61, - 0x6d, 0x62, 0x64, 0x61, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x4c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5c, 0x0a, 0x0b, 0x43, 0x72, 0x65, + 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5c, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x27, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, - 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x09, 0x52, 0x65, 0x61, 0x64, 0x56, - 0x61, 0x75, 0x6c, 0x74, 0x12, 0x25, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, - 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x64, 0x65, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x27, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, + 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x33, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, + 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, - 0x00, 0x12, 0x5c, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, - 0x12, 0x27, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, - 0x6c, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x73, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x7c, 0x0a, + 0x15, 0x52, 0x65, 0x61, 0x64, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x31, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, + 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, + 0x64, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, - 0x56, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, - 0x51, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x27, - 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, - 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x00, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, - 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x33, + 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x80, 0x01, 0x0a, 0x17, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x33, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, + 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x64, + 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x69, + 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x33, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x11, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2d, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x7c, 0x0a, 0x15, 0x52, 0x65, 0x61, 0x64, 0x56, 0x61, 0x75, - 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x31, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, + 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x6a, 0x0a, 0x0f, 0x52, 0x65, 0x61, + 0x64, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2b, 0x2e, 0x64, + 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, + 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, + 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2d, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, + 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, + 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2d, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x69, + 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x30, 0x2e, 0x64, + 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, + 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2e, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x22, 0x00, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x61, - 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, - 0x33, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, - 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x69, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x56, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x12, 0x33, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x61, - 0x75, 0x6c, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, - 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, - 0x00, 0x12, 0x6e, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2d, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, - 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, - 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x75, 0x6c, - 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, - 0x00, 0x12, 0x6a, 0x0a, 0x0f, 0x52, 0x65, 0x61, 0x64, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x12, 0x2b, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, - 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, - 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x12, 0x2d, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, - 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, - 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x12, 0x2d, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, - 0x61, 0x75, 0x6c, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x14, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, - 0x68, 0x69, 0x6e, 0x65, 0x12, 0x30, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, - 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x69, 0x72, - 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x12, 0x52, 0x65, 0x61, 0x64, 0x56, 0x69, 0x72, - 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x2e, 0x2e, 0x64, 0x65, - 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, - 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, - 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x14, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, - 0x6e, 0x65, 0x12, 0x30, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, - 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, - 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x22, 0x00, 0x12, 0x63, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x69, 0x72, - 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x30, 0x2e, 0x64, 0x65, - 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, - 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0c, 0x52, 0x65, 0x66, 0x72, - 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, - 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0d, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x17, 0x2e, - 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x27, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, - 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x12, 0x20, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, + 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, + 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, + 0x12, 0x52, 0x65, 0x61, 0x64, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, + 0x69, 0x6e, 0x65, 0x12, 0x2e, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x69, + 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, + 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x22, 0x00, 0x12, 0x77, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, + 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x30, 0x2e, 0x64, 0x65, 0x76, + 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, + 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, + 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, + 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x00, 0x12, 0x63, 0x0a, 0x14, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, + 0x69, 0x6e, 0x65, 0x12, 0x30, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, - 0x32, 0x5f, 0x0a, 0x10, 0x4d, 0x75, 0x6c, 0x74, 0x79, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x4b, 0x0a, 0x0c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x55, 0x73, 0x65, 0x72, 0x12, 0x23, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x55, 0x73, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x64, 0x65, 0x76, 0x2e, - 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x22, - 0x00, 0x42, 0x44, 0x0a, 0x0d, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x61, - 0x70, 0x69, 0x42, 0x0a, 0x4d, 0x75, 0x6c, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x75, 0x6c, - 0x74, 0x79, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x42, 0x0a, 0x0c, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, + 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x17, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x27, + 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x20, 0x2e, 0x64, 0x65, + 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x32, 0x5f, 0x0a, 0x10, 0x4d, 0x75, 0x6c, 0x74, + 0x79, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4b, 0x0a, 0x0c, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x12, 0x23, 0x2e, 0x64, + 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x14, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x75, 0x73, + 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x22, 0x00, 0x42, 0x44, 0x0a, 0x0d, 0x64, 0x65, 0x76, + 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x61, 0x70, 0x69, 0x42, 0x0a, 0x4d, 0x75, 0x6c, 0x74, + 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, + 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -757,48 +732,43 @@ var file_api_proto_multy_service_proto_goTypes = []interface{}{ (*resourcespb.ReadKubernetesNodePoolRequest)(nil), // 50: dev.multy.resources.ReadKubernetesNodePoolRequest (*resourcespb.UpdateKubernetesNodePoolRequest)(nil), // 51: dev.multy.resources.UpdateKubernetesNodePoolRequest (*resourcespb.DeleteKubernetesNodePoolRequest)(nil), // 52: dev.multy.resources.DeleteKubernetesNodePoolRequest - (*resourcespb.CreateLambdaRequest)(nil), // 53: dev.multy.resources.CreateLambdaRequest - (*resourcespb.ReadLambdaRequest)(nil), // 54: dev.multy.resources.ReadLambdaRequest - (*resourcespb.UpdateLambdaRequest)(nil), // 55: dev.multy.resources.UpdateLambdaRequest - (*resourcespb.DeleteLambdaRequest)(nil), // 56: dev.multy.resources.DeleteLambdaRequest - (*resourcespb.CreateVaultRequest)(nil), // 57: dev.multy.resources.CreateVaultRequest - (*resourcespb.ReadVaultRequest)(nil), // 58: dev.multy.resources.ReadVaultRequest - (*resourcespb.UpdateVaultRequest)(nil), // 59: dev.multy.resources.UpdateVaultRequest - (*resourcespb.DeleteVaultRequest)(nil), // 60: dev.multy.resources.DeleteVaultRequest - (*resourcespb.CreateVaultAccessPolicyRequest)(nil), // 61: dev.multy.resources.CreateVaultAccessPolicyRequest - (*resourcespb.ReadVaultAccessPolicyRequest)(nil), // 62: dev.multy.resources.ReadVaultAccessPolicyRequest - (*resourcespb.UpdateVaultAccessPolicyRequest)(nil), // 63: dev.multy.resources.UpdateVaultAccessPolicyRequest - (*resourcespb.DeleteVaultAccessPolicyRequest)(nil), // 64: dev.multy.resources.DeleteVaultAccessPolicyRequest - (*resourcespb.CreateVaultSecretRequest)(nil), // 65: dev.multy.resources.CreateVaultSecretRequest - (*resourcespb.ReadVaultSecretRequest)(nil), // 66: dev.multy.resources.ReadVaultSecretRequest - (*resourcespb.UpdateVaultSecretRequest)(nil), // 67: dev.multy.resources.UpdateVaultSecretRequest - (*resourcespb.DeleteVaultSecretRequest)(nil), // 68: dev.multy.resources.DeleteVaultSecretRequest - (*resourcespb.CreateVirtualMachineRequest)(nil), // 69: dev.multy.resources.CreateVirtualMachineRequest - (*resourcespb.ReadVirtualMachineRequest)(nil), // 70: dev.multy.resources.ReadVirtualMachineRequest - (*resourcespb.UpdateVirtualMachineRequest)(nil), // 71: dev.multy.resources.UpdateVirtualMachineRequest - (*resourcespb.DeleteVirtualMachineRequest)(nil), // 72: dev.multy.resources.DeleteVirtualMachineRequest - (*commonpb.Empty)(nil), // 73: dev.multy.common.Empty - (*userpb.RegisterUserRequest)(nil), // 74: dev.multy.user.RegisterUserRequest - (*resourcespb.SubnetResource)(nil), // 75: dev.multy.resources.SubnetResource - (*resourcespb.VirtualNetworkResource)(nil), // 76: dev.multy.resources.VirtualNetworkResource - (*resourcespb.NetworkInterfaceResource)(nil), // 77: dev.multy.resources.NetworkInterfaceResource - (*resourcespb.NetworkInterfaceSecurityGroupAssociationResource)(nil), // 78: dev.multy.resources.NetworkInterfaceSecurityGroupAssociationResource - (*resourcespb.RouteTableResource)(nil), // 79: dev.multy.resources.RouteTableResource - (*resourcespb.RouteTableAssociationResource)(nil), // 80: dev.multy.resources.RouteTableAssociationResource - (*resourcespb.NetworkSecurityGroupResource)(nil), // 81: dev.multy.resources.NetworkSecurityGroupResource - (*resourcespb.DatabaseResource)(nil), // 82: dev.multy.resources.DatabaseResource - (*resourcespb.ObjectStorageResource)(nil), // 83: dev.multy.resources.ObjectStorageResource - (*resourcespb.ObjectStorageObjectResource)(nil), // 84: dev.multy.resources.ObjectStorageObjectResource - (*resourcespb.PublicIpResource)(nil), // 85: dev.multy.resources.PublicIpResource - (*resourcespb.KubernetesClusterResource)(nil), // 86: dev.multy.resources.KubernetesClusterResource - (*resourcespb.KubernetesNodePoolResource)(nil), // 87: dev.multy.resources.KubernetesNodePoolResource - (*resourcespb.LambdaResource)(nil), // 88: dev.multy.resources.LambdaResource - (*resourcespb.VaultResource)(nil), // 89: dev.multy.resources.VaultResource - (*resourcespb.VaultAccessPolicyResource)(nil), // 90: dev.multy.resources.VaultAccessPolicyResource - (*resourcespb.VaultSecretResource)(nil), // 91: dev.multy.resources.VaultSecretResource - (*resourcespb.VirtualMachineResource)(nil), // 92: dev.multy.resources.VirtualMachineResource - (*commonpb.ListResourcesResponse)(nil), // 93: dev.multy.common.ListResourcesResponse - (*userpb.User)(nil), // 94: dev.multy.user.User + (*resourcespb.CreateVaultRequest)(nil), // 53: dev.multy.resources.CreateVaultRequest + (*resourcespb.ReadVaultRequest)(nil), // 54: dev.multy.resources.ReadVaultRequest + (*resourcespb.UpdateVaultRequest)(nil), // 55: dev.multy.resources.UpdateVaultRequest + (*resourcespb.DeleteVaultRequest)(nil), // 56: dev.multy.resources.DeleteVaultRequest + (*resourcespb.CreateVaultAccessPolicyRequest)(nil), // 57: dev.multy.resources.CreateVaultAccessPolicyRequest + (*resourcespb.ReadVaultAccessPolicyRequest)(nil), // 58: dev.multy.resources.ReadVaultAccessPolicyRequest + (*resourcespb.UpdateVaultAccessPolicyRequest)(nil), // 59: dev.multy.resources.UpdateVaultAccessPolicyRequest + (*resourcespb.DeleteVaultAccessPolicyRequest)(nil), // 60: dev.multy.resources.DeleteVaultAccessPolicyRequest + (*resourcespb.CreateVaultSecretRequest)(nil), // 61: dev.multy.resources.CreateVaultSecretRequest + (*resourcespb.ReadVaultSecretRequest)(nil), // 62: dev.multy.resources.ReadVaultSecretRequest + (*resourcespb.UpdateVaultSecretRequest)(nil), // 63: dev.multy.resources.UpdateVaultSecretRequest + (*resourcespb.DeleteVaultSecretRequest)(nil), // 64: dev.multy.resources.DeleteVaultSecretRequest + (*resourcespb.CreateVirtualMachineRequest)(nil), // 65: dev.multy.resources.CreateVirtualMachineRequest + (*resourcespb.ReadVirtualMachineRequest)(nil), // 66: dev.multy.resources.ReadVirtualMachineRequest + (*resourcespb.UpdateVirtualMachineRequest)(nil), // 67: dev.multy.resources.UpdateVirtualMachineRequest + (*resourcespb.DeleteVirtualMachineRequest)(nil), // 68: dev.multy.resources.DeleteVirtualMachineRequest + (*commonpb.Empty)(nil), // 69: dev.multy.common.Empty + (*userpb.RegisterUserRequest)(nil), // 70: dev.multy.user.RegisterUserRequest + (*resourcespb.SubnetResource)(nil), // 71: dev.multy.resources.SubnetResource + (*resourcespb.VirtualNetworkResource)(nil), // 72: dev.multy.resources.VirtualNetworkResource + (*resourcespb.NetworkInterfaceResource)(nil), // 73: dev.multy.resources.NetworkInterfaceResource + (*resourcespb.NetworkInterfaceSecurityGroupAssociationResource)(nil), // 74: dev.multy.resources.NetworkInterfaceSecurityGroupAssociationResource + (*resourcespb.RouteTableResource)(nil), // 75: dev.multy.resources.RouteTableResource + (*resourcespb.RouteTableAssociationResource)(nil), // 76: dev.multy.resources.RouteTableAssociationResource + (*resourcespb.NetworkSecurityGroupResource)(nil), // 77: dev.multy.resources.NetworkSecurityGroupResource + (*resourcespb.DatabaseResource)(nil), // 78: dev.multy.resources.DatabaseResource + (*resourcespb.ObjectStorageResource)(nil), // 79: dev.multy.resources.ObjectStorageResource + (*resourcespb.ObjectStorageObjectResource)(nil), // 80: dev.multy.resources.ObjectStorageObjectResource + (*resourcespb.PublicIpResource)(nil), // 81: dev.multy.resources.PublicIpResource + (*resourcespb.KubernetesClusterResource)(nil), // 82: dev.multy.resources.KubernetesClusterResource + (*resourcespb.KubernetesNodePoolResource)(nil), // 83: dev.multy.resources.KubernetesNodePoolResource + (*resourcespb.VaultResource)(nil), // 84: dev.multy.resources.VaultResource + (*resourcespb.VaultAccessPolicyResource)(nil), // 85: dev.multy.resources.VaultAccessPolicyResource + (*resourcespb.VaultSecretResource)(nil), // 86: dev.multy.resources.VaultSecretResource + (*resourcespb.VirtualMachineResource)(nil), // 87: dev.multy.resources.VirtualMachineResource + (*commonpb.ListResourcesResponse)(nil), // 88: dev.multy.common.ListResourcesResponse + (*userpb.User)(nil), // 89: dev.multy.user.User } var file_api_proto_multy_service_proto_depIdxs = []int32{ 1, // 0: dev.multy.MultyResourceService.CreateSubnet:input_type -> dev.multy.resources.CreateSubnetRequest @@ -853,108 +823,100 @@ var file_api_proto_multy_service_proto_depIdxs = []int32{ 50, // 49: dev.multy.MultyResourceService.ReadKubernetesNodePool:input_type -> dev.multy.resources.ReadKubernetesNodePoolRequest 51, // 50: dev.multy.MultyResourceService.UpdateKubernetesNodePool:input_type -> dev.multy.resources.UpdateKubernetesNodePoolRequest 52, // 51: dev.multy.MultyResourceService.DeleteKubernetesNodePool:input_type -> dev.multy.resources.DeleteKubernetesNodePoolRequest - 53, // 52: dev.multy.MultyResourceService.CreateLambda:input_type -> dev.multy.resources.CreateLambdaRequest - 54, // 53: dev.multy.MultyResourceService.ReadLambda:input_type -> dev.multy.resources.ReadLambdaRequest - 55, // 54: dev.multy.MultyResourceService.UpdateLambda:input_type -> dev.multy.resources.UpdateLambdaRequest - 56, // 55: dev.multy.MultyResourceService.DeleteLambda:input_type -> dev.multy.resources.DeleteLambdaRequest - 57, // 56: dev.multy.MultyResourceService.CreateVault:input_type -> dev.multy.resources.CreateVaultRequest - 58, // 57: dev.multy.MultyResourceService.ReadVault:input_type -> dev.multy.resources.ReadVaultRequest - 59, // 58: dev.multy.MultyResourceService.UpdateVault:input_type -> dev.multy.resources.UpdateVaultRequest - 60, // 59: dev.multy.MultyResourceService.DeleteVault:input_type -> dev.multy.resources.DeleteVaultRequest - 61, // 60: dev.multy.MultyResourceService.CreateVaultAccessPolicy:input_type -> dev.multy.resources.CreateVaultAccessPolicyRequest - 62, // 61: dev.multy.MultyResourceService.ReadVaultAccessPolicy:input_type -> dev.multy.resources.ReadVaultAccessPolicyRequest - 63, // 62: dev.multy.MultyResourceService.UpdateVaultAccessPolicy:input_type -> dev.multy.resources.UpdateVaultAccessPolicyRequest - 64, // 63: dev.multy.MultyResourceService.DeleteVaultAccessPolicy:input_type -> dev.multy.resources.DeleteVaultAccessPolicyRequest - 65, // 64: dev.multy.MultyResourceService.CreateVaultSecret:input_type -> dev.multy.resources.CreateVaultSecretRequest - 66, // 65: dev.multy.MultyResourceService.ReadVaultSecret:input_type -> dev.multy.resources.ReadVaultSecretRequest - 67, // 66: dev.multy.MultyResourceService.UpdateVaultSecret:input_type -> dev.multy.resources.UpdateVaultSecretRequest - 68, // 67: dev.multy.MultyResourceService.DeleteVaultSecret:input_type -> dev.multy.resources.DeleteVaultSecretRequest - 69, // 68: dev.multy.MultyResourceService.CreateVirtualMachine:input_type -> dev.multy.resources.CreateVirtualMachineRequest - 70, // 69: dev.multy.MultyResourceService.ReadVirtualMachine:input_type -> dev.multy.resources.ReadVirtualMachineRequest - 71, // 70: dev.multy.MultyResourceService.UpdateVirtualMachine:input_type -> dev.multy.resources.UpdateVirtualMachineRequest - 72, // 71: dev.multy.MultyResourceService.DeleteVirtualMachine:input_type -> dev.multy.resources.DeleteVirtualMachineRequest - 73, // 72: dev.multy.MultyResourceService.RefreshState:input_type -> dev.multy.common.Empty - 73, // 73: dev.multy.MultyResourceService.ListResources:input_type -> dev.multy.common.Empty - 0, // 74: dev.multy.MultyResourceService.DeleteResource:input_type -> dev.multy.DeleteResourceRequest - 74, // 75: dev.multy.MultyUserService.RegisterUser:input_type -> dev.multy.user.RegisterUserRequest - 75, // 76: dev.multy.MultyResourceService.CreateSubnet:output_type -> dev.multy.resources.SubnetResource - 75, // 77: dev.multy.MultyResourceService.ReadSubnet:output_type -> dev.multy.resources.SubnetResource - 75, // 78: dev.multy.MultyResourceService.UpdateSubnet:output_type -> dev.multy.resources.SubnetResource - 73, // 79: dev.multy.MultyResourceService.DeleteSubnet:output_type -> dev.multy.common.Empty - 76, // 80: dev.multy.MultyResourceService.CreateVirtualNetwork:output_type -> dev.multy.resources.VirtualNetworkResource - 76, // 81: dev.multy.MultyResourceService.ReadVirtualNetwork:output_type -> dev.multy.resources.VirtualNetworkResource - 76, // 82: dev.multy.MultyResourceService.UpdateVirtualNetwork:output_type -> dev.multy.resources.VirtualNetworkResource - 73, // 83: dev.multy.MultyResourceService.DeleteVirtualNetwork:output_type -> dev.multy.common.Empty - 77, // 84: dev.multy.MultyResourceService.CreateNetworkInterface:output_type -> dev.multy.resources.NetworkInterfaceResource - 77, // 85: dev.multy.MultyResourceService.ReadNetworkInterface:output_type -> dev.multy.resources.NetworkInterfaceResource - 77, // 86: dev.multy.MultyResourceService.UpdateNetworkInterface:output_type -> dev.multy.resources.NetworkInterfaceResource - 73, // 87: dev.multy.MultyResourceService.DeleteNetworkInterface:output_type -> dev.multy.common.Empty - 78, // 88: dev.multy.MultyResourceService.CreateNetworkInterfaceSecurityGroupAssociation:output_type -> dev.multy.resources.NetworkInterfaceSecurityGroupAssociationResource - 78, // 89: dev.multy.MultyResourceService.ReadNetworkInterfaceSecurityGroupAssociation:output_type -> dev.multy.resources.NetworkInterfaceSecurityGroupAssociationResource - 78, // 90: dev.multy.MultyResourceService.UpdateNetworkInterfaceSecurityGroupAssociation:output_type -> dev.multy.resources.NetworkInterfaceSecurityGroupAssociationResource - 73, // 91: dev.multy.MultyResourceService.DeleteNetworkInterfaceSecurityGroupAssociation:output_type -> dev.multy.common.Empty - 79, // 92: dev.multy.MultyResourceService.CreateRouteTable:output_type -> dev.multy.resources.RouteTableResource - 79, // 93: dev.multy.MultyResourceService.ReadRouteTable:output_type -> dev.multy.resources.RouteTableResource - 79, // 94: dev.multy.MultyResourceService.UpdateRouteTable:output_type -> dev.multy.resources.RouteTableResource - 73, // 95: dev.multy.MultyResourceService.DeleteRouteTable:output_type -> dev.multy.common.Empty - 80, // 96: dev.multy.MultyResourceService.CreateRouteTableAssociation:output_type -> dev.multy.resources.RouteTableAssociationResource - 80, // 97: dev.multy.MultyResourceService.ReadRouteTableAssociation:output_type -> dev.multy.resources.RouteTableAssociationResource - 80, // 98: dev.multy.MultyResourceService.UpdateRouteTableAssociation:output_type -> dev.multy.resources.RouteTableAssociationResource - 73, // 99: dev.multy.MultyResourceService.DeleteRouteTableAssociation:output_type -> dev.multy.common.Empty - 81, // 100: dev.multy.MultyResourceService.CreateNetworkSecurityGroup:output_type -> dev.multy.resources.NetworkSecurityGroupResource - 81, // 101: dev.multy.MultyResourceService.ReadNetworkSecurityGroup:output_type -> dev.multy.resources.NetworkSecurityGroupResource - 81, // 102: dev.multy.MultyResourceService.UpdateNetworkSecurityGroup:output_type -> dev.multy.resources.NetworkSecurityGroupResource - 73, // 103: dev.multy.MultyResourceService.DeleteNetworkSecurityGroup:output_type -> dev.multy.common.Empty - 82, // 104: dev.multy.MultyResourceService.CreateDatabase:output_type -> dev.multy.resources.DatabaseResource - 82, // 105: dev.multy.MultyResourceService.ReadDatabase:output_type -> dev.multy.resources.DatabaseResource - 82, // 106: dev.multy.MultyResourceService.UpdateDatabase:output_type -> dev.multy.resources.DatabaseResource - 73, // 107: dev.multy.MultyResourceService.DeleteDatabase:output_type -> dev.multy.common.Empty - 83, // 108: dev.multy.MultyResourceService.CreateObjectStorage:output_type -> dev.multy.resources.ObjectStorageResource - 83, // 109: dev.multy.MultyResourceService.ReadObjectStorage:output_type -> dev.multy.resources.ObjectStorageResource - 83, // 110: dev.multy.MultyResourceService.UpdateObjectStorage:output_type -> dev.multy.resources.ObjectStorageResource - 73, // 111: dev.multy.MultyResourceService.DeleteObjectStorage:output_type -> dev.multy.common.Empty - 84, // 112: dev.multy.MultyResourceService.CreateObjectStorageObject:output_type -> dev.multy.resources.ObjectStorageObjectResource - 84, // 113: dev.multy.MultyResourceService.ReadObjectStorageObject:output_type -> dev.multy.resources.ObjectStorageObjectResource - 84, // 114: dev.multy.MultyResourceService.UpdateObjectStorageObject:output_type -> dev.multy.resources.ObjectStorageObjectResource - 73, // 115: dev.multy.MultyResourceService.DeleteObjectStorageObject:output_type -> dev.multy.common.Empty - 85, // 116: dev.multy.MultyResourceService.CreatePublicIp:output_type -> dev.multy.resources.PublicIpResource - 85, // 117: dev.multy.MultyResourceService.ReadPublicIp:output_type -> dev.multy.resources.PublicIpResource - 85, // 118: dev.multy.MultyResourceService.UpdatePublicIp:output_type -> dev.multy.resources.PublicIpResource - 73, // 119: dev.multy.MultyResourceService.DeletePublicIp:output_type -> dev.multy.common.Empty - 86, // 120: dev.multy.MultyResourceService.CreateKubernetesCluster:output_type -> dev.multy.resources.KubernetesClusterResource - 86, // 121: dev.multy.MultyResourceService.ReadKubernetesCluster:output_type -> dev.multy.resources.KubernetesClusterResource - 86, // 122: dev.multy.MultyResourceService.UpdateKubernetesCluster:output_type -> dev.multy.resources.KubernetesClusterResource - 73, // 123: dev.multy.MultyResourceService.DeleteKubernetesCluster:output_type -> dev.multy.common.Empty - 87, // 124: dev.multy.MultyResourceService.CreateKubernetesNodePool:output_type -> dev.multy.resources.KubernetesNodePoolResource - 87, // 125: dev.multy.MultyResourceService.ReadKubernetesNodePool:output_type -> dev.multy.resources.KubernetesNodePoolResource - 87, // 126: dev.multy.MultyResourceService.UpdateKubernetesNodePool:output_type -> dev.multy.resources.KubernetesNodePoolResource - 73, // 127: dev.multy.MultyResourceService.DeleteKubernetesNodePool:output_type -> dev.multy.common.Empty - 88, // 128: dev.multy.MultyResourceService.CreateLambda:output_type -> dev.multy.resources.LambdaResource - 88, // 129: dev.multy.MultyResourceService.ReadLambda:output_type -> dev.multy.resources.LambdaResource - 88, // 130: dev.multy.MultyResourceService.UpdateLambda:output_type -> dev.multy.resources.LambdaResource - 73, // 131: dev.multy.MultyResourceService.DeleteLambda:output_type -> dev.multy.common.Empty - 89, // 132: dev.multy.MultyResourceService.CreateVault:output_type -> dev.multy.resources.VaultResource - 89, // 133: dev.multy.MultyResourceService.ReadVault:output_type -> dev.multy.resources.VaultResource - 89, // 134: dev.multy.MultyResourceService.UpdateVault:output_type -> dev.multy.resources.VaultResource - 73, // 135: dev.multy.MultyResourceService.DeleteVault:output_type -> dev.multy.common.Empty - 90, // 136: dev.multy.MultyResourceService.CreateVaultAccessPolicy:output_type -> dev.multy.resources.VaultAccessPolicyResource - 90, // 137: dev.multy.MultyResourceService.ReadVaultAccessPolicy:output_type -> dev.multy.resources.VaultAccessPolicyResource - 90, // 138: dev.multy.MultyResourceService.UpdateVaultAccessPolicy:output_type -> dev.multy.resources.VaultAccessPolicyResource - 73, // 139: dev.multy.MultyResourceService.DeleteVaultAccessPolicy:output_type -> dev.multy.common.Empty - 91, // 140: dev.multy.MultyResourceService.CreateVaultSecret:output_type -> dev.multy.resources.VaultSecretResource - 91, // 141: dev.multy.MultyResourceService.ReadVaultSecret:output_type -> dev.multy.resources.VaultSecretResource - 91, // 142: dev.multy.MultyResourceService.UpdateVaultSecret:output_type -> dev.multy.resources.VaultSecretResource - 73, // 143: dev.multy.MultyResourceService.DeleteVaultSecret:output_type -> dev.multy.common.Empty - 92, // 144: dev.multy.MultyResourceService.CreateVirtualMachine:output_type -> dev.multy.resources.VirtualMachineResource - 92, // 145: dev.multy.MultyResourceService.ReadVirtualMachine:output_type -> dev.multy.resources.VirtualMachineResource - 92, // 146: dev.multy.MultyResourceService.UpdateVirtualMachine:output_type -> dev.multy.resources.VirtualMachineResource - 73, // 147: dev.multy.MultyResourceService.DeleteVirtualMachine:output_type -> dev.multy.common.Empty - 73, // 148: dev.multy.MultyResourceService.RefreshState:output_type -> dev.multy.common.Empty - 93, // 149: dev.multy.MultyResourceService.ListResources:output_type -> dev.multy.common.ListResourcesResponse - 73, // 150: dev.multy.MultyResourceService.DeleteResource:output_type -> dev.multy.common.Empty - 94, // 151: dev.multy.MultyUserService.RegisterUser:output_type -> dev.multy.user.User - 76, // [76:152] is the sub-list for method output_type - 0, // [0:76] is the sub-list for method input_type + 53, // 52: dev.multy.MultyResourceService.CreateVault:input_type -> dev.multy.resources.CreateVaultRequest + 54, // 53: dev.multy.MultyResourceService.ReadVault:input_type -> dev.multy.resources.ReadVaultRequest + 55, // 54: dev.multy.MultyResourceService.UpdateVault:input_type -> dev.multy.resources.UpdateVaultRequest + 56, // 55: dev.multy.MultyResourceService.DeleteVault:input_type -> dev.multy.resources.DeleteVaultRequest + 57, // 56: dev.multy.MultyResourceService.CreateVaultAccessPolicy:input_type -> dev.multy.resources.CreateVaultAccessPolicyRequest + 58, // 57: dev.multy.MultyResourceService.ReadVaultAccessPolicy:input_type -> dev.multy.resources.ReadVaultAccessPolicyRequest + 59, // 58: dev.multy.MultyResourceService.UpdateVaultAccessPolicy:input_type -> dev.multy.resources.UpdateVaultAccessPolicyRequest + 60, // 59: dev.multy.MultyResourceService.DeleteVaultAccessPolicy:input_type -> dev.multy.resources.DeleteVaultAccessPolicyRequest + 61, // 60: dev.multy.MultyResourceService.CreateVaultSecret:input_type -> dev.multy.resources.CreateVaultSecretRequest + 62, // 61: dev.multy.MultyResourceService.ReadVaultSecret:input_type -> dev.multy.resources.ReadVaultSecretRequest + 63, // 62: dev.multy.MultyResourceService.UpdateVaultSecret:input_type -> dev.multy.resources.UpdateVaultSecretRequest + 64, // 63: dev.multy.MultyResourceService.DeleteVaultSecret:input_type -> dev.multy.resources.DeleteVaultSecretRequest + 65, // 64: dev.multy.MultyResourceService.CreateVirtualMachine:input_type -> dev.multy.resources.CreateVirtualMachineRequest + 66, // 65: dev.multy.MultyResourceService.ReadVirtualMachine:input_type -> dev.multy.resources.ReadVirtualMachineRequest + 67, // 66: dev.multy.MultyResourceService.UpdateVirtualMachine:input_type -> dev.multy.resources.UpdateVirtualMachineRequest + 68, // 67: dev.multy.MultyResourceService.DeleteVirtualMachine:input_type -> dev.multy.resources.DeleteVirtualMachineRequest + 69, // 68: dev.multy.MultyResourceService.RefreshState:input_type -> dev.multy.common.Empty + 69, // 69: dev.multy.MultyResourceService.ListResources:input_type -> dev.multy.common.Empty + 0, // 70: dev.multy.MultyResourceService.DeleteResource:input_type -> dev.multy.DeleteResourceRequest + 70, // 71: dev.multy.MultyUserService.RegisterUser:input_type -> dev.multy.user.RegisterUserRequest + 71, // 72: dev.multy.MultyResourceService.CreateSubnet:output_type -> dev.multy.resources.SubnetResource + 71, // 73: dev.multy.MultyResourceService.ReadSubnet:output_type -> dev.multy.resources.SubnetResource + 71, // 74: dev.multy.MultyResourceService.UpdateSubnet:output_type -> dev.multy.resources.SubnetResource + 69, // 75: dev.multy.MultyResourceService.DeleteSubnet:output_type -> dev.multy.common.Empty + 72, // 76: dev.multy.MultyResourceService.CreateVirtualNetwork:output_type -> dev.multy.resources.VirtualNetworkResource + 72, // 77: dev.multy.MultyResourceService.ReadVirtualNetwork:output_type -> dev.multy.resources.VirtualNetworkResource + 72, // 78: dev.multy.MultyResourceService.UpdateVirtualNetwork:output_type -> dev.multy.resources.VirtualNetworkResource + 69, // 79: dev.multy.MultyResourceService.DeleteVirtualNetwork:output_type -> dev.multy.common.Empty + 73, // 80: dev.multy.MultyResourceService.CreateNetworkInterface:output_type -> dev.multy.resources.NetworkInterfaceResource + 73, // 81: dev.multy.MultyResourceService.ReadNetworkInterface:output_type -> dev.multy.resources.NetworkInterfaceResource + 73, // 82: dev.multy.MultyResourceService.UpdateNetworkInterface:output_type -> dev.multy.resources.NetworkInterfaceResource + 69, // 83: dev.multy.MultyResourceService.DeleteNetworkInterface:output_type -> dev.multy.common.Empty + 74, // 84: dev.multy.MultyResourceService.CreateNetworkInterfaceSecurityGroupAssociation:output_type -> dev.multy.resources.NetworkInterfaceSecurityGroupAssociationResource + 74, // 85: dev.multy.MultyResourceService.ReadNetworkInterfaceSecurityGroupAssociation:output_type -> dev.multy.resources.NetworkInterfaceSecurityGroupAssociationResource + 74, // 86: dev.multy.MultyResourceService.UpdateNetworkInterfaceSecurityGroupAssociation:output_type -> dev.multy.resources.NetworkInterfaceSecurityGroupAssociationResource + 69, // 87: dev.multy.MultyResourceService.DeleteNetworkInterfaceSecurityGroupAssociation:output_type -> dev.multy.common.Empty + 75, // 88: dev.multy.MultyResourceService.CreateRouteTable:output_type -> dev.multy.resources.RouteTableResource + 75, // 89: dev.multy.MultyResourceService.ReadRouteTable:output_type -> dev.multy.resources.RouteTableResource + 75, // 90: dev.multy.MultyResourceService.UpdateRouteTable:output_type -> dev.multy.resources.RouteTableResource + 69, // 91: dev.multy.MultyResourceService.DeleteRouteTable:output_type -> dev.multy.common.Empty + 76, // 92: dev.multy.MultyResourceService.CreateRouteTableAssociation:output_type -> dev.multy.resources.RouteTableAssociationResource + 76, // 93: dev.multy.MultyResourceService.ReadRouteTableAssociation:output_type -> dev.multy.resources.RouteTableAssociationResource + 76, // 94: dev.multy.MultyResourceService.UpdateRouteTableAssociation:output_type -> dev.multy.resources.RouteTableAssociationResource + 69, // 95: dev.multy.MultyResourceService.DeleteRouteTableAssociation:output_type -> dev.multy.common.Empty + 77, // 96: dev.multy.MultyResourceService.CreateNetworkSecurityGroup:output_type -> dev.multy.resources.NetworkSecurityGroupResource + 77, // 97: dev.multy.MultyResourceService.ReadNetworkSecurityGroup:output_type -> dev.multy.resources.NetworkSecurityGroupResource + 77, // 98: dev.multy.MultyResourceService.UpdateNetworkSecurityGroup:output_type -> dev.multy.resources.NetworkSecurityGroupResource + 69, // 99: dev.multy.MultyResourceService.DeleteNetworkSecurityGroup:output_type -> dev.multy.common.Empty + 78, // 100: dev.multy.MultyResourceService.CreateDatabase:output_type -> dev.multy.resources.DatabaseResource + 78, // 101: dev.multy.MultyResourceService.ReadDatabase:output_type -> dev.multy.resources.DatabaseResource + 78, // 102: dev.multy.MultyResourceService.UpdateDatabase:output_type -> dev.multy.resources.DatabaseResource + 69, // 103: dev.multy.MultyResourceService.DeleteDatabase:output_type -> dev.multy.common.Empty + 79, // 104: dev.multy.MultyResourceService.CreateObjectStorage:output_type -> dev.multy.resources.ObjectStorageResource + 79, // 105: dev.multy.MultyResourceService.ReadObjectStorage:output_type -> dev.multy.resources.ObjectStorageResource + 79, // 106: dev.multy.MultyResourceService.UpdateObjectStorage:output_type -> dev.multy.resources.ObjectStorageResource + 69, // 107: dev.multy.MultyResourceService.DeleteObjectStorage:output_type -> dev.multy.common.Empty + 80, // 108: dev.multy.MultyResourceService.CreateObjectStorageObject:output_type -> dev.multy.resources.ObjectStorageObjectResource + 80, // 109: dev.multy.MultyResourceService.ReadObjectStorageObject:output_type -> dev.multy.resources.ObjectStorageObjectResource + 80, // 110: dev.multy.MultyResourceService.UpdateObjectStorageObject:output_type -> dev.multy.resources.ObjectStorageObjectResource + 69, // 111: dev.multy.MultyResourceService.DeleteObjectStorageObject:output_type -> dev.multy.common.Empty + 81, // 112: dev.multy.MultyResourceService.CreatePublicIp:output_type -> dev.multy.resources.PublicIpResource + 81, // 113: dev.multy.MultyResourceService.ReadPublicIp:output_type -> dev.multy.resources.PublicIpResource + 81, // 114: dev.multy.MultyResourceService.UpdatePublicIp:output_type -> dev.multy.resources.PublicIpResource + 69, // 115: dev.multy.MultyResourceService.DeletePublicIp:output_type -> dev.multy.common.Empty + 82, // 116: dev.multy.MultyResourceService.CreateKubernetesCluster:output_type -> dev.multy.resources.KubernetesClusterResource + 82, // 117: dev.multy.MultyResourceService.ReadKubernetesCluster:output_type -> dev.multy.resources.KubernetesClusterResource + 82, // 118: dev.multy.MultyResourceService.UpdateKubernetesCluster:output_type -> dev.multy.resources.KubernetesClusterResource + 69, // 119: dev.multy.MultyResourceService.DeleteKubernetesCluster:output_type -> dev.multy.common.Empty + 83, // 120: dev.multy.MultyResourceService.CreateKubernetesNodePool:output_type -> dev.multy.resources.KubernetesNodePoolResource + 83, // 121: dev.multy.MultyResourceService.ReadKubernetesNodePool:output_type -> dev.multy.resources.KubernetesNodePoolResource + 83, // 122: dev.multy.MultyResourceService.UpdateKubernetesNodePool:output_type -> dev.multy.resources.KubernetesNodePoolResource + 69, // 123: dev.multy.MultyResourceService.DeleteKubernetesNodePool:output_type -> dev.multy.common.Empty + 84, // 124: dev.multy.MultyResourceService.CreateVault:output_type -> dev.multy.resources.VaultResource + 84, // 125: dev.multy.MultyResourceService.ReadVault:output_type -> dev.multy.resources.VaultResource + 84, // 126: dev.multy.MultyResourceService.UpdateVault:output_type -> dev.multy.resources.VaultResource + 69, // 127: dev.multy.MultyResourceService.DeleteVault:output_type -> dev.multy.common.Empty + 85, // 128: dev.multy.MultyResourceService.CreateVaultAccessPolicy:output_type -> dev.multy.resources.VaultAccessPolicyResource + 85, // 129: dev.multy.MultyResourceService.ReadVaultAccessPolicy:output_type -> dev.multy.resources.VaultAccessPolicyResource + 85, // 130: dev.multy.MultyResourceService.UpdateVaultAccessPolicy:output_type -> dev.multy.resources.VaultAccessPolicyResource + 69, // 131: dev.multy.MultyResourceService.DeleteVaultAccessPolicy:output_type -> dev.multy.common.Empty + 86, // 132: dev.multy.MultyResourceService.CreateVaultSecret:output_type -> dev.multy.resources.VaultSecretResource + 86, // 133: dev.multy.MultyResourceService.ReadVaultSecret:output_type -> dev.multy.resources.VaultSecretResource + 86, // 134: dev.multy.MultyResourceService.UpdateVaultSecret:output_type -> dev.multy.resources.VaultSecretResource + 69, // 135: dev.multy.MultyResourceService.DeleteVaultSecret:output_type -> dev.multy.common.Empty + 87, // 136: dev.multy.MultyResourceService.CreateVirtualMachine:output_type -> dev.multy.resources.VirtualMachineResource + 87, // 137: dev.multy.MultyResourceService.ReadVirtualMachine:output_type -> dev.multy.resources.VirtualMachineResource + 87, // 138: dev.multy.MultyResourceService.UpdateVirtualMachine:output_type -> dev.multy.resources.VirtualMachineResource + 69, // 139: dev.multy.MultyResourceService.DeleteVirtualMachine:output_type -> dev.multy.common.Empty + 69, // 140: dev.multy.MultyResourceService.RefreshState:output_type -> dev.multy.common.Empty + 88, // 141: dev.multy.MultyResourceService.ListResources:output_type -> dev.multy.common.ListResourcesResponse + 69, // 142: dev.multy.MultyResourceService.DeleteResource:output_type -> dev.multy.common.Empty + 89, // 143: dev.multy.MultyUserService.RegisterUser:output_type -> dev.multy.user.User + 72, // [72:144] is the sub-list for method output_type + 0, // [0:72] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name diff --git a/api/proto/multy_service.proto b/api/proto/multy_service.proto index 742a4114..72224595 100644 --- a/api/proto/multy_service.proto +++ b/api/proto/multy_service.proto @@ -11,7 +11,6 @@ import "api/proto/resourcespb/vault.proto"; import "api/proto/resourcespb/vault_access_policy.proto"; import "api/proto/resourcespb/vault_secret.proto"; import "api/proto/resourcespb/public_ip.proto"; -import "api/proto/resourcespb/lambda.proto"; import "api/proto/resourcespb/kubernetes_cluster.proto"; import "api/proto/resourcespb/kubernetes_node_pool.proto"; import "api/proto/resourcespb/object_storage.proto"; @@ -94,11 +93,6 @@ service MultyResourceService { rpc UpdateKubernetesNodePool (resources.UpdateKubernetesNodePoolRequest) returns (resources.KubernetesNodePoolResource) {} rpc DeleteKubernetesNodePool (resources.DeleteKubernetesNodePoolRequest) returns (common.Empty) {} - rpc CreateLambda (resources.CreateLambdaRequest) returns (resources.LambdaResource) {} - rpc ReadLambda (resources.ReadLambdaRequest) returns (resources.LambdaResource) {} - rpc UpdateLambda (resources.UpdateLambdaRequest) returns (resources.LambdaResource) {} - rpc DeleteLambda (resources.DeleteLambdaRequest) returns (common.Empty) {} - rpc CreateVault (resources.CreateVaultRequest) returns (resources.VaultResource) {} rpc ReadVault (resources.ReadVaultRequest) returns (resources.VaultResource) {} rpc UpdateVault (resources.UpdateVaultRequest) returns (resources.VaultResource) {} diff --git a/api/proto/multy_service_grpc.pb.go b/api/proto/multy_service_grpc.pb.go index 8e19d89b..05201a88 100644 --- a/api/proto/multy_service_grpc.pb.go +++ b/api/proto/multy_service_grpc.pb.go @@ -77,10 +77,6 @@ type MultyResourceServiceClient interface { ReadKubernetesNodePool(ctx context.Context, in *resourcespb.ReadKubernetesNodePoolRequest, opts ...grpc.CallOption) (*resourcespb.KubernetesNodePoolResource, error) UpdateKubernetesNodePool(ctx context.Context, in *resourcespb.UpdateKubernetesNodePoolRequest, opts ...grpc.CallOption) (*resourcespb.KubernetesNodePoolResource, error) DeleteKubernetesNodePool(ctx context.Context, in *resourcespb.DeleteKubernetesNodePoolRequest, opts ...grpc.CallOption) (*commonpb.Empty, error) - CreateLambda(ctx context.Context, in *resourcespb.CreateLambdaRequest, opts ...grpc.CallOption) (*resourcespb.LambdaResource, error) - ReadLambda(ctx context.Context, in *resourcespb.ReadLambdaRequest, opts ...grpc.CallOption) (*resourcespb.LambdaResource, error) - UpdateLambda(ctx context.Context, in *resourcespb.UpdateLambdaRequest, opts ...grpc.CallOption) (*resourcespb.LambdaResource, error) - DeleteLambda(ctx context.Context, in *resourcespb.DeleteLambdaRequest, opts ...grpc.CallOption) (*commonpb.Empty, error) CreateVault(ctx context.Context, in *resourcespb.CreateVaultRequest, opts ...grpc.CallOption) (*resourcespb.VaultResource, error) ReadVault(ctx context.Context, in *resourcespb.ReadVaultRequest, opts ...grpc.CallOption) (*resourcespb.VaultResource, error) UpdateVault(ctx context.Context, in *resourcespb.UpdateVaultRequest, opts ...grpc.CallOption) (*resourcespb.VaultResource, error) @@ -578,42 +574,6 @@ func (c *multyResourceServiceClient) DeleteKubernetesNodePool(ctx context.Contex return out, nil } -func (c *multyResourceServiceClient) CreateLambda(ctx context.Context, in *resourcespb.CreateLambdaRequest, opts ...grpc.CallOption) (*resourcespb.LambdaResource, error) { - out := new(resourcespb.LambdaResource) - err := c.cc.Invoke(ctx, "/dev.multy.MultyResourceService/CreateLambda", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *multyResourceServiceClient) ReadLambda(ctx context.Context, in *resourcespb.ReadLambdaRequest, opts ...grpc.CallOption) (*resourcespb.LambdaResource, error) { - out := new(resourcespb.LambdaResource) - err := c.cc.Invoke(ctx, "/dev.multy.MultyResourceService/ReadLambda", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *multyResourceServiceClient) UpdateLambda(ctx context.Context, in *resourcespb.UpdateLambdaRequest, opts ...grpc.CallOption) (*resourcespb.LambdaResource, error) { - out := new(resourcespb.LambdaResource) - err := c.cc.Invoke(ctx, "/dev.multy.MultyResourceService/UpdateLambda", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *multyResourceServiceClient) DeleteLambda(ctx context.Context, in *resourcespb.DeleteLambdaRequest, opts ...grpc.CallOption) (*commonpb.Empty, error) { - out := new(commonpb.Empty) - err := c.cc.Invoke(ctx, "/dev.multy.MultyResourceService/DeleteLambda", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *multyResourceServiceClient) CreateVault(ctx context.Context, in *resourcespb.CreateVaultRequest, opts ...grpc.CallOption) (*resourcespb.VaultResource, error) { out := new(resourcespb.VaultResource) err := c.cc.Invoke(ctx, "/dev.multy.MultyResourceService/CreateVault", in, out, opts...) @@ -841,10 +801,6 @@ type MultyResourceServiceServer interface { ReadKubernetesNodePool(context.Context, *resourcespb.ReadKubernetesNodePoolRequest) (*resourcespb.KubernetesNodePoolResource, error) UpdateKubernetesNodePool(context.Context, *resourcespb.UpdateKubernetesNodePoolRequest) (*resourcespb.KubernetesNodePoolResource, error) DeleteKubernetesNodePool(context.Context, *resourcespb.DeleteKubernetesNodePoolRequest) (*commonpb.Empty, error) - CreateLambda(context.Context, *resourcespb.CreateLambdaRequest) (*resourcespb.LambdaResource, error) - ReadLambda(context.Context, *resourcespb.ReadLambdaRequest) (*resourcespb.LambdaResource, error) - UpdateLambda(context.Context, *resourcespb.UpdateLambdaRequest) (*resourcespb.LambdaResource, error) - DeleteLambda(context.Context, *resourcespb.DeleteLambdaRequest) (*commonpb.Empty, error) CreateVault(context.Context, *resourcespb.CreateVaultRequest) (*resourcespb.VaultResource, error) ReadVault(context.Context, *resourcespb.ReadVaultRequest) (*resourcespb.VaultResource, error) UpdateVault(context.Context, *resourcespb.UpdateVaultRequest) (*resourcespb.VaultResource, error) @@ -1027,18 +983,6 @@ func (UnimplementedMultyResourceServiceServer) UpdateKubernetesNodePool(context. func (UnimplementedMultyResourceServiceServer) DeleteKubernetesNodePool(context.Context, *resourcespb.DeleteKubernetesNodePoolRequest) (*commonpb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteKubernetesNodePool not implemented") } -func (UnimplementedMultyResourceServiceServer) CreateLambda(context.Context, *resourcespb.CreateLambdaRequest) (*resourcespb.LambdaResource, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateLambda not implemented") -} -func (UnimplementedMultyResourceServiceServer) ReadLambda(context.Context, *resourcespb.ReadLambdaRequest) (*resourcespb.LambdaResource, error) { - return nil, status.Errorf(codes.Unimplemented, "method ReadLambda not implemented") -} -func (UnimplementedMultyResourceServiceServer) UpdateLambda(context.Context, *resourcespb.UpdateLambdaRequest) (*resourcespb.LambdaResource, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdateLambda not implemented") -} -func (UnimplementedMultyResourceServiceServer) DeleteLambda(context.Context, *resourcespb.DeleteLambdaRequest) (*commonpb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteLambda not implemented") -} func (UnimplementedMultyResourceServiceServer) CreateVault(context.Context, *resourcespb.CreateVaultRequest) (*resourcespb.VaultResource, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateVault not implemented") } @@ -2045,78 +1989,6 @@ func _MultyResourceService_DeleteKubernetesNodePool_Handler(srv interface{}, ctx return interceptor(ctx, in, info, handler) } -func _MultyResourceService_CreateLambda_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(resourcespb.CreateLambdaRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MultyResourceServiceServer).CreateLambda(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/dev.multy.MultyResourceService/CreateLambda", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MultyResourceServiceServer).CreateLambda(ctx, req.(*resourcespb.CreateLambdaRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _MultyResourceService_ReadLambda_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(resourcespb.ReadLambdaRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MultyResourceServiceServer).ReadLambda(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/dev.multy.MultyResourceService/ReadLambda", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MultyResourceServiceServer).ReadLambda(ctx, req.(*resourcespb.ReadLambdaRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _MultyResourceService_UpdateLambda_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(resourcespb.UpdateLambdaRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MultyResourceServiceServer).UpdateLambda(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/dev.multy.MultyResourceService/UpdateLambda", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MultyResourceServiceServer).UpdateLambda(ctx, req.(*resourcespb.UpdateLambdaRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _MultyResourceService_DeleteLambda_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(resourcespb.DeleteLambdaRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MultyResourceServiceServer).DeleteLambda(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/dev.multy.MultyResourceService/DeleteLambda", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MultyResourceServiceServer).DeleteLambda(ctx, req.(*resourcespb.DeleteLambdaRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _MultyResourceService_CreateVault_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(resourcespb.CreateVaultRequest) if err := dec(in); err != nil { @@ -2674,22 +2546,6 @@ var MultyResourceService_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteKubernetesNodePool", Handler: _MultyResourceService_DeleteKubernetesNodePool_Handler, }, - { - MethodName: "CreateLambda", - Handler: _MultyResourceService_CreateLambda_Handler, - }, - { - MethodName: "ReadLambda", - Handler: _MultyResourceService_ReadLambda_Handler, - }, - { - MethodName: "UpdateLambda", - Handler: _MultyResourceService_UpdateLambda_Handler, - }, - { - MethodName: "DeleteLambda", - Handler: _MultyResourceService_DeleteLambda_Handler, - }, { MethodName: "CreateVault", Handler: _MultyResourceService_CreateVault_Handler, diff --git a/api/proto/resourcespb/lambda.pb.go b/api/proto/resourcespb/lambda.pb.go deleted file mode 100644 index ea22da1e..00000000 --- a/api/proto/resourcespb/lambda.pb.go +++ /dev/null @@ -1,566 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc v3.19.4 -// source: api/proto/resourcespb/lambda.proto - -package resourcespb - -import ( - commonpb "github.com/multycloud/multy/api/proto/commonpb" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type CreateLambdaRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Resource *LambdaArgs `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` -} - -func (x *CreateLambdaRequest) Reset() { - *x = CreateLambdaRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_api_proto_resourcespb_lambda_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CreateLambdaRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateLambdaRequest) ProtoMessage() {} - -func (x *CreateLambdaRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_resourcespb_lambda_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateLambdaRequest.ProtoReflect.Descriptor instead. -func (*CreateLambdaRequest) Descriptor() ([]byte, []int) { - return file_api_proto_resourcespb_lambda_proto_rawDescGZIP(), []int{0} -} - -func (x *CreateLambdaRequest) GetResource() *LambdaArgs { - if x != nil { - return x.Resource - } - return nil -} - -type ReadLambdaRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` -} - -func (x *ReadLambdaRequest) Reset() { - *x = ReadLambdaRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_api_proto_resourcespb_lambda_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ReadLambdaRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReadLambdaRequest) ProtoMessage() {} - -func (x *ReadLambdaRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_resourcespb_lambda_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReadLambdaRequest.ProtoReflect.Descriptor instead. -func (*ReadLambdaRequest) Descriptor() ([]byte, []int) { - return file_api_proto_resourcespb_lambda_proto_rawDescGZIP(), []int{1} -} - -func (x *ReadLambdaRequest) GetResourceId() string { - if x != nil { - return x.ResourceId - } - return "" -} - -type UpdateLambdaRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` - Resource *LambdaArgs `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"` -} - -func (x *UpdateLambdaRequest) Reset() { - *x = UpdateLambdaRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_api_proto_resourcespb_lambda_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UpdateLambdaRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateLambdaRequest) ProtoMessage() {} - -func (x *UpdateLambdaRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_resourcespb_lambda_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateLambdaRequest.ProtoReflect.Descriptor instead. -func (*UpdateLambdaRequest) Descriptor() ([]byte, []int) { - return file_api_proto_resourcespb_lambda_proto_rawDescGZIP(), []int{2} -} - -func (x *UpdateLambdaRequest) GetResourceId() string { - if x != nil { - return x.ResourceId - } - return "" -} - -func (x *UpdateLambdaRequest) GetResource() *LambdaArgs { - if x != nil { - return x.Resource - } - return nil -} - -type DeleteLambdaRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` -} - -func (x *DeleteLambdaRequest) Reset() { - *x = DeleteLambdaRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_api_proto_resourcespb_lambda_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DeleteLambdaRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteLambdaRequest) ProtoMessage() {} - -func (x *DeleteLambdaRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_resourcespb_lambda_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteLambdaRequest.ProtoReflect.Descriptor instead. -func (*DeleteLambdaRequest) Descriptor() ([]byte, []int) { - return file_api_proto_resourcespb_lambda_proto_rawDescGZIP(), []int{3} -} - -func (x *DeleteLambdaRequest) GetResourceId() string { - if x != nil { - return x.ResourceId - } - return "" -} - -type LambdaArgs struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - CommonParameters *commonpb.ResourceCommonArgs `protobuf:"bytes,1,opt,name=common_parameters,json=commonParameters,proto3" json:"common_parameters,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Runtime string `protobuf:"bytes,3,opt,name=runtime,proto3" json:"runtime,omitempty"` - SourceCodeObjectId string `protobuf:"bytes,4,opt,name=source_code_object_id,json=sourceCodeObjectId,proto3" json:"source_code_object_id,omitempty"` - // outputs - Url string `protobuf:"bytes,5,opt,name=url,proto3" json:"url,omitempty"` -} - -func (x *LambdaArgs) Reset() { - *x = LambdaArgs{} - if protoimpl.UnsafeEnabled { - mi := &file_api_proto_resourcespb_lambda_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *LambdaArgs) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LambdaArgs) ProtoMessage() {} - -func (x *LambdaArgs) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_resourcespb_lambda_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LambdaArgs.ProtoReflect.Descriptor instead. -func (*LambdaArgs) Descriptor() ([]byte, []int) { - return file_api_proto_resourcespb_lambda_proto_rawDescGZIP(), []int{4} -} - -func (x *LambdaArgs) GetCommonParameters() *commonpb.ResourceCommonArgs { - if x != nil { - return x.CommonParameters - } - return nil -} - -func (x *LambdaArgs) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *LambdaArgs) GetRuntime() string { - if x != nil { - return x.Runtime - } - return "" -} - -func (x *LambdaArgs) GetSourceCodeObjectId() string { - if x != nil { - return x.SourceCodeObjectId - } - return "" -} - -func (x *LambdaArgs) GetUrl() string { - if x != nil { - return x.Url - } - return "" -} - -type LambdaResource struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - CommonParameters *commonpb.CommonResourceParameters `protobuf:"bytes,1,opt,name=common_parameters,json=commonParameters,proto3" json:"common_parameters,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Runtime string `protobuf:"bytes,3,opt,name=runtime,proto3" json:"runtime,omitempty"` - SourceCodeObjectId string `protobuf:"bytes,4,opt,name=source_code_object_id,json=sourceCodeObjectId,proto3" json:"source_code_object_id,omitempty"` -} - -func (x *LambdaResource) Reset() { - *x = LambdaResource{} - if protoimpl.UnsafeEnabled { - mi := &file_api_proto_resourcespb_lambda_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *LambdaResource) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LambdaResource) ProtoMessage() {} - -func (x *LambdaResource) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_resourcespb_lambda_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LambdaResource.ProtoReflect.Descriptor instead. -func (*LambdaResource) Descriptor() ([]byte, []int) { - return file_api_proto_resourcespb_lambda_proto_rawDescGZIP(), []int{5} -} - -func (x *LambdaResource) GetCommonParameters() *commonpb.CommonResourceParameters { - if x != nil { - return x.CommonParameters - } - return nil -} - -func (x *LambdaResource) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *LambdaResource) GetRuntime() string { - if x != nil { - return x.Runtime - } - return "" -} - -func (x *LambdaResource) GetSourceCodeObjectId() string { - if x != nil { - return x.SourceCodeObjectId - } - return "" -} - -var File_api_proto_resourcespb_lambda_proto protoreflect.FileDescriptor - -var file_api_proto_resourcespb_lambda_proto_rawDesc = []byte{ - 0x0a, 0x22, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x73, 0x70, 0x62, 0x2f, 0x6c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, 0x1f, 0x61, 0x70, 0x69, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x52, 0x0a, 0x13, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x3b, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x61, 0x6d, 0x62, 0x64, 0x61, - 0x41, 0x72, 0x67, 0x73, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x34, - 0x0a, 0x11, 0x52, 0x65, 0x61, 0x64, 0x4c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x49, 0x64, 0x22, 0x73, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x61, - 0x6d, 0x62, 0x64, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x08, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, - 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x41, 0x72, 0x67, 0x73, 0x52, - 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x36, 0x0a, 0x13, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x4c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, - 0x64, 0x22, 0xd2, 0x01, 0x0a, 0x0a, 0x4c, 0x61, 0x6d, 0x62, 0x64, 0x61, 0x41, 0x72, 0x67, 0x73, - 0x12, 0x51, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x64, 0x65, - 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x41, 0x72, 0x67, - 0x73, 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x12, 0x31, 0x0a, 0x15, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x64, 0x65, - 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x12, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0xca, 0x01, 0x0a, 0x0e, 0x4c, 0x61, 0x6d, 0x62, 0x64, - 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x57, 0x0a, 0x11, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x12, 0x31, 0x0a, 0x15, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x5f, - 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x12, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x49, 0x64, 0x42, 0x5e, 0x0a, 0x17, 0x64, 0x65, 0x76, 0x2e, 0x6d, 0x75, 0x6c, 0x74, 0x79, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x0e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x75, 0x6c, - 0x74, 0x79, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6d, 0x75, 0x6c, 0x74, 0x79, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_api_proto_resourcespb_lambda_proto_rawDescOnce sync.Once - file_api_proto_resourcespb_lambda_proto_rawDescData = file_api_proto_resourcespb_lambda_proto_rawDesc -) - -func file_api_proto_resourcespb_lambda_proto_rawDescGZIP() []byte { - file_api_proto_resourcespb_lambda_proto_rawDescOnce.Do(func() { - file_api_proto_resourcespb_lambda_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_proto_resourcespb_lambda_proto_rawDescData) - }) - return file_api_proto_resourcespb_lambda_proto_rawDescData -} - -var file_api_proto_resourcespb_lambda_proto_msgTypes = make([]protoimpl.MessageInfo, 6) -var file_api_proto_resourcespb_lambda_proto_goTypes = []interface{}{ - (*CreateLambdaRequest)(nil), // 0: dev.multy.resources.CreateLambdaRequest - (*ReadLambdaRequest)(nil), // 1: dev.multy.resources.ReadLambdaRequest - (*UpdateLambdaRequest)(nil), // 2: dev.multy.resources.UpdateLambdaRequest - (*DeleteLambdaRequest)(nil), // 3: dev.multy.resources.DeleteLambdaRequest - (*LambdaArgs)(nil), // 4: dev.multy.resources.LambdaArgs - (*LambdaResource)(nil), // 5: dev.multy.resources.LambdaResource - (*commonpb.ResourceCommonArgs)(nil), // 6: dev.multy.common.ResourceCommonArgs - (*commonpb.CommonResourceParameters)(nil), // 7: dev.multy.common.CommonResourceParameters -} -var file_api_proto_resourcespb_lambda_proto_depIdxs = []int32{ - 4, // 0: dev.multy.resources.CreateLambdaRequest.resource:type_name -> dev.multy.resources.LambdaArgs - 4, // 1: dev.multy.resources.UpdateLambdaRequest.resource:type_name -> dev.multy.resources.LambdaArgs - 6, // 2: dev.multy.resources.LambdaArgs.common_parameters:type_name -> dev.multy.common.ResourceCommonArgs - 7, // 3: dev.multy.resources.LambdaResource.common_parameters:type_name -> dev.multy.common.CommonResourceParameters - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name -} - -func init() { file_api_proto_resourcespb_lambda_proto_init() } -func file_api_proto_resourcespb_lambda_proto_init() { - if File_api_proto_resourcespb_lambda_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_api_proto_resourcespb_lambda_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateLambdaRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_api_proto_resourcespb_lambda_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadLambdaRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_api_proto_resourcespb_lambda_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateLambdaRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_api_proto_resourcespb_lambda_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteLambdaRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_api_proto_resourcespb_lambda_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LambdaArgs); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_api_proto_resourcespb_lambda_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LambdaResource); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_api_proto_resourcespb_lambda_proto_rawDesc, - NumEnums: 0, - NumMessages: 6, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_api_proto_resourcespb_lambda_proto_goTypes, - DependencyIndexes: file_api_proto_resourcespb_lambda_proto_depIdxs, - MessageInfos: file_api_proto_resourcespb_lambda_proto_msgTypes, - }.Build() - File_api_proto_resourcespb_lambda_proto = out.File - file_api_proto_resourcespb_lambda_proto_rawDesc = nil - file_api_proto_resourcespb_lambda_proto_goTypes = nil - file_api_proto_resourcespb_lambda_proto_depIdxs = nil -} diff --git a/api/proto/resourcespb/lambda.proto b/api/proto/resourcespb/lambda.proto deleted file mode 100644 index a6633976..00000000 --- a/api/proto/resourcespb/lambda.proto +++ /dev/null @@ -1,44 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/multycloud/multy/api/proto/resourcespb"; -option java_multiple_files = true; -option java_package = "dev.multy.api.resources"; -option java_outer_classname = "ResourcesProto"; - -import "api/proto/commonpb/common.proto"; - -package dev.multy.resources; - -message CreateLambdaRequest { - LambdaArgs resource = 1; -} - -message ReadLambdaRequest { - string resource_id = 1; -} - -message UpdateLambdaRequest { - string resource_id = 1; - LambdaArgs resource = 2; -} - -message DeleteLambdaRequest { - string resource_id = 1; -} - -message LambdaArgs { - common.ResourceCommonArgs common_parameters = 1; - string name = 2; - string runtime = 3; - string source_code_object_id = 4; - - // outputs - string url = 5; -} - -message LambdaResource { - common.CommonResourceParameters common_parameters = 1; - string name = 2; - string runtime = 3; - string source_code_object_id = 4; -} diff --git a/api/server.go b/api/server.go index 13df520c..b55fa146 100644 --- a/api/server.go +++ b/api/server.go @@ -15,7 +15,7 @@ import ( "github.com/multycloud/multy/db" "github.com/multycloud/multy/flags" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/types" + "github.com/multycloud/multy/resources/types/metadata" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "log" @@ -42,7 +42,6 @@ type Server struct { PublicIpService services.Service[*resourcespb.PublicIpArgs, *resourcespb.PublicIpResource] KubernetesClusterService services.Service[*resourcespb.KubernetesClusterArgs, *resourcespb.KubernetesClusterResource] KubernetesNodePoolService services.Service[*resourcespb.KubernetesNodePoolArgs, *resourcespb.KubernetesNodePoolResource] - LambdaService services.Service[*resourcespb.LambdaArgs, *resourcespb.LambdaResource] VaultService services.Service[*resourcespb.VaultArgs, *resourcespb.VaultResource] VaultAccessPolicyService services.Service[*resourcespb.VaultAccessPolicyArgs, *resourcespb.VaultAccessPolicyResource] VaultSecretService services.Service[*resourcespb.VaultSecretArgs, *resourcespb.VaultSecretResource] @@ -125,7 +124,6 @@ func CreateServer(serviceContext *service_context.ResourceServiceContext) Server services.NewService[*resourcespb.PublicIpArgs, *resourcespb.PublicIpResource]("public_ip", serviceContext), services.NewService[*resourcespb.KubernetesClusterArgs, *resourcespb.KubernetesClusterResource]("kubernetes_cluster", serviceContext), services.NewService[*resourcespb.KubernetesNodePoolArgs, *resourcespb.KubernetesNodePoolResource]("kubernetes_node_pool", serviceContext), - services.NewService[*resourcespb.LambdaArgs, *resourcespb.LambdaResource]("lambda", serviceContext), services.NewService[*resourcespb.VaultArgs, *resourcespb.VaultResource]("vault", serviceContext), services.NewService[*resourcespb.VaultAccessPolicyArgs, *resourcespb.VaultAccessPolicyResource]("vault_access_policy", serviceContext), services.NewService[*resourcespb.VaultSecretArgs, *resourcespb.VaultSecretResource]("vault_secret", serviceContext), @@ -302,19 +300,6 @@ func (s *Server) DeleteKubernetesNodePool(ctx context.Context, in *resourcespb.D return s.KubernetesNodePoolService.Delete(ctx, in) } -func (s *Server) CreateLambda(ctx context.Context, in *resourcespb.CreateLambdaRequest) (*resourcespb.LambdaResource, error) { - return s.LambdaService.Create(ctx, in) -} -func (s *Server) ReadLambda(ctx context.Context, in *resourcespb.ReadLambdaRequest) (*resourcespb.LambdaResource, error) { - return s.LambdaService.Read(ctx, in) -} -func (s *Server) UpdateLambda(ctx context.Context, in *resourcespb.UpdateLambdaRequest) (*resourcespb.LambdaResource, error) { - return s.LambdaService.Update(ctx, in) -} -func (s *Server) DeleteLambda(ctx context.Context, in *resourcespb.DeleteLambdaRequest) (*commonpb.Empty, error) { - return s.LambdaService.Delete(ctx, in) -} - func (s *Server) CreateVault(ctx context.Context, in *resourcespb.CreateVaultRequest) (*resourcespb.VaultResource, error) { return s.VaultService.Create(ctx, in) } @@ -398,7 +383,7 @@ func (s *Server) refresh(ctx context.Context, _ *commonpb.Empty) (*commonpb.Empt if err != nil { return nil, err } - mconfig, err := resources.LoadConfig(c, types.Metadatas) + mconfig, err := resources.LoadConfig(c, metadata.Metadatas) if err != nil { return nil, err } diff --git a/api/services/service.go b/api/services/service.go index 486d94c4..4a559f8d 100644 --- a/api/services/service.go +++ b/api/services/service.go @@ -2,6 +2,7 @@ package services import ( "context" + "fmt" "github.com/multycloud/multy/api/errors" "github.com/multycloud/multy/api/proto/commonpb" pberr "github.com/multycloud/multy/api/proto/errorspb" @@ -10,7 +11,7 @@ import ( "github.com/multycloud/multy/api/util" "github.com/multycloud/multy/db" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/types" + "github.com/multycloud/multy/resources/types/metadata" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" @@ -99,7 +100,7 @@ func (s Service[Arg, OutT]) getConfig(userId string, lock *db.ConfigLock) (*reso if err != nil { return nil, err } - mconfig, err := resources.LoadConfig(c, types.Metadatas) + mconfig, err := resources.LoadConfig(c, metadata.Metadatas) if err != nil { return nil, err } @@ -164,11 +165,14 @@ func (s Service[Arg, OutT]) readFromConfig(ctx context.Context, c *resources.Mul if err != nil { return *new(OutT), err } - - out, err := r.GetMetadata().ReadFromState(r, state) + m, err := r.GetMetadata(metadata.Metadatas) if err != nil { return *new(OutT), err } + out, err := m.ReadFromState(r, state) + if err != nil { + return *new(OutT), errors.InternalServerErrorWithMessage(fmt.Sprintf("Unable to retrieve data for resource %s.", r.GetResourceId()), err) + } return out.(OutT), err } } diff --git a/encoder/translater.go b/encoder/translater.go index 90acc837..1da1f6e4 100644 --- a/encoder/translater.go +++ b/encoder/translater.go @@ -7,6 +7,7 @@ import ( "github.com/multycloud/multy/resources" "github.com/multycloud/multy/resources/output" "github.com/multycloud/multy/resources/types" + "github.com/multycloud/multy/resources/types/metadata" "github.com/multycloud/multy/util" "github.com/multycloud/multy/validate" "golang.org/x/exp/maps" @@ -26,7 +27,15 @@ func TranslateResources(decodedResources *DecodedResources, ctx resources.MultyC errors[err] = true } if len(validationErrors) == 0 { - translationCache[r], err = r.Translate(ctx) + m, err2 := r.GetMetadata(metadata.Metadatas) + if err2 != nil { + return nil, nil, err2 + } + cr, err2 := m.GetCloudSpecificResource(r) + if err2 != nil { + return nil, nil, err2 + } + translationCache[r], err = cr.Translate(ctx) if err != nil { return translationCache, nil, err } diff --git a/resources/resource.go b/resources/resource.go index d6d0245a..fe4150a6 100644 --- a/resources/resource.go +++ b/resources/resource.go @@ -10,6 +10,7 @@ import ( "github.com/multycloud/multy/validate" "golang.org/x/exp/maps" "golang.org/x/exp/slices" + "google.golang.org/protobuf/proto" ) type Resources struct { @@ -50,7 +51,13 @@ func (r *Resources) GetAll() []Resource { // Get finds the resource with the given id and adds a dependency between dependentResourceId and id. func Get[T Resource](dependentResourceId string, resources *Resources, id string) (T, error) { - // TODO return better error on empty ID + if id == "" { + return *new(T), errors.ValidationError(validate.ValidationError{ + ErrorMessage: fmt.Sprintf("Required id is empty in resource %s.", dependentResourceId), + ResourceId: id, + }) + } + item, exists, err := GetOptional[T](dependentResourceId, resources, id) if err != nil { return item, err @@ -135,55 +142,34 @@ func mergeGroups(all map[Resource]*MultyResourceGroup, res1 Resource, res2 Resou } } -func GetCloudSpecificResourceId(r Resource, cloud commonpb.CloudProvider) string { - return GetResourceIdForCloud(r.GetResourceId(), cloud) -} - -func GetResourceIdForCloud(resourceId string, cloud commonpb.CloudProvider) string { - return fmt.Sprintf("%s.%s", cloud, resourceId) -} - -type CloudSpecificResource struct { - Cloud commonpb.CloudProvider - Resource Resource - ImplicitlyCreated bool -} - -func (c *CloudSpecificResource) GetResourceId() string { - return GetCloudSpecificResourceId(c.Resource, c.Cloud) -} - -func (c *CloudSpecificResource) GetLocation(ctx MultyContext) string { - return c.Resource.GetCloudSpecificLocation() -} - -func (c *CloudSpecificResource) Translate(ctx MultyContext) ([]output.TfBlock, error) { - return c.Resource.Translate(ctx) -} - type Resource interface { - Translate(ctx MultyContext) ([]output.TfBlock, error) - GetResourceId() string - GetCloudSpecificLocation() string + GetCloud() commonpb.CloudProvider Validate(ctx MultyContext) []validate.ValidationError - GetMainResourceName() (string, error) + GetMetadata(ResourceMetadatas) (ResourceMetadataInterface, error) +} - GetCloud() commonpb.CloudProvider +type CloudSpecificResource[OutT proto.Message] interface { + FromState(state *output.TfState) (OutT, error) - GetCommonArgs() any + CloudSpecificResourceTranslator + Resource +} - GetMetadata() ResourceMetadataInterface +type CloudSpecificResourceTranslator interface { + GetMainResourceName() (string, error) + Translate(ctx MultyContext) ([]output.TfBlock, error) } -func (c *CloudSpecificResource) GetMainOutputId() (string, error) { - return GetMainOutputId(c.Resource) +type namedResource interface { + GetMainResourceName() (string, error) + GetResourceId() string } -func GetMainOutputId(r Resource) (string, error) { +func GetMainOutputId(r namedResource) (string, error) { name, err := r.GetMainResourceName() if err != nil { return "", err @@ -191,7 +177,7 @@ func GetMainOutputId(r Resource) (string, error) { return fmt.Sprintf("%s.%s.id", name, r.GetResourceId()), nil } -func GetMainOutputRef(r Resource) (string, error) { +func GetMainOutputRef(r namedResource) (string, error) { name, err := r.GetMainResourceName() if err != nil { return "", err diff --git a/resources/resource_metadata.go b/resources/resource_metadata.go index cc9cb8c8..4724a23c 100644 --- a/resources/resource_metadata.go +++ b/resources/resource_metadata.go @@ -3,51 +3,77 @@ package resources import ( "fmt" "github.com/multycloud/multy/api/errors" + "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/configpb" "github.com/multycloud/multy/resources/output" "golang.org/x/exp/slices" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/anypb" + "reflect" "sort" ) -type ResourceCreateFunc[T proto.Message, O Resource] func(string, T, *Resources) (O, error) -type ResourceUpdateFunc[T proto.Message, O Resource] func(O, T, *Resources) error -type ResourceReadFromStateFunc[O Resource, OutT proto.Message] func(O, *output.TfState) (OutT, error) +type ResourceMetadatas map[proto.Message]ResourceMetadataInterface -type ExportFunc[T proto.Message, R Resource] func(R, *Resources) (T, bool, error) -type ImportFunc[T proto.Message, O Resource] func(string, T, *Resources) (O, error) +type ResourceTranslator[OutT proto.Message] interface { + Translate(MultyContext) ([]output.TfBlock, error) + FromState(state *output.TfState) (OutT, error) +} + +type ResourceExporter[ArgsT proto.Message] interface { + Create(resourceId string, args ArgsT, others *Resources) error + Update(args ArgsT, others *Resources) error + Import(resourceId string, args ArgsT, others *Resources) error + Export(others *Resources) (ArgsT, bool, error) -type ResourceMetadata[ArgsT proto.Message, R Resource, OutT proto.Message] struct { - CreateFunc ResourceCreateFunc[ArgsT, R] - UpdateFunc ResourceUpdateFunc[ArgsT, R] - ReadFromStateFunc ResourceReadFromStateFunc[R, OutT] + Resource +} - ImportFunc ImportFunc[ArgsT, R] - ExportFunc ExportFunc[ArgsT, R] +type ResourceMetadata[ArgsT proto.Message, R ResourceExporter[ArgsT], OutT proto.Message] struct { + Translators map[commonpb.CloudProvider]func(R) ResourceTranslator[OutT] AbbreviatedName string + // Used for error messages + ResourceType string } func (m *ResourceMetadata[ArgsT, R, OutT]) Create(resourceId string, args proto.Message, resources *Resources) (Resource, error) { - return m.CreateFunc(resourceId, args.(ArgsT), resources) + r := reflect.New(reflect.TypeOf(*new(R)).Elem()).Interface().(R) + err := r.Create(resourceId, args.(ArgsT), resources) + return r, err } func (m *ResourceMetadata[ArgsT, R, OutT]) Update(resource Resource, args proto.Message, resources *Resources) error { - return m.UpdateFunc(resource.(R), args.(ArgsT), resources) + r := resource.(ResourceExporter[ArgsT]) + return r.Update(args.(ArgsT), resources) } func (m *ResourceMetadata[ArgsT, R, OutT]) ReadFromState(resource Resource, state *output.TfState) (proto.Message, error) { - return m.ReadFromStateFunc(resource.(R), state) + out, err := m.Translators[resource.GetCloud()](resource.(R)).FromState(state) + if err != nil { + return nil, err + } + return out, nil } func (m *ResourceMetadata[ArgsT, R, OutT]) Export(resource Resource, resources *Resources) (proto.Message, bool, error) { - return m.ExportFunc(resource.(R), resources) + r := resource.(ResourceExporter[ArgsT]) + return r.Export(resources) } func (m *ResourceMetadata[ArgsT, R, OutT]) Import(resourceId string, args proto.Message, resources *Resources) (Resource, error) { - return m.ImportFunc(resourceId, args.(ArgsT), resources) + // TODO: do this without reflection + r := reflect.New(reflect.TypeOf(*new(R)).Elem()).Interface().(R) + err := r.Import(resourceId, args.(ArgsT), resources) + return r, err +} + +func (m *ResourceMetadata[ArgsT, R, OutT]) GetCloudSpecificResource(r Resource) (CloudSpecificResourceTranslator, error) { + if _, ok := m.Translators[r.GetCloud()]; !ok { + return nil, fmt.Errorf("resource of type %s not supported in cloud %s", m.ResourceType, r.GetCloud()) + } + return m.Translators[r.GetCloud()](r.(R)).(CloudSpecificResourceTranslator), nil } func (m *ResourceMetadata[ArgsT, R, OutT]) GetAbbreviatedName() string { @@ -61,6 +87,8 @@ type ResourceMetadataInterface interface { Export(Resource, *Resources) (proto.Message, bool, error) Import(string, proto.Message, *Resources) (Resource, error) + GetCloudSpecificResource(r Resource) (CloudSpecificResourceTranslator, error) + GetAbbreviatedName() string } @@ -69,7 +97,7 @@ type MultyConfig struct { c *configpb.Config groupsByResourceId map[Resource]*MultyResourceGroup affectedResourcesById map[string][]string - metadatas map[proto.Message]ResourceMetadataInterface + metadatas ResourceMetadatas affectedResourcesByGroupId map[string][]string } @@ -77,7 +105,7 @@ func (c *MultyConfig) GetUserId() string { return c.c.UserId } -func LoadConfig(c *configpb.Config, metadatas map[proto.Message]ResourceMetadataInterface) (*MultyConfig, error) { +func LoadConfig(c *configpb.Config, metadatas ResourceMetadatas) (*MultyConfig, error) { multyc := &MultyConfig{ c: c, metadatas: metadatas, @@ -85,7 +113,7 @@ func LoadConfig(c *configpb.Config, metadatas map[proto.Message]ResourceMetadata res := NewResources() for _, r := range c.Resources { - conv, err := multyc.getConverter(r.ResourceArgs.ResourceArgs.MessageName()) + conv, err := multyc.metadatas.getConverter(r.ResourceArgs.ResourceArgs.MessageName()) if err != nil { return multyc, err } @@ -109,7 +137,7 @@ func LoadConfig(c *configpb.Config, metadatas map[proto.Message]ResourceMetadata return multyc, nil } -func (c *MultyConfig) GetOriginalConfig(metadatas map[proto.Message]ResourceMetadataInterface) (*MultyConfig, error) { +func (c *MultyConfig) GetOriginalConfig(metadatas ResourceMetadatas) (*MultyConfig, error) { return LoadConfig(c.c, metadatas) } @@ -128,7 +156,7 @@ func addMultyResource(r *configpb.Resource, res *Resources, metadata ResourceMet } func (c *MultyConfig) CreateResource(args proto.Message) (Resource, error) { - conv, err := c.getConverter(proto.MessageName(args)) + conv, err := c.metadatas.getConverter(proto.MessageName(args)) if err != nil { return nil, err } @@ -143,7 +171,7 @@ func (c *MultyConfig) CreateResource(args proto.Message) (Resource, error) { } func (c *MultyConfig) UpdateResource(resourceId string, args proto.Message) (Resource, error) { - conv, err := c.getConverter(proto.MessageName(args)) + conv, err := c.metadatas.getConverter(proto.MessageName(args)) if err != nil { return nil, err } @@ -218,7 +246,11 @@ func (c *MultyConfig) ExportConfig() (*configpb.Config, error) { } for _, r := range c.Resources.GetAll() { - msg, export, err := r.GetMetadata().Export(r, c.Resources) + m, err := r.GetMetadata(c.metadatas) + if err != nil { + return nil, err + } + msg, export, err := m.Export(r, c.Resources) if err != nil { return nil, err } @@ -240,8 +272,8 @@ func (c *MultyConfig) ExportConfig() (*configpb.Config, error) { return result, nil } -func (c *MultyConfig) getConverter(name protoreflect.FullName) (ResourceMetadataInterface, error) { - for messageType, conv := range c.metadatas { +func (m ResourceMetadatas) getConverter(name protoreflect.FullName) (ResourceMetadataInterface, error) { + for messageType, conv := range m { if name == proto.MessageName(messageType) { return conv, nil } diff --git a/resources/resource_with_id.go b/resources/resource_with_id.go index 0c932157..f0df5583 100644 --- a/resources/resource_with_id.go +++ b/resources/resource_with_id.go @@ -8,14 +8,17 @@ import ( "github.com/multycloud/multy/validate" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" ) type WithCommonParams interface { GetCommonParameters() *commonpb.ResourceCommonArgs + proto.Message } type WithChildCommonParams interface { GetCommonParameters() *commonpb.ChildResourceCommonArgs + proto.Message } type ResourceWithId[T WithCommonParams] struct { @@ -48,8 +51,12 @@ func (r *ResourceWithId[T]) GetCloud() commonpb.CloudProvider { return r.Args.GetCommonParameters().CloudProvider } -func (r *ResourceWithId[T]) GetCommonArgs() any { - return r.Args.GetCommonParameters() +func (r *ResourceWithId[T]) GetMetadata(m ResourceMetadatas) (ResourceMetadataInterface, error) { + converter, err := m.getConverter(proto.MessageName(r.Args)) + if err != nil { + return nil, err + } + return converter, nil } func (r *ResourceWithId[T]) Validate() (errs []validate.ValidationError) { @@ -77,13 +84,29 @@ type ChildResourceWithId[ParentT Resource, ChildT WithChildCommonParams] struct ResourceId string Args ChildT Parent ParentT + Metadata ResourceMetadataInterface +} + +func NewResource[T WithCommonParams](resourceId string, args T) ResourceWithId[T] { + return ResourceWithId[T]{ + ResourceId: resourceId, + Args: args, + } } -func (r ResourceWithId[T]) NewValidationError(err error, field string) validate.ValidationError { - return newError(err, r.ResourceId, field) +func NewChildResource[ParentT Resource, ChildT WithChildCommonParams](resourceId string, parent ParentT, args ChildT) ChildResourceWithId[ParentT, ChildT] { + return ChildResourceWithId[ParentT, ChildT]{ + ResourceId: resourceId, + Args: args, + Parent: parent, + } +} + +func (r *ResourceWithId[T]) NewValidationError(err error, field string) validate.ValidationError { + return NewError(err, r.ResourceId, field) } -func newError(err error, resourceId string, fieldName string) validate.ValidationError { +func NewError(err error, resourceId string, fieldName string) validate.ValidationError { result := validate.ValidationError{ ErrorMessage: err.Error(), ResourceId: resourceId, @@ -97,11 +120,11 @@ func newError(err error, resourceId string, fieldName string) validate.Validatio return result } -func (r ChildResourceWithId[A, B]) NewValidationError(err error, field string) validate.ValidationError { - return newError(err, r.ResourceId, field) +func (r *ChildResourceWithId[A, B]) NewValidationError(err error, field string) validate.ValidationError { + return NewError(err, r.ResourceId, field) } -func (r ChildResourceWithId[A, B]) GetResourceId() string { +func (r *ChildResourceWithId[A, B]) GetResourceId() string { return r.ResourceId } @@ -113,6 +136,10 @@ func (r *ChildResourceWithId[A, B]) GetCloudSpecificLocation() string { return r.Parent.GetCloudSpecificLocation() } -func (r *ChildResourceWithId[A, B]) GetCommonArgs() any { - return r.Args.GetCommonParameters() +func (r *ChildResourceWithId[A, B]) GetMetadata(m ResourceMetadatas) (ResourceMetadataInterface, error) { + converter, err := m.getConverter(proto.MessageName(r.Args)) + if err != nil { + return nil, err + } + return converter, nil } diff --git a/resources/types/all_resources_metadata.go b/resources/types/all_resources_metadata.go deleted file mode 100644 index c28b57fa..00000000 --- a/resources/types/all_resources_metadata.go +++ /dev/null @@ -1,29 +0,0 @@ -package types - -import ( - "github.com/multycloud/multy/api/proto/resourcespb" - "github.com/multycloud/multy/resources" - "google.golang.org/protobuf/proto" -) - -var Metadatas = map[proto.Message]resources.ResourceMetadataInterface{ - &resourcespb.VirtualNetworkArgs{}: &virtualNetworkMetadata, - &resourcespb.SubnetArgs{}: &subnetMetadata, - &resourcespb.DatabaseArgs{}: &dbMetadata, - &resourcespb.PublicIpArgs{}: &publicIpMetadata, - &resourcespb.RouteTableArgs{}: &routeTableMetadata, - &resourcespb.RouteTableAssociationArgs{}: &routeTableAssociationMetadata, - &resourcespb.KubernetesNodePoolArgs{}: &kubernetesNodePoolMetadata, - &resourcespb.KubernetesClusterArgs{}: &kubernetesClusterMetadata, - &resourcespb.LambdaArgs{}: &lambdaMetadata, - &resourcespb.NetworkInterfaceArgs{}: &networkInterfaceMetadata, - &resourcespb.NetworkInterfaceSecurityGroupAssociationArgs{}: &networkInterfaceSecurityGroupAssociationMetadata, - &resourcespb.NetworkSecurityGroupArgs{}: &networkSecurityGroupMetadata, - &resourcespb.ObjectStorageArgs{}: &objectStorageMetadata, - &resourcespb.ObjectStorageObjectArgs{}: &objectStorageObjectMetadata, - &resourcespb.VaultArgs{}: &vaultMetadata, - &resourcespb.VaultAccessPolicyArgs{}: &vaultAccessPolicyMetadata, - &resourcespb.VaultSecretArgs{}: &vaultSecretMetadata, - &resourcespb.VirtualMachineArgs{}: &virtualMachineMetadata, - &resourcespb.ResourceGroupArgs{}: &resourceGroupMetadata, -} diff --git a/resources/types/aws/database.go b/resources/types/aws/database.go new file mode 100644 index 00000000..18ade233 --- /dev/null +++ b/resources/types/aws/database.go @@ -0,0 +1,117 @@ +package aws_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/database" + "github.com/multycloud/multy/resources/output/network_security_group" + "github.com/multycloud/multy/resources/types" + "github.com/multycloud/multy/util" + "strings" +) + +type AwsDatabase struct { + *types.Database +} + +func InitDatabase(r *types.Database) resources.ResourceTranslator[*resourcespb.DatabaseResource] { + return AwsDatabase{r} +} + +func (r AwsDatabase) FromState(state *output.TfState) (*resourcespb.DatabaseResource, error) { + host := "dyrun" + if !flags.DryRun { + values, err := state.GetValues(database.AwsDbInstance{}, r.ResourceId) + if err != nil { + return nil, err + } + host = values["address"].(string) + } + + return &resourcespb.DatabaseResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + Engine: r.Args.Engine, + EngineVersion: r.Args.EngineVersion, + StorageGb: r.Args.StorageGb, + Size: r.Args.Size, + Username: r.Args.Username, + Password: r.Args.Password, + SubnetIds: r.Args.SubnetIds, + Host: host, + ConnectionUsername: r.Args.Username, + }, nil +} + +func (r AwsDatabase) Translate(_ resources.MultyContext) ([]output.TfBlock, error) { + subnetIds, err := util.MapSliceValuesErr(r.Subnets, func(v *types.Subnet) (string, error) { + return resources.GetMainOutputId(AwsSubnet{v}) + }) + if err != nil { + return nil, err + } + vpcId, err := resources.GetMainOutputId(AwsVirtualNetwork{r.Subnets[0].VirtualNetwork}) + if err != nil { + return nil, err + } + + name := common.RemoveSpecialChars(r.Args.Name) + dbSubnetGroup := database.AwsDbSubnetGroup{ + AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), + Name: r.Args.Name, + Description: "Managed by Multy", + SubnetIds: subnetIds, + } + nsg := network_security_group.AwsSecurityGroup{ + AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), + VpcId: vpcId, + Name: r.Args.Name, + Description: fmt.Sprintf("Default security group of %s", r.Args.Name), + Ingress: []network_security_group.AwsSecurityGroupRule{{ + Protocol: "-1", + FromPort: 0, + ToPort: 0, + CidrBlocks: []string{"0.0.0.0/0"}, + }}, + Egress: []network_security_group.AwsSecurityGroupRule{{ + Protocol: "-1", + FromPort: 0, + ToPort: 0, + CidrBlocks: []string{"0.0.0.0/0"}, + }}, + } + return []output.TfBlock{ + dbSubnetGroup, + nsg, + database.AwsDbInstance{ + AwsResource: common.NewAwsResource(r.ResourceId, name), + AllocatedStorage: int(r.Args.StorageGb), + Engine: strings.ToLower(r.Args.Engine.String()), + EngineVersion: r.Args.EngineVersion, + Username: r.Args.Username, + Password: r.Args.Password, + InstanceClass: common.DBSIZE[r.Args.Size][r.GetCloud()], + Identifier: r.Args.Name, + SkipFinalSnapshot: true, + DbSubnetGroupName: dbSubnetGroup.GetResourceName(), + PubliclyAccessible: true, + VpcSecurityGroupIds: []string{fmt.Sprintf("%s.%s.id", output.GetResourceName(nsg), nsg.ResourceId)}, + Port: int(r.Args.Port), + }, + }, nil +} + +func (r AwsDatabase) GetMainResourceName() (string, error) { + return database.AwsResourceName, nil +} diff --git a/resources/types/aws/kubernetes_cluster.go b/resources/types/aws/kubernetes_cluster.go new file mode 100644 index 00000000..fe110d7f --- /dev/null +++ b/resources/types/aws/kubernetes_cluster.go @@ -0,0 +1,278 @@ +package aws_resources + +import ( + "fmt" + "github.com/apparentlymart/go-cidr/cidr" + "github.com/multycloud/multy/api/errors" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/iam" + "github.com/multycloud/multy/resources/output/kubernetes_service" + "github.com/multycloud/multy/resources/output/route_table" + "github.com/multycloud/multy/resources/output/route_table_association" + "github.com/multycloud/multy/resources/output/subnet" + "github.com/multycloud/multy/resources/output/virtual_network" + "github.com/multycloud/multy/resources/types" + "github.com/multycloud/multy/validate" + "gopkg.in/yaml.v3" + "net" +) + +type AwsKubernetesCluster struct { + *types.KubernetesCluster +} + +func InitKubernetesCluster(r *types.KubernetesCluster) resources.ResourceTranslator[*resourcespb.KubernetesClusterResource] { + return AwsKubernetesCluster{r} +} + +type KubeConfig struct { + ApiVersion string `yaml:"apiVersion"` + Clusters []NamedKubeConfigCluster `yaml:"clusters"` + Contexts []NamedKubeConfigContext `yaml:"contexts"` + CurrentContext string `yaml:"current-context"` + Users []KubeConfigUser `yaml:"users"` + Kind string `yaml:"kind"` +} + +type NamedKubeConfigCluster struct { + Name string `yaml:"name"` + Cluster KubeConfigCluster `yaml:"cluster"` +} + +type KubeConfigCluster struct { + CertificateAuthorityData string `yaml:"certificate-authority-data"` + Server string `yaml:"server"` +} + +type NamedKubeConfigContext struct { + Name string `yaml:"name"` + Context KubeConfigContext `yaml:"context"` +} + +type KubeConfigContext struct { + User string `yaml:"user"` + Cluster string `yaml:"cluster"` +} + +type KubeConfigUser struct { + Name string `yaml:"name"` + User struct { + Exec KubeConfigExec `yaml:"exec"` + } `yaml:"user"` +} + +type KubeConfigExec struct { + ApiVersion string `yaml:"apiVersion"` + Command string `yaml:"command"` + Args []string `yaml:"args"` + InteractiveMode string `yaml:"interactiveMode"` +} + +func createKubeConfig(clusterName string, certData string, endpoint string, awsRegion string) (string, error) { + username := fmt.Sprintf("clusterUser_%s", clusterName) + kubeConfig := &KubeConfig{ + ApiVersion: "v1", + Kind: "Config", + Clusters: []NamedKubeConfigCluster{ + { + Name: clusterName, + Cluster: KubeConfigCluster{ + CertificateAuthorityData: certData, + Server: endpoint, + }, + }, + }, + Contexts: []NamedKubeConfigContext{ + { + Name: clusterName, + Context: KubeConfigContext{ + User: username, + Cluster: clusterName, + }, + }, + }, + Users: []KubeConfigUser{ + { + Name: username, + User: struct { + Exec KubeConfigExec `yaml:"exec"` + }{ + Exec: KubeConfigExec{ + ApiVersion: "client.authentication.k8s.io/v1beta1", + Command: "aws", + Args: []string{"--region", awsRegion, "eks", "get-token", "--cluster-name", clusterName}, + InteractiveMode: "IfAvailable", + }, + }, + }, + }, + CurrentContext: clusterName, + } + + s, err := yaml.Marshal(kubeConfig) + if err != nil { + return "", fmt.Errorf("unable to marshal kube config, %s", err) + } + + return string(s), nil +} + +func (r AwsKubernetesCluster) FromState(state *output.TfState) (*resourcespb.KubernetesClusterResource, error) { + result := &resourcespb.KubernetesClusterResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + ServiceCidr: r.Args.ServiceCidr, + VirtualNetworkId: r.Args.VirtualNetworkId, + } + result.Endpoint = "dryrun" + if !flags.DryRun { + values, err := state.GetValues(kubernetes_service.AwsEksCluster{}, r.ResourceId) + if err != nil { + return nil, err + } + result.Endpoint = values["endpoint"].(string) + result.CaCertificate = values["certificate_authority"].([]interface{})[0].(map[string]interface{})["data"].(string) + kubeCgfRaw, err := createKubeConfig(r.Args.Name, result.CaCertificate, result.Endpoint, r.GetCloudSpecificLocation()) + if err != nil { + return nil, err + } + result.KubeConfigRaw = kubeCgfRaw + } + + return result, nil +} + +func (r AwsKubernetesCluster) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { + var outputs []output.TfBlock + defaultNodePoolResources, err := AwsKubernetesNodePool{r.DefaultNodePool}.Translate(ctx) + if err != nil { + return nil, err + } + outputs = append(outputs, defaultNodePoolResources...) + subnets, subnetResources, err := r.getAwsSubnets() + if err != nil { + return nil, err + } + var subnetIds []string + for _, s := range subnets { + subnetIds = append(subnetIds, fmt.Sprintf("%s.%s.id", output.GetResourceName(s), s.ResourceId)) + } + + outputs = append(outputs, subnetResources...) + var deps []string + for _, s := range subnetResources { + // todo: get the id without casting + deps = append(deps, fmt.Sprintf("%s.%s", output.GetResourceName(s), s.GetResourceId())) + } + + roleName := fmt.Sprintf("multy-k8cluster-%s-role", r.Args.Name) + role := iam.AwsIamRole{ + AwsResource: common.NewAwsResource(r.ResourceId, roleName), + Name: roleName, + AssumeRolePolicy: iam.NewAssumeRolePolicy("eks.amazonaws.com"), + } + + policy1Id := fmt.Sprintf("%s_%s", r.ResourceId, "AmazonEKSClusterPolicy") + policy1 := iam.AwsIamRolePolicyAttachment{ + AwsResource: common.NewAwsResourceWithIdOnly(policy1Id), + Role: fmt.Sprintf("aws_iam_role.%s.name", r.ResourceId), + PolicyArn: "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy", + } + policy2Id := fmt.Sprintf("%s_%s", r.ResourceId, "AmazonEKSVPCResourceController") + policy2 := iam.AwsIamRolePolicyAttachment{ + AwsResource: common.NewAwsResourceWithIdOnly(policy2Id), + Role: fmt.Sprintf("aws_iam_role.%s.name", r.ResourceId), + PolicyArn: "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController", + } + deps = append(deps, fmt.Sprintf("%s.%s", output.GetResourceName(policy1), policy1Id), + fmt.Sprintf("%s.%s", output.GetResourceName(policy1), policy2Id)) + + outputs = append(outputs, &role, + policy1, + policy2, + &kubernetes_service.AwsEksCluster{ + AwsResource: common.NewAwsResourceWithDeps(r.ResourceId, r.Args.Name, deps), + RoleArn: fmt.Sprintf("aws_iam_role.%s.arn", r.ResourceId), + VpcConfig: kubernetes_service.VpcConfig{SubnetIds: subnetIds, EndpointPrivateAccess: true}, + Name: r.Args.Name, + KubernetesNetworkConfig: kubernetes_service.KubernetesNetworkConfig{ + ServiceIpv4Cidr: r.Args.ServiceCidr, + }, + }) + return outputs, nil +} + +func (r AwsKubernetesCluster) GetMainResourceName() (string, error) { + return output.GetResourceName(kubernetes_service.AwsEksCluster{}), nil +} + +func (r AwsKubernetesCluster) getAwsSubnets() ([]subnet.AwsSubnet, []output.TfBlock, error) { + block := r.VirtualNetwork.Args.CidrBlock + _, vnNet, err := net.ParseCIDR(block) + if err != nil { + return nil, nil, err + } + tempSubnet, _ := cidr.NextSubnet(vnNet, 31) + subnetBlock1, _ := cidr.PreviousSubnet(tempSubnet, 28) + subnetBlock2, _ := cidr.PreviousSubnet(subnetBlock1, 28) + validationError := validate.ValidationError{ + ErrorMessage: fmt.Sprintf("Not enough availabilty zones available in region %s. Kubernetes clusters in AWS require 2 availabilty zones.", r.VirtualNetwork.GetLocation()), + ResourceId: r.ResourceId, + FieldName: "virtual_network_id", + } + az1, err := common.GetAvailabilityZone(r.VirtualNetwork.GetLocation(), 1, r.GetCloud()) + if err != nil { + return nil, nil, errors.ValidationErrors([]validate.ValidationError{validationError}) + } + az2, err := common.GetAvailabilityZone(r.VirtualNetwork.GetLocation(), 2, r.GetCloud()) + if err != nil { + return nil, nil, errors.ValidationErrors([]validate.ValidationError{validationError}) + } + + vpcId, err := resources.GetMainOutputId(AwsVirtualNetwork{r.VirtualNetwork}) + if err != nil { + return nil, nil, err + } + rt := route_table.AwsRouteTable{ + AwsResource: common.NewAwsResource(r.ResourceId+"_public_rt", r.Args.Name+"_public_rt"), + VpcId: vpcId, + Routes: []route_table.AwsRouteTableRoute{ + { + CidrBlock: "0.0.0.0/0", + GatewayId: AwsVirtualNetwork{r.VirtualNetwork}.GetAssociatedInternetGateway(), + }, + }, + } + + subnet1 := subnet.AwsSubnet{ + AwsResource: common.NewAwsResource(r.ResourceId+"_public_subnet", r.Args.Name+"_public_subnet"), + CidrBlock: subnetBlock1.String(), + VpcId: fmt.Sprintf("%s.%s.id", virtual_network.AwsResourceName, r.VirtualNetwork.ResourceId), + AvailabilityZone: az1, + } + subnet2 := subnet.AwsSubnet{ + AwsResource: common.NewAwsResource(r.ResourceId+"_private_subnet", r.Args.Name+"_private_subnet"), + CidrBlock: subnetBlock2.String(), + VpcId: fmt.Sprintf("%s.%s.id", virtual_network.AwsResourceName, r.VirtualNetwork.ResourceId), + AvailabilityZone: az2, + } + + rta := route_table_association.AwsRouteTableAssociation{ + AwsResource: common.NewAwsResourceWithIdOnly(r.ResourceId + "_public_rta"), + SubnetId: fmt.Sprintf("%s.%s.id", output.GetResourceName(subnet1), subnet1.ResourceId), + RouteTableId: fmt.Sprintf("%s.%s.id", output.GetResourceName(rt), rt.ResourceId), + } + + return []subnet.AwsSubnet{subnet1, subnet2}, []output.TfBlock{subnet1, subnet2, rt, rta}, nil +} diff --git a/resources/types/aws/kubernetes_node_pool.go b/resources/types/aws/kubernetes_node_pool.go new file mode 100644 index 00000000..71507e08 --- /dev/null +++ b/resources/types/aws/kubernetes_node_pool.go @@ -0,0 +1,103 @@ +package aws_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/iam" + "github.com/multycloud/multy/resources/output/kubernetes_node_pool" + "github.com/multycloud/multy/resources/types" +) + +type AwsKubernetesNodePool struct { + *types.KubernetesNodePool +} + +func InitKubernetesNodePool(r *types.KubernetesNodePool) resources.ResourceTranslator[*resourcespb.KubernetesNodePoolResource] { + return AwsKubernetesNodePool{r} +} + +func (r AwsKubernetesNodePool) FromState(state *output.TfState) (*resourcespb.KubernetesNodePoolResource, error) { + return &resourcespb.KubernetesNodePoolResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + Name: r.Args.Name, + SubnetId: r.Args.SubnetId, + ClusterId: r.Args.ClusterId, + StartingNodeCount: r.Args.StartingNodeCount, + MinNodeCount: r.Args.MinNodeCount, + MaxNodeCount: r.Args.MaxNodeCount, + VmSize: r.Args.VmSize, + DiskSizeGb: r.Args.DiskSizeGb, + Labels: r.Args.Labels, + AwsOverride: r.Args.AwsOverride, + AzureOverride: r.Args.AzureOverride, + }, nil +} + +func (r AwsKubernetesNodePool) Translate(_ resources.MultyContext) ([]output.TfBlock, error) { + subnetId, err := resources.GetMainOutputId(AwsSubnet{r.Subnet}) + if err != nil { + return nil, err + } + + var instanceTypes []string + if r.Args.AwsOverride.GetInstanceTypes() != nil { + instanceTypes = r.Args.AwsOverride.GetInstanceTypes() + } else { + instanceTypes = []string{common.VMSIZE[r.Args.VmSize][r.GetCloud()]} + } + + roleName := fmt.Sprintf("multy-k8nodepool-%s-%s-role", r.KubernetesCluster.Args.Name, r.Args.Name) + role := iam.AwsIamRole{ + AwsResource: common.NewAwsResource(r.ResourceId, roleName), + Name: roleName, + AssumeRolePolicy: iam.NewAssumeRolePolicy("ec2.amazonaws.com"), + } + clusterId, err := resources.GetMainOutputId(AwsKubernetesCluster{r.KubernetesCluster}) + if err != nil { + return nil, err + } + return []output.TfBlock{ + &role, + iam.AwsIamRolePolicyAttachment{ + AwsResource: common.NewAwsResourceWithIdOnly(fmt.Sprintf("%s_%s", r.ResourceId, "AmazonEKSWorkerNodePolicy")), + Role: fmt.Sprintf("aws_iam_role.%s.name", r.ResourceId), + PolicyArn: "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy", + }, + iam.AwsIamRolePolicyAttachment{ + AwsResource: common.NewAwsResourceWithIdOnly(fmt.Sprintf("%s_%s", r.ResourceId, "AmazonEKS_CNI_Policy")), + Role: fmt.Sprintf("aws_iam_role.%s.name", r.ResourceId), + PolicyArn: "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", + }, + iam.AwsIamRolePolicyAttachment{ + AwsResource: common.NewAwsResourceWithIdOnly(fmt.Sprintf("%s_%s", r.ResourceId, "AmazonEC2ContainerRegistryReadOnly")), + Role: fmt.Sprintf("aws_iam_role.%s.name", r.ResourceId), + PolicyArn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly", + }, + &kubernetes_node_pool.AwsKubernetesNodeGroup{ + AwsResource: common.NewAwsResourceWithIdOnly(r.ResourceId), + ClusterName: clusterId, + NodeGroupName: r.Args.Name, + NodeRoleArn: fmt.Sprintf("aws_iam_role.%s.arn", r.ResourceId), + SubnetIds: []string{subnetId}, + ScalingConfig: kubernetes_node_pool.ScalingConfig{ + DesiredSize: int(r.Args.StartingNodeCount), + MaxSize: int(r.Args.MaxNodeCount), + MinSize: int(r.Args.MinNodeCount), + }, + Labels: r.Args.Labels, + InstanceTypes: instanceTypes, + }, + }, nil + +} + +func (r AwsKubernetesNodePool) GetMainResourceName() (string, error) { + return output.GetResourceName(kubernetes_node_pool.AwsKubernetesNodeGroup{}), nil +} diff --git a/resources/types/aws/network_interface.go b/resources/types/aws/network_interface.go new file mode 100644 index 00000000..6b2d7ecd --- /dev/null +++ b/resources/types/aws/network_interface.go @@ -0,0 +1,72 @@ +package aws_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/network_interface" + "github.com/multycloud/multy/resources/types" +) + +type AwsNetworkInterface struct { + *types.NetworkInterface +} + +func InitNetworkInterface(r *types.NetworkInterface) resources.ResourceTranslator[*resourcespb.NetworkInterfaceResource] { + return AwsNetworkInterface{r} +} + +func (r AwsNetworkInterface) FromState(_ *output.TfState) (*resourcespb.NetworkInterfaceResource, error) { + return &resourcespb.NetworkInterfaceResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + SubnetId: r.Args.SubnetId, + PublicIpId: r.Args.PublicIpId, + }, nil +} + +func (r AwsNetworkInterface) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { + var pIpId string + subnetId, err := resources.GetMainOutputId(AwsSubnet{r.Subnet}) + if err != nil { + return nil, err + } + if r.PublicIp != nil { + pIpId, err = resources.GetMainOutputId(AwsPublicIp{r.PublicIp}) + if err != nil { + return nil, err + } + } + + var res []output.TfBlock + nic := network_interface.AwsNetworkInterface{ + AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), + SubnetId: subnetId, + } + if pIpId != "" { + res = append(res, network_interface.AwsEipAssociation{ + AwsResource: &common.AwsResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + }, + AllocationId: pIpId, + NetworkInterfaceId: fmt.Sprintf("%s.%s.id", output.GetResourceName(nic), nic.ResourceId), + }) + } + + res = append(res, nic) + + return res, nil +} + +func (r AwsNetworkInterface) GetMainResourceName() (string, error) { + return network_interface.AwsResourceName, nil +} diff --git a/resources/types/aws/network_interface_security_group_association.go b/resources/types/aws/network_interface_security_group_association.go new file mode 100644 index 00000000..0e803b73 --- /dev/null +++ b/resources/types/aws/network_interface_security_group_association.go @@ -0,0 +1,54 @@ +package aws_resources + +import ( + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/network_interface_security_group_association" + "github.com/multycloud/multy/resources/types" +) + +type AwsNetworkInterfaceSecurityGroupAssociation struct { + *types.NetworkInterfaceSecurityGroupAssociation +} + +func InitNetworkInterfaceSecurityGroupAssociation(vn *types.NetworkInterfaceSecurityGroupAssociation) resources.ResourceTranslator[*resourcespb.NetworkInterfaceSecurityGroupAssociationResource] { + return AwsNetworkInterfaceSecurityGroupAssociation{vn} +} + +func (r AwsNetworkInterfaceSecurityGroupAssociation) FromState(state *output.TfState) (*resourcespb.NetworkInterfaceSecurityGroupAssociationResource, error) { + return &resourcespb.NetworkInterfaceSecurityGroupAssociationResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + NetworkInterfaceId: r.Args.NetworkInterfaceId, + SecurityGroupId: r.Args.SecurityGroupId, + }, nil +} + +func (r AwsNetworkInterfaceSecurityGroupAssociation) Translate(resources.MultyContext) ([]output.TfBlock, error) { + nicId, err := resources.GetMainOutputId(AwsNetworkInterface{r.NetworkInterface}) + if err != nil { + return nil, err + } + nsgId, err := resources.GetMainOutputId(AwsNetworkSecurityGroup{r.NetworkSecurityGroup}) + if err != nil { + return nil, err + } + return []output.TfBlock{ + network_interface_security_group_association.AwsNetworkInterfaceSecurityGroupAssociation{ + AwsResource: &common.AwsResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + }, + NetworkInterfaceId: nicId, + NetworkSecurityGroupId: nsgId, + }, + }, nil +} + +func (r AwsNetworkInterfaceSecurityGroupAssociation) GetMainResourceName() (string, error) { + return network_interface_security_group_association.AwsResourceName, nil +} diff --git a/resources/types/aws/network_security_group.go b/resources/types/aws/network_security_group.go new file mode 100644 index 00000000..2071afa8 --- /dev/null +++ b/resources/types/aws/network_security_group.go @@ -0,0 +1,128 @@ +package aws_resources + +import ( + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/network_security_group" + "github.com/multycloud/multy/resources/types" +) + +type AwsNetworkSecurityGroup struct { + *types.NetworkSecurityGroup +} + +func InitNetworkSecurityGroup(r *types.NetworkSecurityGroup) resources.ResourceTranslator[*resourcespb.NetworkSecurityGroupResource] { + return AwsNetworkSecurityGroup{r} +} + +const ( + INGRESS = "ingress" + EGRESS = "egress" + BOTH = "both" + ALLOW = "allow" + DENY = "deny" +) + +func (r AwsNetworkSecurityGroup) FromState(_ *output.TfState) (*resourcespb.NetworkSecurityGroupResource, error) { + return &resourcespb.NetworkSecurityGroupResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + VirtualNetworkId: r.Args.VirtualNetworkId, + Rules: r.Args.Rules, + }, nil +} + +func (r AwsNetworkSecurityGroup) Translate(_ resources.MultyContext) ([]output.TfBlock, error) { + awsRules := translateAwsNsgRules(r.Args.Rules) + + allowVpcTraffic := network_security_group.AwsSecurityGroupRule{ + Protocol: "-1", + FromPort: 0, + ToPort: 0, + CidrBlocks: []string{r.VirtualNetwork.Args.CidrBlock}, + } + + awsRules[INGRESS] = append(awsRules[INGRESS], allowVpcTraffic) + awsRules[EGRESS] = append(awsRules[EGRESS], allowVpcTraffic) + + vnId, err := resources.GetMainOutputId(AwsVirtualNetwork{r.VirtualNetwork}) + if err != nil { + return nil, err + } + return []output.TfBlock{ + network_security_group.AwsSecurityGroup{ + AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), + VpcId: vnId, + Name: r.Args.Name, + Description: "Managed by Multy", + Ingress: awsRules["ingress"], + Egress: awsRules["egress"], + }, + }, nil +} +func translateAwsNsgRules(rules []*resourcespb.NetworkSecurityRule) map[string][]network_security_group.AwsSecurityGroupRule { + awsRules := map[string][]network_security_group.AwsSecurityGroupRule{} + + for _, rule := range rules { + awsFromPort := int(rule.PortRange.From) + awsToPort := int(rule.PortRange.To) + + awsProtocol := rule.Protocol + if rule.Protocol == "*" { + awsProtocol = "-1" + awsFromPort = 0 + awsToPort = 0 + } + + if rule.Direction == resourcespb.Direction_BOTH_DIRECTIONS { + awsRules[INGRESS] = append( + awsRules[INGRESS], network_security_group.AwsSecurityGroupRule{ + Protocol: awsProtocol, + FromPort: awsFromPort, + ToPort: awsToPort, + CidrBlocks: []string{rule.CidrBlock}, + }, + ) + awsRules[EGRESS] = append( + awsRules[EGRESS], network_security_group.AwsSecurityGroupRule{ + Protocol: awsProtocol, + FromPort: awsFromPort, + ToPort: awsToPort, + CidrBlocks: []string{rule.CidrBlock}, + }, + ) + } else if rule.Direction == resourcespb.Direction_EGRESS { + awsRules[EGRESS] = append( + awsRules[EGRESS], network_security_group.AwsSecurityGroupRule{ + Protocol: awsProtocol, + FromPort: awsFromPort, + ToPort: awsToPort, + CidrBlocks: []string{rule.CidrBlock}, + }, + ) + } else if rule.Direction == resourcespb.Direction_INGRESS { + awsRules[INGRESS] = append( + awsRules[INGRESS], network_security_group.AwsSecurityGroupRule{ + Protocol: awsProtocol, + FromPort: awsFromPort, + ToPort: awsToPort, + CidrBlocks: []string{rule.CidrBlock}, + }, + ) + } + } + return awsRules +} + +func (r AwsNetworkSecurityGroup) GetMainResourceName() (string, error) { + return network_security_group.AwsSecurityGroupResourceName, nil +} diff --git a/resources/types/aws/object_storage.go b/resources/types/aws/object_storage.go new file mode 100644 index 00000000..8eae7406 --- /dev/null +++ b/resources/types/aws/object_storage.go @@ -0,0 +1,59 @@ +package aws_resources + +import ( + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/object_storage" + "github.com/multycloud/multy/resources/types" +) + +type AwsObjectStorage struct { + *types.ObjectStorage +} + +func InitObjectStorage(vn *types.ObjectStorage) resources.ResourceTranslator[*resourcespb.ObjectStorageResource] { + return AwsObjectStorage{vn} +} + +func (r AwsObjectStorage) FromState(state *output.TfState) (*resourcespb.ObjectStorageResource, error) { + return &resourcespb.ObjectStorageResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + Versioning: r.Args.Versioning, + }, nil +} + +func (r AwsObjectStorage) Translate(resources.MultyContext) ([]output.TfBlock, error) { + var awsResources []output.TfBlock + s3Bucket := object_storage.AwsS3Bucket{ + AwsResource: &common.AwsResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + }, + Bucket: r.Args.Name, + } + awsResources = append(awsResources, s3Bucket) + + if r.Args.Versioning { + awsResources = append(awsResources, object_storage.AwsS3BucketVersioning{ + AwsResource: &common.AwsResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + }, + BucketId: s3Bucket.GetBucketId(), + VersioningConfiguration: object_storage.VersioningConfiguration{Status: "Enabled"}, + }) + } + return awsResources, nil +} + +func (r AwsObjectStorage) GetMainResourceName() (string, error) { + return "aws_s3_bucket", nil +} diff --git a/resources/types/aws/object_storage_object.go b/resources/types/aws/object_storage_object.go new file mode 100644 index 00000000..97ef616d --- /dev/null +++ b/resources/types/aws/object_storage_object.go @@ -0,0 +1,84 @@ +package aws_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/object_storage" + "github.com/multycloud/multy/resources/output/object_storage_object" + "github.com/multycloud/multy/resources/types" +) + +type AwsObjectStorageObject struct { + *types.ObjectStorageObject +} + +func InitObjectStorageObject(vn *types.ObjectStorageObject) resources.ResourceTranslator[*resourcespb.ObjectStorageObjectResource] { + return AwsObjectStorageObject{vn} +} + +func (r AwsObjectStorageObject) FromState(state *output.TfState) (*resourcespb.ObjectStorageObjectResource, error) { + out := new(resourcespb.ObjectStorageObjectResource) + out.CommonParameters = &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + } + + id, err := resources.GetMainOutputRef(r) + if err != nil { + return nil, err + } + + out.Name = r.Args.Name + out.ContentBase64 = r.Args.ContentBase64 + out.ContentType = r.Args.ContentType + out.ObjectStorageId = r.Args.ObjectStorageId + out.Acl = r.Args.Acl + out.Source = r.Args.Source + + if !flags.DryRun { + stateResource, err := output.GetParsed[object_storage.AwsS3Bucket](state, id) + if err != nil { + return nil, err + } + out.Url = fmt.Sprintf("https://%s.s3.amazonaws.com/%s", stateResource.Bucket, r.Args.Name) + } else { + out.Url = "dryrun" + } + + return out, nil +} + +func (r AwsObjectStorageObject) Translate(resources.MultyContext) ([]output.TfBlock, error) { + var acl string + if r.Args.Acl == resourcespb.ObjectStorageObjectAcl_PUBLIC_READ { + acl = "public-read" + } else { + acl = "private" + } + + bucketId, err := resources.GetMainOutputId(AwsObjectStorage{r.ObjectStorage}) + if err != nil { + return nil, err + } + + return []output.TfBlock{object_storage_object.AwsS3BucketObject{ + AwsResource: &common.AwsResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + }, + Bucket: bucketId, + Key: r.Args.Name, + Acl: acl, + ContentBase64: r.Args.ContentBase64, + ContentType: r.Args.ContentType, + Source: r.Args.Source, + }}, nil +} + +func (r AwsObjectStorageObject) GetMainResourceName() (string, error) { + return "aws_s3_object", nil +} diff --git a/resources/types/aws/public_ip.go b/resources/types/aws/public_ip.go new file mode 100644 index 00000000..a664fab0 --- /dev/null +++ b/resources/types/aws/public_ip.go @@ -0,0 +1,57 @@ +package aws_resources + +import ( + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/public_ip" + "github.com/multycloud/multy/resources/types" +) + +type AwsPublicIp struct { + *types.PublicIp +} + +func InitPublicIp(vn *types.PublicIp) resources.ResourceTranslator[*resourcespb.PublicIpResource] { + return AwsPublicIp{vn} +} + +func (r AwsPublicIp) FromState(state *output.TfState) (*resourcespb.PublicIpResource, error) { + ip := "dryrun" + if !flags.DryRun { + values, err := state.GetValues(public_ip.AwsElasticIp{}, r.ResourceId) + if err != nil { + return nil, err + } + ip = values["public_ip"].(string) + } + + return &resourcespb.PublicIpResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + + Ip: ip, + }, nil +} + +func (r AwsPublicIp) Translate(resources.MultyContext) ([]output.TfBlock, error) { + return []output.TfBlock{ + public_ip.AwsElasticIp{ + AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), + //Vpc: true, + }, + }, nil +} + +func (r AwsPublicIp) GetMainResourceName() (string, error) { + return public_ip.AwsResourceName, nil +} diff --git a/resources/types/aws/resource_group.go b/resources/types/aws/resource_group.go new file mode 100644 index 00000000..c409821d --- /dev/null +++ b/resources/types/aws/resource_group.go @@ -0,0 +1,36 @@ +package aws_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/types" +) + +type ResourceGroup struct { + *types.ResourceGroup +} + +func InitResourceGroup(rg *types.ResourceGroup) resources.ResourceTranslator[*resourcespb.ResourceGroupResource] { + return ResourceGroup{rg} +} + +func (rg ResourceGroup) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { + return nil, nil +} + +func (rg ResourceGroup) FromState(_ *output.TfState) (*resourcespb.ResourceGroupResource, error) { + return &resourcespb.ResourceGroupResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: rg.ResourceId, + Location: rg.Args.CommonParameters.Location, + CloudProvider: rg.Args.CommonParameters.CloudProvider, + }, + }, nil +} + +func (rg ResourceGroup) GetMainResourceName() (string, error) { + return "", fmt.Errorf("cloud %s is not supported for this resource type ", rg.GetCloud()) +} diff --git a/resources/types/aws/route_table.go b/resources/types/aws/route_table.go new file mode 100644 index 00000000..20fc77c1 --- /dev/null +++ b/resources/types/aws/route_table.go @@ -0,0 +1,111 @@ +package aws_resources + +import ( + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/route_table" + "github.com/multycloud/multy/resources/types" +) + +type AwsRouteTable struct { + *types.RouteTable +} + +func InitRouteTable(vn *types.RouteTable) resources.ResourceTranslator[*resourcespb.RouteTableResource] { + return AwsRouteTable{vn} +} + +func (r AwsRouteTable) FromState(state *output.TfState) (*resourcespb.RouteTableResource, error) { + if flags.DryRun { + return &resourcespb.RouteTableResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + Name: r.Args.Name, + VirtualNetworkId: r.Args.VirtualNetworkId, + Routes: r.Args.Routes, + }, nil + } + out := new(resourcespb.RouteTableResource) + out.CommonParameters = &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + } + + id, err := resources.GetMainOutputRef(r) + if err != nil { + return nil, err + } + + switch r.GetCloud() { + case common.AWS: + stateResource, err := output.GetParsed[route_table.AwsRouteTable](state, id) + if err != nil { + return nil, err + } + out.Name = stateResource.AwsResource.Tags["Name"] + out.VirtualNetworkId = r.Args.VirtualNetworkId + var routes []*resourcespb.Route + for _, r := range stateResource.Routes { + route := &resourcespb.Route{ + CidrBlock: r.CidrBlock, + Destination: resourcespb.RouteDestination_INTERNET, + } + routes = append(routes, route) + } + out.Routes = routes + case common.AZURE: + stateResource, err := output.GetParsed[route_table.AzureRouteTable](state, id) + if err != nil { + return nil, err + } + out.Name = stateResource.Name + out.VirtualNetworkId = r.Args.VirtualNetworkId + var routes []*resourcespb.Route + for _, r := range stateResource.Routes { + route := &resourcespb.Route{ + CidrBlock: r.AddressPrefix, + Destination: resourcespb.RouteDestination_INTERNET, + } + routes = append(routes, route) + } + out.Routes = routes + } + + return out, nil +} + +func (r AwsRouteTable) Translate(resources.MultyContext) ([]output.TfBlock, error) { + vpcId, err := resources.GetMainOutputId(AwsVirtualNetwork{r.VirtualNetwork}) + if err != nil { + return nil, err + } + rt := route_table.AwsRouteTable{ + AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), + VpcId: vpcId, + } + + var routes []route_table.AwsRouteTableRoute + for _, route := range r.Args.Routes { + if route.Destination == resourcespb.RouteDestination_INTERNET { + routes = append( + routes, route_table.AwsRouteTableRoute{ + CidrBlock: route.CidrBlock, + GatewayId: AwsVirtualNetwork{r.VirtualNetwork}.GetAssociatedInternetGateway(), + }, + ) + } + } + rt.Routes = routes + + return []output.TfBlock{rt}, nil +} + +func (r AwsRouteTable) GetMainResourceName() (string, error) { + return route_table.AwsResourceName, nil +} diff --git a/resources/types/aws/route_table_association.go b/resources/types/aws/route_table_association.go new file mode 100644 index 00000000..fa7fbffd --- /dev/null +++ b/resources/types/aws/route_table_association.go @@ -0,0 +1,55 @@ +package aws_resources + +import ( + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/route_table_association" + "github.com/multycloud/multy/resources/output/virtual_network" + "github.com/multycloud/multy/resources/types" +) + +type AwsRouteTableAssociation struct { + *types.RouteTableAssociation +} + +func InitRouteTableAssociation(vn *types.RouteTableAssociation) resources.ResourceTranslator[*resourcespb.RouteTableAssociationResource] { + return AwsRouteTableAssociation{vn} +} + +func (r AwsRouteTableAssociation) FromState(state *output.TfState) (*resourcespb.RouteTableAssociationResource, error) { + return &resourcespb.RouteTableAssociationResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + SubnetId: r.Args.SubnetId, + RouteTableId: r.Args.RouteTableId, + }, nil +} + +func (r AwsRouteTableAssociation) Translate(resources.MultyContext) ([]output.TfBlock, error) { + rtId, err := resources.GetMainOutputId(AwsRouteTable{r.RouteTable}) + if err != nil { + return nil, err + } + subnetId, err := resources.GetMainOutputId(AwsSubnet{r.Subnet}) + if err != nil { + return nil, err + } + return []output.TfBlock{ + route_table_association.AwsRouteTableAssociation{ + AwsResource: &common.AwsResource{ + TerraformResource: output.TerraformResource{ResourceId: r.Subnet.ResourceId}, + }, + RouteTableId: rtId, + SubnetId: subnetId, + }, + }, nil +} + +func (r AwsRouteTableAssociation) GetMainResourceName() (string, error) { + return virtual_network.AwsResourceName, nil +} diff --git a/resources/types/aws/subnet.go b/resources/types/aws/subnet.go new file mode 100644 index 00000000..b7a1e468 --- /dev/null +++ b/resources/types/aws/subnet.go @@ -0,0 +1,84 @@ +package aws_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/subnet" + "github.com/multycloud/multy/resources/output/virtual_network" + "github.com/multycloud/multy/resources/types" +) + +type AwsSubnet struct { + *types.Subnet +} + +func InitSubnet(r *types.Subnet) resources.ResourceTranslator[*resourcespb.SubnetResource] { + return AwsSubnet{r} +} + +func (r AwsSubnet) FromState(state *output.TfState) (*resourcespb.SubnetResource, error) { + if flags.DryRun { + return &resourcespb.SubnetResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + Name: r.Args.Name, + CidrBlock: r.Args.CidrBlock, + AvailabilityZone: r.Args.AvailabilityZone, + VirtualNetworkId: r.Args.VirtualNetworkId, + }, nil + } + out := new(resourcespb.SubnetResource) + out.CommonParameters = &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + } + out.AvailabilityZone = r.Args.AvailabilityZone + out.VirtualNetworkId = r.Args.GetVirtualNetworkId() + + id, err := resources.GetMainOutputRef(r) + if err != nil { + return nil, err + } + stateResource, err := output.GetParsed[subnet.AwsSubnet](state, id) + if err != nil { + return nil, err + } + out.Name = stateResource.AwsResource.Tags["Name"] + out.CidrBlock = stateResource.CidrBlock + return out, nil +} + +func (r AwsSubnet) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { + location := r.VirtualNetwork.GetLocation() + az, err := common.GetAvailabilityZone(location, int(r.Args.AvailabilityZone), r.GetCloud()) + if err != nil { + return nil, err + } + awsSubnet := subnet.AwsSubnet{ + AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), + CidrBlock: r.Args.CidrBlock, + VpcId: fmt.Sprintf("%s.%s.id", virtual_network.AwsResourceName, r.VirtualNetwork.ResourceId), + AvailabilityZone: az, + } + // This flag needs to be set so that eks nodes can connect to the kubernetes cluster + // https://aws.amazon.com/blogs/containers/upcoming-changes-to-ip-assignment-for-eks-managed-node-groups/ + // How to tell if this subnet is private? + if len(resources.GetAllResourcesWithRef(ctx, func(k *types.KubernetesNodePool) *types.Subnet { return k.Subnet }, r.Subnet)) > 0 { + awsSubnet.MapPublicIpOnLaunch = true + } + if len(resources.GetAllResourcesWithRef(ctx, func(k *types.KubernetesCluster) *types.Subnet { return k.DefaultNodePool.Subnet }, r.Subnet)) > 0 { + awsSubnet.MapPublicIpOnLaunch = true + } + return []output.TfBlock{awsSubnet}, nil +} + +func (r AwsSubnet) GetMainResourceName() (string, error) { + return subnet.AwsResourceName, nil +} diff --git a/resources/types/aws/vault.go b/resources/types/aws/vault.go new file mode 100644 index 00000000..532b6abe --- /dev/null +++ b/resources/types/aws/vault.go @@ -0,0 +1,43 @@ +package aws_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/types" +) + +type AwsVault struct { + *types.Vault +} + +func InitVault(vn *types.Vault) resources.ResourceTranslator[*resourcespb.VaultResource] { + return AwsVault{vn} +} + +type awsClientConfig struct { + *output.TerraformDataSource `hcl:",squash" default:"name=awsrm_client_config"` +} + +func (r AwsVault) FromState(state *output.TfState) (*resourcespb.VaultResource, error) { + return &resourcespb.VaultResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + }, nil +} + +func (r AwsVault) Translate(resources.MultyContext) ([]output.TfBlock, error) { + return nil, nil +} + +func (r AwsVault) GetMainResourceName() (string, error) { + return "", fmt.Errorf("vaults doesn't output any resources in AWS") +} diff --git a/resources/types/aws/vault_access_policy.go b/resources/types/aws/vault_access_policy.go new file mode 100644 index 00000000..b8ded3c9 --- /dev/null +++ b/resources/types/aws/vault_access_policy.go @@ -0,0 +1,38 @@ +package aws_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/types" +) + +type AwsVaultAccessPolicy struct { + *types.VaultAccessPolicy +} + +func InitVaultAccessPolicy(vn *types.VaultAccessPolicy) resources.ResourceTranslator[*resourcespb.VaultAccessPolicyResource] { + return AwsVaultAccessPolicy{vn} +} + +func (r AwsVaultAccessPolicy) FromState(state *output.TfState) (*resourcespb.VaultAccessPolicyResource, error) { + return &resourcespb.VaultAccessPolicyResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + VaultId: r.Args.VaultId, + Identity: r.Args.Identity, + Access: r.Args.Access, + }, nil +} + +func (r AwsVaultAccessPolicy) Translate(resources.MultyContext) ([]output.TfBlock, error) { + return nil, nil +} + +func (r AwsVaultAccessPolicy) GetMainResourceName() (string, error) { + return "", fmt.Errorf("vault access policy doesn't output any resources in AWS") +} diff --git a/resources/types/aws/vault_secret.go b/resources/types/aws/vault_secret.go new file mode 100644 index 00000000..5632b818 --- /dev/null +++ b/resources/types/aws/vault_secret.go @@ -0,0 +1,49 @@ +package aws_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/vault_secret" + "github.com/multycloud/multy/resources/types" +) + +type AwsVaultSecret struct { + *types.VaultSecret +} + +func InitVaultSecret(vn *types.VaultSecret) resources.ResourceTranslator[*resourcespb.VaultSecretResource] { + return AwsVaultSecret{vn} +} + +func (r AwsVaultSecret) FromState(state *output.TfState) (*resourcespb.VaultSecretResource, error) { + return &resourcespb.VaultSecretResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + Name: r.Args.Name, + Value: r.Args.Value, + VaultId: r.Args.VaultId, + }, nil +} + +func (r AwsVaultSecret) Translate(resources.MultyContext) ([]output.TfBlock, error) { + return []output.TfBlock{ + vault_secret.AwsSsmParameter{ + AwsResource: &common.AwsResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + }, + Name: fmt.Sprintf("/%s/%s", r.Vault.Args.Name, r.Args.Name), + Type: "SecureString", + Value: r.Args.Value, + }, + }, nil +} + +func (r AwsVaultSecret) GetMainResourceName() (string, error) { + return vault_secret.AwsResourceName, nil +} diff --git a/resources/types/aws/virtual_machine.go b/resources/types/aws/virtual_machine.go new file mode 100644 index 00000000..8ad2849f --- /dev/null +++ b/resources/types/aws/virtual_machine.go @@ -0,0 +1,236 @@ +package aws_resources + +import ( + "encoding/json" + "fmt" + "github.com/multy-dev/hclencoder" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/iam" + "github.com/multycloud/multy/resources/output/virtual_machine" + "github.com/multycloud/multy/resources/types" + "github.com/multycloud/multy/util" +) + +type AwsVirtualMachine struct { + *types.VirtualMachine +} + +func InitVirtualMachine(vn *types.VirtualMachine) resources.ResourceTranslator[*resourcespb.VirtualMachineResource] { + return AwsVirtualMachine{vn} +} + +func (r AwsVirtualMachine) FromState(state *output.TfState) (*resourcespb.VirtualMachineResource, error) { + var ip string + identityId := "dryrun" + if r.Args.GeneratePublicIp { + ip = "dryrun" + } + + if !flags.DryRun { + if r.Args.GeneratePublicIp { + values, err := state.GetValues(virtual_machine.AwsEC2{}, r.ResourceId) + if err != nil { + return nil, err + } + ip = values["public_ip"].(string) + } + values, err := state.GetValues(iam.AwsIamRole{}, r.ResourceId) + if err != nil { + return nil, err + } + identityId = values["id"].(string) + } + + // TODO: handle default values on create + if r.Args.ImageReference == nil { + r.Args.ImageReference = &resourcespb.ImageReference{ + Os: resourcespb.ImageReference_UBUNTU, + Version: "16.04", + } + } + + return &resourcespb.VirtualMachineResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + NetworkInterfaceIds: r.Args.NetworkInterfaceIds, + NetworkSecurityGroupIds: r.Args.NetworkSecurityGroupIds, + VmSize: r.Args.VmSize, + UserDataBase64: r.Args.UserDataBase64, + SubnetId: r.Args.SubnetId, + PublicSshKey: r.Args.PublicSshKey, + PublicIpId: r.Args.PublicIpId, + GeneratePublicIp: r.Args.GeneratePublicIp, + ImageReference: r.Args.ImageReference, + AwsOverride: r.Args.AwsOverride, + AzureOverride: r.Args.AzureOverride, + + PublicIp: ip, + IdentityId: identityId, + }, nil +} + +type AwsCallerIdentityData struct { + *output.TerraformDataSource `hcl:",squash" default:"name=aws_caller_identity"` +} + +type AwsRegionData struct { + *output.TerraformDataSource `hcl:",squash" default:"name=aws_region"` +} + +func (r AwsVirtualMachine) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { + if r.Args.UserDataBase64 != "" { + r.Args.UserDataBase64 = fmt.Sprintf(hclencoder.EscapeString(r.Args.UserDataBase64)) + } + + subnetId, err := resources.GetMainOutputId(AwsSubnet{r.Subnet}) + if err != nil { + return nil, err + } + + var awsResources []output.TfBlock + var ec2NicIds []virtual_machine.AwsEc2NetworkInterface + for i, ni := range r.NetworkInterface { + niId, err := resources.GetMainOutputId(AwsNetworkInterface{ni}) + if err != nil { + return nil, err + } + ec2NicIds = append( + ec2NicIds, virtual_machine.AwsEc2NetworkInterface{ + NetworkInterfaceId: niId, + DeviceIndex: i, + }, + ) + } + + nsgIds, err := util.MapSliceValuesErr(r.NetworkSecurityGroups, func(v *types.NetworkSecurityGroup) (string, error) { + return resources.GetMainOutputId(AwsNetworkSecurityGroup{v}) + }) + if err != nil { + return nil, err + } + + var vmSize string + if r.Args.AwsOverride.GetInstanceType() != "" { + vmSize = r.Args.AwsOverride.GetInstanceType() + } else { + vmSize = common.VMSIZE[r.Args.VmSize][r.GetCloud()] + } + + ec2 := virtual_machine.AwsEC2{ + AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), + InstanceType: vmSize, + AssociatePublicIpAddress: r.Args.GeneratePublicIp, + UserDataBase64: r.Args.UserDataBase64, + SubnetId: subnetId, + NetworkInterfaces: ec2NicIds, + SecurityGroupIds: nsgIds, + } + + if len(ec2NicIds) != 0 { + ec2.SubnetId = "" + ec2.AssociatePublicIpAddress = false + } + + iamRole := iam.AwsIamRole{ + AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), + Name: r.GetIdentity(), + AssumeRolePolicy: iam.NewAssumeRolePolicy("ec2.amazonaws.com"), + } + + if vault := getVaultAssociatedIdentity(ctx, r.GetIdentity()); vault != nil { + awsResources = append(awsResources, + AwsCallerIdentityData{TerraformDataSource: &output.TerraformDataSource{ResourceId: r.ResourceId}}, + AwsRegionData{TerraformDataSource: &output.TerraformDataSource{ResourceId: r.ResourceId}}) + + policy, err := json.Marshal(iam.AwsIamPolicy{ + Statement: []iam.AwsIamPolicyStatement{{ + Action: []string{"ssm:GetParameter*"}, + Effect: "Allow", + Resource: fmt.Sprintf("arn:aws:ssm:${data.aws_region.%s.name}:${data.aws_caller_identity.%s.account_id}:parameter/%s/*", r.ResourceId, r.ResourceId, vault.Args.Name), + }, { + Action: []string{"ssm:DescribeParameters"}, + Effect: "Allow", + Resource: "*", + }}, + Version: "2012-10-17", + }) + + if err != nil { + return nil, fmt.Errorf("unable to encode aws policy: %s", err) + } + + iamRole.InlinePolicy = iam.AwsIamRoleInlinePolicy{ + Name: "vault_policy", + // we need to have an expression here because we use template strings within the policy json + Policy: fmt.Sprintf("\"%s\"", hclencoder.EscapeString(string(policy))), + } + } + + iamInstanceProfile := iam.AwsIamInstanceProfile{ + AwsResource: &common.AwsResource{TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}}, + Name: r.GetIdentity(), + Role: fmt.Sprintf( + "%s.%s.name", output.GetResourceName(iam.AwsIamRole{}), iamRole.ResourceId, + ), + } + + ec2.IamInstanceProfile = iamInstanceProfile.GetId() + + awsResources = append(awsResources, + iamInstanceProfile, + iamRole, + ) + + // this gives permission to write cloudwatch logs + + // adding ssh key to r requires aws key pair resource + // key pair will be added and referenced via key_name parameter + if r.Args.PublicSshKey != "" { + keyPair := virtual_machine.AwsKeyPair{ + AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), + KeyName: fmt.Sprintf("%s_multy", r.ResourceId), + PublicKey: r.Args.PublicSshKey, + } + ec2.KeyName = fmt.Sprintf("%s.%s.key_name", virtual_machine.AwsKeyPairResourceName, r.ResourceId) + awsResources = append(awsResources, keyPair) + } + + awsAmi, err := virtual_machine.LatestAwsAmi(r.Args.ImageReference, r.ResourceId) + if err != nil { + return nil, err + } + + ec2.Ami = fmt.Sprintf("%s.id", awsAmi.GetFullResourceRef()) + + awsResources = append(awsResources, awsAmi, ec2) + return awsResources, nil + +} + +func (r AwsVirtualMachine) GetMainResourceName() (string, error) { + return virtual_machine.AwsResourceName, nil +} + +func getVaultAssociatedIdentity(ctx resources.MultyContext, identity string) *types.Vault { + for _, resource := range resources.GetAllResources[*types.VaultAccessPolicy](ctx) { + if identity == resource.Args.Identity { + return resource.Vault + } + } + return nil +} + +func (r AwsVirtualMachine) GetIdentity() string { + return r.GetAwsIdentity() +} diff --git a/resources/types/aws/virtual_network.go b/resources/types/aws/virtual_network.go new file mode 100644 index 00000000..0bb9e3c7 --- /dev/null +++ b/resources/types/aws/virtual_network.go @@ -0,0 +1,98 @@ +package aws_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/network_security_group" + "github.com/multycloud/multy/resources/output/virtual_network" + "github.com/multycloud/multy/resources/types" +) + +type AwsVirtualNetwork struct { + *types.VirtualNetwork +} + +func InitVirtualNetwork(vn *types.VirtualNetwork) resources.ResourceTranslator[*resourcespb.VirtualNetworkResource] { + return AwsVirtualNetwork{vn} +} + +func (r AwsVirtualNetwork) FromState(state *output.TfState) (*resourcespb.VirtualNetworkResource, error) { + if flags.DryRun { + return &resourcespb.VirtualNetworkResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.GetResourceId(), + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + CidrBlock: r.Args.CidrBlock, + }, nil + } + out := new(resourcespb.VirtualNetworkResource) + out.CommonParameters = &commonpb.CommonResourceParameters{ + ResourceId: r.GetResourceId(), + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.GetCloud(), + NeedsUpdate: false, + } + + id, err := resources.GetMainOutputRef(r) + if err != nil { + return nil, err + } + + stateResource, err := output.GetParsed[virtual_network.AwsVpc](state, id) + if err != nil { + return nil, err + } + out.Name = stateResource.AwsResource.Tags["Name"] + out.CidrBlock = stateResource.CidrBlock + + return out, nil +} + +func (r AwsVirtualNetwork) Translate(resources.MultyContext) ([]output.TfBlock, error) { + vpc := virtual_network.AwsVpc{ + AwsResource: common.NewAwsResource(r.GetResourceId(), r.Args.Name), + CidrBlock: r.Args.CidrBlock, + EnableDnsHostnames: true, + } + // TODO make conditional on route_table_association with Internet Destination + igw := virtual_network.AwsInternetGateway{ + AwsResource: common.NewAwsResource(r.GetResourceId(), r.Args.Name), + VpcId: fmt.Sprintf("%s.%s.id", virtual_network.AwsResourceName, r.ResourceId), + } + allowAllSgRule := []network_security_group.AwsSecurityGroupRule{{ + Protocol: "-1", + FromPort: 0, + ToPort: 0, + Self: true, + }} + sg := network_security_group.AwsDefaultSecurityGroup{ + AwsResource: common.NewAwsResource(r.GetResourceId(), r.Args.Name), + VpcId: fmt.Sprintf("%s.%s.id", virtual_network.AwsResourceName, r.ResourceId), + Ingress: allowAllSgRule, + Egress: allowAllSgRule, + } + return []output.TfBlock{ + vpc, + igw, + sg, + }, nil +} + +func (r AwsVirtualNetwork) GetMainResourceName() (string, error) { + return virtual_network.AwsResourceName, nil +} + +func (r AwsVirtualNetwork) GetAssociatedInternetGateway() string { + return fmt.Sprintf("%s.%s.id", virtual_network.AwsInternetGatewayName, r.ResourceId) +} diff --git a/resources/types/azure/database.go b/resources/types/azure/database.go new file mode 100644 index 00000000..6454398b --- /dev/null +++ b/resources/types/azure/database.go @@ -0,0 +1,104 @@ +package azure_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/database" + "github.com/multycloud/multy/resources/types" + "github.com/multycloud/multy/util" + "strings" +) + +type AzureDatabase struct { + *types.Database +} + +func InitDatabase(r *types.Database) resources.ResourceTranslator[*resourcespb.DatabaseResource] { + return AzureDatabase{r} +} + +func (r AzureDatabase) FromState(state *output.TfState) (*resourcespb.DatabaseResource, error) { + host := "dyrun" + if !flags.DryRun { + var azureDatabaseEngine database.AzureDatabaseEngine + + if r.Args.Engine == resourcespb.DatabaseEngine_MYSQL { + azureDatabaseEngine = database.AzureMySqlServer{} + } else if r.Args.Engine == resourcespb.DatabaseEngine_POSTGRES { + azureDatabaseEngine = database.AzurePostgreSqlServer{} + } else if r.Args.Engine == resourcespb.DatabaseEngine_MARIADB { + azureDatabaseEngine = database.AzureMariaDbServer{} + } else { + return nil, fmt.Errorf("unhandled engine %s", r.Args.Engine.String()) + } + + values, err := state.GetValues(azureDatabaseEngine, r.ResourceId) + if err != nil { + return nil, err + } + host = values["fqdn"].(string) + } + + return &resourcespb.DatabaseResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + Engine: r.Args.Engine, + EngineVersion: r.Args.EngineVersion, + StorageGb: r.Args.StorageGb, + Size: r.Args.Size, + Username: r.Args.Username, + Password: r.Args.Password, + SubnetIds: r.Args.SubnetIds, + Host: host, + ConnectionUsername: fmt.Sprintf("%s@%s", r.Args.Username, host), + }, nil +} + +func (r AzureDatabase) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { + subnetIds, err := util.MapSliceValuesErr(r.Subnets, func(v *types.Subnet) (string, error) { + return resources.GetMainOutputId(AzureSubnet{v}) + }) + if err != nil { + return nil, err + } + return database.NewAzureDatabase( + database.AzureDbServer{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + Name: r.Args.Name, + ResourceGroupName: GetResourceGroupName(r.Args.GetCommonParameters().ResourceGroupId), + Location: r.GetCloudSpecificLocation(), + }, + Engine: strings.ToLower(r.Args.Engine.String()), + Version: r.Args.EngineVersion, + StorageMb: int(r.Args.StorageGb * 1024), + AdministratorLogin: r.Args.Username, + AdministratorLoginPassword: r.Args.Password, + SkuName: common.DBSIZE[r.Args.Size][r.GetCloud()], + SubnetIds: subnetIds, + }, + ), nil +} + +func (r AzureDatabase) GetMainResourceName() (string, error) { + switch r.Args.Engine { + case resourcespb.DatabaseEngine_MYSQL: + return database.AzureMysqlResourceName, nil + case resourcespb.DatabaseEngine_MARIADB: + return database.AzureMariaDbResourceName, nil + case resourcespb.DatabaseEngine_POSTGRES: + return database.AzurePostgresqlResourceName, nil + } + return "", fmt.Errorf("unhandled engine %s", r.Args.Engine.String()) +} diff --git a/resources/types/azure/kubernetes_cluster.go b/resources/types/azure/kubernetes_cluster.go new file mode 100644 index 00000000..440ace47 --- /dev/null +++ b/resources/types/azure/kubernetes_cluster.go @@ -0,0 +1,75 @@ +package azure_resources + +import ( + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/kubernetes_service" + "github.com/multycloud/multy/resources/types" +) + +type AzureKubernetesCluster struct { + *types.KubernetesCluster +} + +func InitKubernetesCluster(r *types.KubernetesCluster) resources.ResourceTranslator[*resourcespb.KubernetesClusterResource] { + return AzureKubernetesCluster{r} +} + +func (r AzureKubernetesCluster) FromState(state *output.TfState) (*resourcespb.KubernetesClusterResource, error) { + result := &resourcespb.KubernetesClusterResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + ServiceCidr: r.Args.ServiceCidr, + VirtualNetworkId: r.Args.VirtualNetworkId, + } + result.Endpoint = "dryrun" + if !flags.DryRun { + values, err := state.GetValues(kubernetes_service.AzureEksCluster{}, r.ResourceId) + if err != nil { + return nil, err + } + result.Endpoint = values["kube_config"].([]interface{})[0].(map[string]interface{})["host"].(string) + result.CaCertificate = values["kube_config"].([]interface{})[0].(map[string]interface{})["cluster_ca_certificate"].(string) + result.KubeConfigRaw = values["kube_config_raw"].(string) + } + + return result, nil +} + +func (r AzureKubernetesCluster) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { + defaultPool, err := AzureKubernetesNodePool{r.DefaultNodePool}.translateAzNodePool() + if err != nil { + return nil, err + } + defaultPool.Name = defaultPool.AzResource.Name + defaultPool.AzResource = nil + defaultPool.ClusterId = "" + + return []output.TfBlock{ + &kubernetes_service.AzureEksCluster{ + AzResource: common.NewAzResource(r.ResourceId, r.Args.Name, GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId), r.GetCloudSpecificLocation()), + DefaultNodePool: defaultPool, + DnsPrefix: common.UniqueId(r.Args.Name, "aks", common.LowercaseAlphanumericFormatFunc), + Identity: kubernetes_service.AzureIdentity{Type: "SystemAssigned"}, + NetworkProfile: kubernetes_service.NetworkProfile{ + NetworkPlugin: "azure", + DnsServiceIp: "10.100.0.10", + DockerBridgeCidr: "172.17.0.1/16", + ServiceCidr: r.Args.ServiceCidr, + }, + }, + }, nil +} +func (r AzureKubernetesCluster) GetMainResourceName() (string, error) { + return output.GetResourceName(kubernetes_service.AzureEksCluster{}), nil +} diff --git a/resources/types/azure/kubernetes_node_pool.go b/resources/types/azure/kubernetes_node_pool.go new file mode 100644 index 00000000..7be4e6a2 --- /dev/null +++ b/resources/types/azure/kubernetes_node_pool.go @@ -0,0 +1,86 @@ +package azure_resources + +import ( + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/kubernetes_node_pool" + "github.com/multycloud/multy/resources/types" +) + +type AzureKubernetesNodePool struct { + *types.KubernetesNodePool +} + +func InitKubernetesNodePool(r *types.KubernetesNodePool) resources.ResourceTranslator[*resourcespb.KubernetesNodePoolResource] { + return AzureKubernetesNodePool{r} +} + +func (r AzureKubernetesNodePool) FromState(state *output.TfState) (*resourcespb.KubernetesNodePoolResource, error) { + return &resourcespb.KubernetesNodePoolResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + Name: r.Args.Name, + SubnetId: r.Args.SubnetId, + ClusterId: r.Args.ClusterId, + StartingNodeCount: r.Args.StartingNodeCount, + MinNodeCount: r.Args.MinNodeCount, + MaxNodeCount: r.Args.MaxNodeCount, + VmSize: r.Args.VmSize, + DiskSizeGb: r.Args.DiskSizeGb, + Labels: r.Args.Labels, + AwsOverride: r.Args.AwsOverride, + AzureOverride: r.Args.AzureOverride, + }, nil +} + +func (r AzureKubernetesNodePool) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { + pool, err := r.translateAzNodePool() + if err != nil { + return nil, err + } + return []output.TfBlock{ + pool, + }, nil +} + +func (r AzureKubernetesNodePool) translateAzNodePool() (*kubernetes_node_pool.AzureKubernetesNodePool, error) { + clusterId, err := resources.GetMainOutputId(AzureKubernetesCluster{r.KubernetesCluster}) + if err != nil { + return nil, err + } + subnetId, err := resources.GetMainOutputId(AzureSubnet{r.Subnet}) + if err != nil { + return nil, err + } + + var vmSize string + if r.Args.AzureOverride.GetVmSize() != "" { + vmSize = r.Args.AzureOverride.GetVmSize() + } else { + vmSize = common.VMSIZE[r.Args.VmSize][r.GetCloud()] + } + + return &kubernetes_node_pool.AzureKubernetesNodePool{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + Name: r.Args.Name, + }, + ClusterId: clusterId, + NodeCount: int(r.Args.StartingNodeCount), + MaxSize: int(r.Args.MaxNodeCount), + MinSize: int(r.Args.MinNodeCount), + Labels: r.Args.Labels, + EnableAutoScaling: true, + VmSize: vmSize, + VirtualNetworkSubnetId: subnetId, + }, nil +} + +func (r AzureKubernetesNodePool) GetMainResourceName() (string, error) { + return output.GetResourceName(kubernetes_node_pool.AzureKubernetesNodePool{}), nil +} diff --git a/resources/types/azure/network_interface.go b/resources/types/azure/network_interface.go new file mode 100644 index 00000000..fcb254c1 --- /dev/null +++ b/resources/types/azure/network_interface.go @@ -0,0 +1,78 @@ +package azure_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/network_interface" + "github.com/multycloud/multy/resources/types" +) + +type AzureNetworkInterface struct { + *types.NetworkInterface +} + +func InitNetworkInterface(r *types.NetworkInterface) resources.ResourceTranslator[*resourcespb.NetworkInterfaceResource] { + return AzureNetworkInterface{r} +} + +func (r AzureNetworkInterface) FromState(_ *output.TfState) (*resourcespb.NetworkInterfaceResource, error) { + return &resourcespb.NetworkInterfaceResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + SubnetId: r.Args.SubnetId, + PublicIpId: r.Args.PublicIpId, + }, nil +} + +func (r AzureNetworkInterface) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { + var pIpId string + subnetId, err := resources.GetMainOutputId(AzureSubnet{r.Subnet}) + if err != nil { + return nil, err + } + if r.PublicIp != nil { + pIpId, err = resources.GetMainOutputId(AzurePublicIp{r.PublicIp}) + if err != nil { + return nil, err + } + } + rgName := GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId) + nic := network_interface.AzureNetworkInterface{ + AzResource: common.NewAzResource( + r.ResourceId, r.Args.Name, rgName, + r.GetCloudSpecificLocation(), + ), + // by default, virtual_machine will have a private ip + IpConfigurations: []network_interface.AzureIpConfiguration{{ + Name: "internal", // this name shouldn't be vm.name + PrivateIpAddressAllocation: "Dynamic", + SubnetId: subnetId, + Primary: true, + }}, + } + // associate a public ip configuration in case a public_ip resource references this network_interface + if pIpId != "" { + nic.IpConfigurations = []network_interface.AzureIpConfiguration{{ + Name: fmt.Sprintf("external-%s", r.Args.Name), + PrivateIpAddressAllocation: "Dynamic", + PublicIpAddressId: pIpId, + SubnetId: subnetId, + Primary: true, + }} + } + return []output.TfBlock{nic}, nil +} + +func (r AzureNetworkInterface) GetMainResourceName() (string, error) { + return network_interface.AzureResourceName, nil +} diff --git a/resources/types/azure/network_interface_security_group_association.go b/resources/types/azure/network_interface_security_group_association.go new file mode 100644 index 00000000..5e8787ad --- /dev/null +++ b/resources/types/azure/network_interface_security_group_association.go @@ -0,0 +1,52 @@ +package azure_resources + +import ( + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/network_interface_security_group_association" + "github.com/multycloud/multy/resources/types" +) + +type AzureNetworkInterfaceSecurityGroupAssociation struct { + *types.NetworkInterfaceSecurityGroupAssociation +} + +func InitNetworkInterfaceSecurityGroupAssociation(vn *types.NetworkInterfaceSecurityGroupAssociation) resources.ResourceTranslator[*resourcespb.NetworkInterfaceSecurityGroupAssociationResource] { + return AzureNetworkInterfaceSecurityGroupAssociation{vn} +} + +func (r AzureNetworkInterfaceSecurityGroupAssociation) FromState(state *output.TfState) (*resourcespb.NetworkInterfaceSecurityGroupAssociationResource, error) { + return &resourcespb.NetworkInterfaceSecurityGroupAssociationResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + NetworkInterfaceId: r.Args.NetworkInterfaceId, + SecurityGroupId: r.Args.SecurityGroupId, + }, nil +} + +func (r AzureNetworkInterfaceSecurityGroupAssociation) Translate(resources.MultyContext) ([]output.TfBlock, error) { + nicId, err := resources.GetMainOutputId(AzureNetworkInterface{r.NetworkInterface}) + if err != nil { + return nil, err + } + nsgId, err := resources.GetMainOutputId(AzureNetworkSecurityGroup{r.NetworkSecurityGroup}) + if err != nil { + return nil, err + } + return []output.TfBlock{network_interface_security_group_association.AzureNetworkInterfaceSecurityGroupAssociation{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + }, + NetworkInterfaceId: nicId, + SecurityGroupId: nsgId, + }}, nil +} + +func (r AzureNetworkInterfaceSecurityGroupAssociation) GetMainResourceName() (string, error) { + return network_interface_security_group_association.AzureResourceName, nil +} diff --git a/resources/types/azure/network_security_group.go b/resources/types/azure/network_security_group.go new file mode 100644 index 00000000..836b0bcc --- /dev/null +++ b/resources/types/azure/network_security_group.go @@ -0,0 +1,122 @@ +package azure_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/network_security_group" + "github.com/multycloud/multy/resources/types" + "strconv" + "strings" +) + +type AzureNetworkSecurityGroup struct { + *types.NetworkSecurityGroup +} + +func InitNetworkSecurityGroup(r *types.NetworkSecurityGroup) resources.ResourceTranslator[*resourcespb.NetworkSecurityGroupResource] { + return AzureNetworkSecurityGroup{r} +} + +func (r AzureNetworkSecurityGroup) FromState(_ *output.TfState) (*resourcespb.NetworkSecurityGroupResource, error) { + return &resourcespb.NetworkSecurityGroupResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + VirtualNetworkId: r.Args.VirtualNetworkId, + Rules: r.Args.Rules, + }, nil +} + +func (r AzureNetworkSecurityGroup) Translate(_ resources.MultyContext) ([]output.TfBlock, error) { + return []output.TfBlock{ + network_security_group.AzureNsg{ + AzResource: common.NewAzResource( + r.ResourceId, r.Args.Name, GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId), + r.GetCloudSpecificLocation(), + ), + Rules: translateAzNsgRules(r.Args.Rules), + }, + }, nil +} + +func translateAzNsgRules(rules []*resourcespb.NetworkSecurityRule) []network_security_group.AzureRule { + m := map[resourcespb.Direction]string{ + resourcespb.Direction_INGRESS: "Inbound", + resourcespb.Direction_EGRESS: "Outbound", + } + + var rls []network_security_group.AzureRule + + for _, rule := range rules { + protocol := strings.Title(strings.ToLower(rule.Protocol)) + if rule.Direction == resourcespb.Direction_BOTH_DIRECTIONS { + rls = append( + rls, network_security_group.AzureRule{ + Name: strconv.Itoa(len(rls)), + Protocol: protocol, + Priority: int(rule.Priority), + Access: "Allow", + SourcePortRange: "*", + SourceAddressPrefix: "*", + DestinationPortRange: translatePortRange(rule.PortRange), + DestinationAddressPrefix: "*", + Direction: m[resourcespb.Direction_INGRESS], + }, + ) + rls = append( + rls, network_security_group.AzureRule{ + Name: strconv.Itoa(len(rls)), + Protocol: protocol, + Priority: int(rule.Priority), + Access: "Allow", + SourcePortRange: "*", + SourceAddressPrefix: "*", + DestinationPortRange: translatePortRange(rule.PortRange), + DestinationAddressPrefix: "*", + Direction: m[resourcespb.Direction_EGRESS], + }, + ) + } else { + rls = append( + rls, network_security_group.AzureRule{ + Name: strconv.Itoa(len(rls)), + Protocol: protocol, + Priority: int(rule.Priority), + Access: "Allow", + SourcePortRange: "*", + SourceAddressPrefix: "*", + DestinationPortRange: translatePortRange(rule.PortRange), + DestinationAddressPrefix: "*", + Direction: m[rule.Direction], + }, + ) + } + } + + return rls +} + +func translatePortRange(pr *resourcespb.PortRange) string { + from := "*" + if pr.From != 0 { + from = strconv.Itoa(int(pr.From)) + } + to := "*" + if pr.To != 0 { + to = strconv.Itoa(int(pr.To)) + } + return fmt.Sprintf("%s-%s", from, to) +} + +func (r AzureNetworkSecurityGroup) GetMainResourceName() (string, error) { + return network_security_group.AzureNetworkSecurityGroupResourceName, nil +} diff --git a/resources/types/azure/object_storage.go b/resources/types/azure/object_storage.go new file mode 100644 index 00000000..877a884b --- /dev/null +++ b/resources/types/azure/object_storage.go @@ -0,0 +1,78 @@ +package azure_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/object_storage" + "github.com/multycloud/multy/resources/output/object_storage_object" + "github.com/multycloud/multy/resources/types" +) + +type AzureObjectStorage struct { + *types.ObjectStorage +} + +func InitObjectStorage(vn *types.ObjectStorage) resources.ResourceTranslator[*resourcespb.ObjectStorageResource] { + return AzureObjectStorage{vn} +} + +func (r AzureObjectStorage) FromState(state *output.TfState) (*resourcespb.ObjectStorageResource, error) { + return &resourcespb.ObjectStorageResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + Versioning: r.Args.Versioning, + }, nil +} + +func (r AzureObjectStorage) Translate(resources.MultyContext) ([]output.TfBlock, error) { + rgName := GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId) + + storageAccount := object_storage.AzureStorageAccount{ + AzResource: common.NewAzResource( + r.ResourceId, common.RemoveSpecialChars(r.Args.Name), rgName, + r.GetCloudSpecificLocation(), + ), + AccountTier: "Standard", + AccountReplicationType: "GZRS", + AllowNestedItemsToBePublic: true, + BlobProperties: object_storage.BlobProperties{ + VersioningEnabled: r.Args.Versioning, + }, + } + + return []output.TfBlock{ + storageAccount, + object_storage_object.AzureStorageContainer{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ + ResourceId: fmt.Sprintf("%s_%s", r.ResourceId, "public"), + }, + Name: "public", + }, + StorageAccountName: storageAccount.GetResourceName(), + ContainerAccessType: "blob", + }, object_storage_object.AzureStorageContainer{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ + ResourceId: fmt.Sprintf("%s_%s", r.ResourceId, "private"), + }, + Name: "private", + }, + StorageAccountName: storageAccount.GetResourceName(), + ContainerAccessType: "private", + }}, nil +} + +func (r AzureObjectStorage) GetMainResourceName() (string, error) { + return object_storage.AzureResourceName, nil +} diff --git a/resources/types/azure/object_storage_object.go b/resources/types/azure/object_storage_object.go new file mode 100644 index 00000000..41b859b7 --- /dev/null +++ b/resources/types/azure/object_storage_object.go @@ -0,0 +1,83 @@ +package azure_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/object_storage" + "github.com/multycloud/multy/resources/output/object_storage_object" + "github.com/multycloud/multy/resources/output/terraform" + "github.com/multycloud/multy/resources/types" +) + +type AzureObjectStorageObject struct { + *types.ObjectStorageObject +} + +func InitObjectStorageObject(vn *types.ObjectStorageObject) resources.ResourceTranslator[*resourcespb.ObjectStorageObjectResource] { + return AzureObjectStorageObject{vn} +} + +func (r AzureObjectStorageObject) FromState(state *output.TfState) (*resourcespb.ObjectStorageObjectResource, error) { + out := new(resourcespb.ObjectStorageObjectResource) + out.CommonParameters = &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + } + + id, err := resources.GetMainOutputRef(AzureObjectStorage{r.Parent}) + if err != nil { + return nil, err + } + + out.Name = r.Args.Name + out.ContentBase64 = r.Args.ContentBase64 + out.ContentType = r.Args.ContentType + out.ObjectStorageId = r.Args.ObjectStorageId + out.Acl = r.Args.Acl + out.Source = r.Args.Source + + if !flags.DryRun { + stateResource, err := output.GetParsed[object_storage.AzureStorageAccount](state, id) + if err != nil { + return nil, err + } + out.Url = fmt.Sprintf("https://%s.blob.core.windows.net/public/%s", stateResource.AzResource.Name, r.Args.Name) + + } else { + out.Url = "dryrun" + } + + return out, nil +} + +func (r AzureObjectStorageObject) Translate(resources.MultyContext) ([]output.TfBlock, error) { + var containerName string + if r.Args.Acl == resourcespb.ObjectStorageObjectAcl_PUBLIC_READ { + containerName = fmt.Sprintf("azurerm_storage_container.%s_public.name", r.ObjectStorage.ResourceId) + } else { + containerName = fmt.Sprintf("azurerm_storage_container.%s_private.name", r.ObjectStorage.ResourceId) + } + contentFile := terraform.NewLocalFile(r.ResourceId, r.Args.ContentBase64) + return []output.TfBlock{ + contentFile, + object_storage_object.AzureStorageAccountBlob{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + Name: r.Args.Name, + }, + StorageAccountName: fmt.Sprintf("azurerm_storage_account.%s.name", r.ObjectStorage.ResourceId), + StorageContainerName: containerName, + Type: "Block", + Source: contentFile.GetFilename(), + ContentType: r.Args.ContentType, + }}, nil +} + +func (r AzureObjectStorageObject) GetMainResourceName() (string, error) { + return "azurerm_storage_blob", nil +} diff --git a/resources/types/azure/public_ip.go b/resources/types/azure/public_ip.go new file mode 100644 index 00000000..cc5a331d --- /dev/null +++ b/resources/types/azure/public_ip.go @@ -0,0 +1,60 @@ +package azure_resources + +import ( + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/public_ip" + "github.com/multycloud/multy/resources/types" +) + +type AzurePublicIp struct { + *types.PublicIp +} + +func InitPublicIp(r *types.PublicIp) resources.ResourceTranslator[*resourcespb.PublicIpResource] { + return AzurePublicIp{r} +} + +func (r AzurePublicIp) FromState(state *output.TfState) (*resourcespb.PublicIpResource, error) { + ip := "dryrun" + if !flags.DryRun { + values, err := state.GetValues(public_ip.AzurePublicIp{}, r.ResourceId) + if err != nil { + return nil, err + } + ip = values["ip_address"].(string) + } + + return &resourcespb.PublicIpResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + + Ip: ip, + }, nil +} + +func (r AzurePublicIp) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { + return []output.TfBlock{ + public_ip.AzurePublicIp{ + AzResource: common.NewAzResource( + r.ResourceId, r.Args.Name, GetResourceGroupName(r.Args.GetCommonParameters().ResourceGroupId), + r.GetCloudSpecificLocation(), + ), + AllocationMethod: "Static", + }, + }, nil +} + +func (r AzurePublicIp) GetMainResourceName() (string, error) { + return public_ip.AzureResourceName, nil +} diff --git a/resources/types/azure/resource_group.go b/resources/types/azure/resource_group.go new file mode 100644 index 00000000..920e4d9c --- /dev/null +++ b/resources/types/azure/resource_group.go @@ -0,0 +1,62 @@ +package azure_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/types" +) + +type ResourceGroup struct { + *types.ResourceGroup +} + +func InitResourceGroup(rg *types.ResourceGroup) resources.ResourceTranslator[*resourcespb.ResourceGroupResource] { + return ResourceGroup{rg} +} + +type AzureResourceGroup struct { + *common.AzResource `hcl:",squash" default:"name=azurerm_resource_group"` + Location string `hcl:"location"` +} + +func (rg ResourceGroup) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { + allDeps := rg.GetAllDependentResources(ctx.Resources) + if len(allDeps) == 0 { + // if no resources are in this group, just don't output anything + return nil, nil + } + for _, r := range allDeps { + ctx.Resources.AddDependency(rg.ResourceId, r) + } + + return []output.TfBlock{AzureResourceGroup{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ResourceId: rg.ResourceId}, + Name: rg.Args.Name, + }, + Location: rg.GetCloudSpecificLocation(), + }}, nil + +} + +func (rg ResourceGroup) FromState(_ *output.TfState) (*resourcespb.ResourceGroupResource, error) { + return &resourcespb.ResourceGroupResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: rg.ResourceId, + Location: rg.Args.CommonParameters.Location, + CloudProvider: rg.Args.CommonParameters.CloudProvider, + }, + }, nil +} + +func (rg ResourceGroup) GetMainResourceName() (string, error) { + return "azurerm_resource_group", nil +} + +func GetResourceGroupName(name string) string { + return fmt.Sprintf("azurerm_resource_group.%s.name", name) +} diff --git a/resources/types/azure/route_table.go b/resources/types/azure/route_table.go new file mode 100644 index 00000000..4700e5af --- /dev/null +++ b/resources/types/azure/route_table.go @@ -0,0 +1,87 @@ +package azure_resources + +import ( + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/route_table" + "github.com/multycloud/multy/resources/types" +) + +type AzureRouteTable struct { + *types.RouteTable +} + +func InitRouteTable(vn *types.RouteTable) resources.ResourceTranslator[*resourcespb.RouteTableResource] { + return AzureRouteTable{vn} +} + +func (r AzureRouteTable) FromState(state *output.TfState) (*resourcespb.RouteTableResource, error) { + if flags.DryRun { + return &resourcespb.RouteTableResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + Name: r.Args.Name, + VirtualNetworkId: r.Args.VirtualNetworkId, + Routes: r.Args.Routes, + }, nil + } + out := new(resourcespb.RouteTableResource) + out.CommonParameters = &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + } + id, err := resources.GetMainOutputRef(r) + if err != nil { + return nil, err + } + stateResource, err := output.GetParsed[route_table.AzureRouteTable](state, id) + if err != nil { + return nil, err + } + out.Name = stateResource.Name + out.VirtualNetworkId = r.Args.VirtualNetworkId + var routes []*resourcespb.Route + for _, r := range stateResource.Routes { + route := &resourcespb.Route{ + CidrBlock: r.AddressPrefix, + Destination: resourcespb.RouteDestination_INTERNET, + } + routes = append(routes, route) + } + out.Routes = routes + return out, nil +} + +func (r AzureRouteTable) Translate(resources.MultyContext) ([]output.TfBlock, error) { + rt := route_table.AzureRouteTable{ + AzResource: common.NewAzResource( + r.ResourceId, r.Args.Name, GetResourceGroupName(r.VirtualNetwork.Args.GetCommonParameters().ResourceGroupId), + r.GetCloudSpecificLocation(), + ), + } + + var routes []route_table.AzureRouteTableRoute + for _, route := range r.Args.Routes { + if route.Destination == resourcespb.RouteDestination_INTERNET { + routes = append( + routes, route_table.AzureRouteTableRoute{ + Name: "internet", + AddressPrefix: route.CidrBlock, + NextHopType: "Internet", + }, + ) + } + } + rt.Routes = routes + return []output.TfBlock{rt}, nil +} + +func (r AzureRouteTable) GetMainResourceName() (string, error) { + return route_table.AzureResourceName, nil +} diff --git a/resources/types/azure/route_table_association.go b/resources/types/azure/route_table_association.go new file mode 100644 index 00000000..f960f54f --- /dev/null +++ b/resources/types/azure/route_table_association.go @@ -0,0 +1,57 @@ +package azure_resources + +import ( + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/route_table_association" + "github.com/multycloud/multy/resources/types" +) + +type AzureRouteTableAssociation struct { + *types.RouteTableAssociation +} + +func InitRouteTableAssociation(vn *types.RouteTableAssociation) resources.ResourceTranslator[*resourcespb.RouteTableAssociationResource] { + return AzureRouteTableAssociation{vn} +} + +func (r AzureRouteTableAssociation) FromState(state *output.TfState) (*resourcespb.RouteTableAssociationResource, error) { + return &resourcespb.RouteTableAssociationResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + SubnetId: r.Args.SubnetId, + RouteTableId: r.Args.RouteTableId, + }, nil +} + +func (r AzureRouteTableAssociation) Translate(resources.MultyContext) ([]output.TfBlock, error) { + rtId, err := resources.GetMainOutputId(AzureRouteTable{r.RouteTable}) + if err != nil { + return nil, err + } + subnetId, err := resources.GetMainOutputId(AzureSubnet{r.Subnet}) + if err != nil { + return nil, err + } + return []output.TfBlock{ + route_table_association.AzureRouteTableAssociation{ + // Here we use the subnet id so that it is the same as the one that is created by default in the subnet. + // This ensures that if a RTA is created after the default RTA is created, they will have the same ID and + // terraform will either update it in place or destroy it before creating it. + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ResourceId: r.Subnet.ResourceId}, + }, + RouteTableId: rtId, + SubnetId: subnetId, + }, + }, nil +} + +func (r AzureRouteTableAssociation) GetMainResourceName() (string, error) { + return route_table_association.AzureResourceName, nil +} diff --git a/resources/types/azure/subnet.go b/resources/types/azure/subnet.go new file mode 100644 index 00000000..4074889e --- /dev/null +++ b/resources/types/azure/subnet.go @@ -0,0 +1,112 @@ +package azure_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/route_table_association" + "github.com/multycloud/multy/resources/output/subnet" + "github.com/multycloud/multy/resources/output/virtual_network" + "github.com/multycloud/multy/resources/types" + "github.com/multycloud/multy/util" +) + +type AzureSubnet struct { + *types.Subnet +} + +func InitSubnet(r *types.Subnet) resources.ResourceTranslator[*resourcespb.SubnetResource] { + return AzureSubnet{r} +} + +func (r AzureSubnet) FromState(state *output.TfState) (*resourcespb.SubnetResource, error) { + if flags.DryRun { + return &resourcespb.SubnetResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + Name: r.Args.Name, + CidrBlock: r.Args.CidrBlock, + AvailabilityZone: r.Args.AvailabilityZone, + VirtualNetworkId: r.Args.VirtualNetworkId, + }, nil + } + out := new(resourcespb.SubnetResource) + out.CommonParameters = &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + } + out.AvailabilityZone = r.Args.AvailabilityZone + out.VirtualNetworkId = r.Args.GetVirtualNetworkId() + + id, err := resources.GetMainOutputRef(r) + if err != nil { + return nil, err + } + stateResource, err := output.GetParsed[subnet.AzureSubnet](state, id) + if err != nil { + return nil, err + } + out.Name = stateResource.Name + out.CidrBlock = stateResource.AddressPrefixes[0] + + return out, nil +} + +func (r AzureSubnet) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { + var azResources []output.TfBlock + azSubnet := subnet.AzureSubnet{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + Name: r.Args.Name, + ResourceGroupName: GetResourceGroupName(r.VirtualNetwork.Args.GetCommonParameters().GetResourceGroupId()), + }, + AddressPrefixes: []string{r.Args.CidrBlock}, + VirtualNetworkName: fmt.Sprintf("%s.%s.name", virtual_network.AzureResourceName, r.VirtualNetwork.ResourceId), + } + azSubnet.ServiceEndpoints = getServiceEndpointSubnetReferences(ctx, r.Subnet) + azResources = append(azResources, azSubnet) + + // there must be a better way to do this + if !checkSubnetRouteTableAssociated(ctx, r.Subnet) { + subnetId, err := resources.GetMainOutputId(r) + if err != nil { + return nil, err + } + rtAssociation := route_table_association.AzureRouteTableAssociation{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + }, + SubnetId: subnetId, + RouteTableId: AzureVirtualNetwork{r.VirtualNetwork}.GetAssociatedRouteTableId(), + } + azResources = append(azResources, rtAssociation) + } + + return azResources, nil +} + +func (r AzureSubnet) GetMainResourceName() (string, error) { + return subnet.AzureResourceName, nil +} + +func getServiceEndpointSubnetReferences(ctx resources.MultyContext, r *types.Subnet) []string { + const ( + DATABASE = "Microsoft.Sql" + ) + + serviceEndpoints := map[string]bool{} + if len(resources.GetAllResourcesWithListRef(ctx, func(db *types.Database) []*types.Subnet { return db.Subnets }, r)) > 0 { + serviceEndpoints[DATABASE] = true + } + return util.SortedKeys(serviceEndpoints) +} + +func checkSubnetRouteTableAssociated(ctx resources.MultyContext, r *types.Subnet) bool { + return len(resources.GetAllResourcesWithRef(ctx, func(rt *types.RouteTableAssociation) *types.Subnet { return rt.Subnet }, r)) > 0 +} diff --git a/resources/types/azure/vault.go b/resources/types/azure/vault.go new file mode 100644 index 00000000..d6ca0c13 --- /dev/null +++ b/resources/types/azure/vault.go @@ -0,0 +1,69 @@ +package azure_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/vault" + "github.com/multycloud/multy/resources/types" +) + +type AzureVault struct { + *types.Vault +} + +func InitVault(vn *types.Vault) resources.ResourceTranslator[*resourcespb.VaultResource] { + return AzureVault{vn} +} + +type AzureClientConfig struct { + *output.TerraformDataSource `hcl:",squash" default:"name=azurerm_client_config"` +} + +func (r AzureVault) FromState(state *output.TfState) (*resourcespb.VaultResource, error) { + return &resourcespb.VaultResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + }, nil +} + +func (r AzureVault) Translate(resources.MultyContext) ([]output.TfBlock, error) { + return []output.TfBlock{ + AzureClientConfig{TerraformDataSource: &output.TerraformDataSource{ResourceId: r.ResourceId}}, + vault.AzureKeyVault{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + Name: r.Args.Name, + ResourceGroupName: GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId), + Location: r.GetCloudSpecificLocation(), + }, + Sku: "standard", + TenantId: fmt.Sprintf("data.azurerm_client_config.%s.tenant_id", r.ResourceId), + AccessPolicy: []vault.AzureKeyVaultAccessPolicyInline{{ + TenantId: fmt.Sprintf( + "data.azurerm_client_config.%s.tenant_id", r.ResourceId, + ), + ObjectId: fmt.Sprintf( + "data.azurerm_client_config.%s.object_id", r.ResourceId, + ), + AzureKeyVaultPermissions: &vault.AzureKeyVaultPermissions{ + CertificatePermissions: []string{}, + KeyPermissions: []string{}, + SecretPermissions: []string{"List", "Get", "Set", "Delete", "Recover", "Backup", "Restore", "Purge"}, + }, + }}, + }}, nil +} + +func (r AzureVault) GetMainResourceName() (string, error) { + return vault.AzureResourceName, nil +} diff --git a/resources/types/azure/vault_access_policy.go b/resources/types/azure/vault_access_policy.go new file mode 100644 index 00000000..ccc1deab --- /dev/null +++ b/resources/types/azure/vault_access_policy.go @@ -0,0 +1,82 @@ +package azure_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/vault" + "github.com/multycloud/multy/resources/output/vault_access_policy" + "github.com/multycloud/multy/resources/types" +) + +type AzureVaultAccessPolicy struct { + *types.VaultAccessPolicy +} + +func InitVaultAccessPolicy(vn *types.VaultAccessPolicy) resources.ResourceTranslator[*resourcespb.VaultAccessPolicyResource] { + return AzureVaultAccessPolicy{vn} +} + +func (r AzureVaultAccessPolicy) FromState(state *output.TfState) (*resourcespb.VaultAccessPolicyResource, error) { + return &resourcespb.VaultAccessPolicyResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + VaultId: r.Args.VaultId, + Identity: r.Args.Identity, + Access: r.Args.Access, + }, nil +} + +func (r AzureVaultAccessPolicy) Translate(resources.MultyContext) ([]output.TfBlock, error) { + return []output.TfBlock{ + AzureClientConfig{TerraformDataSource: &output.TerraformDataSource{ResourceId: r.ResourceId}}, + vault_access_policy.AzureKeyVaultAccessPolicy{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + }, + KeyVaultId: fmt.Sprintf("%s.%s.id", vault.AzureResourceName, r.Vault.ResourceId), + AzureKeyVaultAccessPolicyInline: &vault.AzureKeyVaultAccessPolicyInline{ + TenantId: fmt.Sprintf( + "data.azurerm_client_config.%s.tenant_id", r.ResourceId, + ), + ObjectId: "\"" + r.Args.Identity + "\"", + // fixme + AzureKeyVaultPermissions: r.GetAccessPolicyRules(), + }, + }, + }, nil +} + +func (r AzureVaultAccessPolicy) GetAccessPolicyRules() *vault.AzureKeyVaultPermissions { + switch r.Args.Access { + case resourcespb.VaultAccess_READ: + return &vault.AzureKeyVaultPermissions{ + CertificatePermissions: []string{}, + KeyPermissions: []string{}, + SecretPermissions: []string{"List", "Get"}, + } + case resourcespb.VaultAccess_WRITE: + return &vault.AzureKeyVaultPermissions{ + CertificatePermissions: []string{}, + KeyPermissions: []string{}, + SecretPermissions: []string{"Set", "Delete"}, + } + case resourcespb.VaultAccess_OWNER: + return &vault.AzureKeyVaultPermissions{ + CertificatePermissions: []string{}, + KeyPermissions: []string{}, + SecretPermissions: []string{"List", "Get", "Set", "Delete", "Recover", "Backup", "Restore", "Purge"}, + } + default: + return nil + } +} + +func (r AzureVaultAccessPolicy) GetMainResourceName() (string, error) { + return output.GetResourceName(vault_access_policy.AzureKeyVaultAccessPolicy{}), nil +} diff --git a/resources/types/azure/vault_secret.go b/resources/types/azure/vault_secret.go new file mode 100644 index 00000000..75491bb6 --- /dev/null +++ b/resources/types/azure/vault_secret.go @@ -0,0 +1,50 @@ +package azure_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/vault" + "github.com/multycloud/multy/resources/output/vault_secret" + "github.com/multycloud/multy/resources/types" +) + +type AzureVaultSecret struct { + *types.VaultSecret +} + +func InitVaultSecret(vn *types.VaultSecret) resources.ResourceTranslator[*resourcespb.VaultSecretResource] { + return AzureVaultSecret{vn} +} + +func (r AzureVaultSecret) FromState(state *output.TfState) (*resourcespb.VaultSecretResource, error) { + return &resourcespb.VaultSecretResource{ + CommonParameters: &commonpb.CommonChildResourceParameters{ + ResourceId: r.ResourceId, + NeedsUpdate: false, + }, + Name: r.Args.Name, + Value: r.Args.Value, + VaultId: r.Args.VaultId, + }, nil +} + +func (r AzureVaultSecret) Translate(resources.MultyContext) ([]output.TfBlock, error) { + return []output.TfBlock{ + vault_secret.AzureKeyVaultSecret{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + Name: r.Args.Name, + }, + KeyVaultId: fmt.Sprintf("%s.%s.id", vault.AzureResourceName, r.Vault.ResourceId), + Value: r.Args.Value, + }, + }, nil +} + +func (r AzureVaultSecret) GetMainResourceName() (string, error) { + return vault_secret.AzureResourceName, nil +} diff --git a/resources/types/azure/virtual_machine.go b/resources/types/azure/virtual_machine.go new file mode 100644 index 00000000..2ee4cbd5 --- /dev/null +++ b/resources/types/azure/virtual_machine.go @@ -0,0 +1,236 @@ +package azure_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/network_interface" + "github.com/multycloud/multy/resources/output/network_security_group" + "github.com/multycloud/multy/resources/output/public_ip" + "github.com/multycloud/multy/resources/output/terraform" + "github.com/multycloud/multy/resources/output/virtual_machine" + "github.com/multycloud/multy/resources/types" + "github.com/multycloud/multy/util" + "log" + "regexp" +) + +type AzureVirtualMachine struct { + *types.VirtualMachine +} + +func InitVirtualMachine(vn *types.VirtualMachine) resources.ResourceTranslator[*resourcespb.VirtualMachineResource] { + return AzureVirtualMachine{vn} +} + +func (r AzureVirtualMachine) FromState(state *output.TfState) (*resourcespb.VirtualMachineResource, error) { + var ip string + identityId := "dryrun" + if r.Args.GeneratePublicIp { + ip = "dryrun" + } + + if !flags.DryRun { + if r.Args.GeneratePublicIp { + values, err := state.GetValues(public_ip.AzurePublicIp{}, r.ResourceId) + if err != nil { + return nil, err + } + ip = values["public_ip"].(string) + } + values, err := state.GetValues(virtual_machine.AzureVirtualMachine{}, r.ResourceId) + if err != nil { + return nil, err + } + identityId = values["identity"].([]interface{})[0].(map[string]interface{})["principal_id"].(string) + } + + // TODO: handle default values on create + if r.Args.ImageReference == nil { + r.Args.ImageReference = &resourcespb.ImageReference{ + Os: resourcespb.ImageReference_UBUNTU, + Version: "16.04", + } + } + + return &resourcespb.VirtualMachineResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + NetworkInterfaceIds: r.Args.NetworkInterfaceIds, + NetworkSecurityGroupIds: r.Args.NetworkSecurityGroupIds, + VmSize: r.Args.VmSize, + UserDataBase64: r.Args.UserDataBase64, + SubnetId: r.Args.SubnetId, + PublicSshKey: r.Args.PublicSshKey, + PublicIpId: r.Args.PublicIpId, + GeneratePublicIp: r.Args.GeneratePublicIp, + ImageReference: r.Args.ImageReference, + AwsOverride: r.Args.AwsOverride, + AzureOverride: r.Args.AzureOverride, + + PublicIp: ip, + IdentityId: identityId, + }, nil +} + +func (r AzureVirtualMachine) Translate(resources.MultyContext) ([]output.TfBlock, error) { + subnetId, err := resources.GetMainOutputId(AzureSubnet{r.Subnet}) + if err != nil { + return nil, err + } + // TODO validate that NIC is on the same VNET + var azResources []output.TfBlock + rgName := GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId) + nicIds, err := util.MapSliceValuesErr(r.NetworkInterface, func(v *types.NetworkInterface) (string, error) { + return resources.GetMainOutputId(AzureNetworkInterface{v}) + }) + if err != nil { + return nil, err + } + + if len(r.NetworkInterface) == 0 { + nic := network_interface.AzureNetworkInterface{ + AzResource: common.NewAzResource( + r.ResourceId, r.Args.Name, rgName, + r.GetCloudSpecificLocation(), + ), + IpConfigurations: []network_interface.AzureIpConfiguration{{ + Name: "internal", // this name shouldn't be r.name + PrivateIpAddressAllocation: "Dynamic", + SubnetId: subnetId, + Primary: true, + }}, + } + + if r.Args.GeneratePublicIp { + pIp := public_ip.AzurePublicIp{ + AzResource: common.NewAzResource( + r.ResourceId, r.Args.Name, rgName, + r.GetCloudSpecificLocation(), + ), + AllocationMethod: "Static", + } + nic.IpConfigurations = []network_interface.AzureIpConfiguration{{ + Name: "external", // this name shouldn't be r.name + PrivateIpAddressAllocation: "Dynamic", + SubnetId: subnetId, + PublicIpAddressId: pIp.GetId(), + Primary: true, + }} + azResources = append(azResources, &pIp) + } + azResources = append(azResources, nic) + nicIds = append(nicIds, fmt.Sprintf("%s.%s.id", output.GetResourceName(nic), nic.ResourceId)) + } + + // TODO change this to multy nsg_nic_attachment resource and use aws_network_interface_sg_attachment + if len(r.NetworkSecurityGroups) != 0 { + for _, nsg := range r.NetworkSecurityGroups { + for _, nicId := range nicIds { + nsgId, err := resources.GetMainOutputId(AzureNetworkSecurityGroup{nsg}) + if err != nil { + return nil, err + } + azResources = append( + azResources, network_security_group.AzureNetworkInterfaceSecurityGroupAssociation{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ + ResourceId: r.ResourceId, + }, + }, + NetworkInterfaceId: nicId, + NetworkSecurityGroupId: nsgId, + }, + ) + } + } + } + + // if ssh key is specified, add admin_ssh param + // ssh authentication will replace password authentication + // if no ssh key is passed, password is required + // random_password will be used + var azureSshKey virtual_machine.AzureAdminSshKey + var vmPassword string + disablePassAuth := false + if r.Args.PublicSshKey != "" { + azureSshKey = virtual_machine.AzureAdminSshKey{ + Username: "adminuser", + PublicKey: r.Args.PublicSshKey, + } + disablePassAuth = true + } else { + randomPassword := terraform.RandomPassword{ + TerraformResource: &output.TerraformResource{ + ResourceId: r.ResourceId, + }, + Length: 16, + Special: true, + Upper: true, + Lower: true, + Number: true, + } + vmPassword = randomPassword.GetResult() + azResources = append(azResources, randomPassword) + } + + reg, err := regexp.Compile("[^a-zA-Z0-9]+") + if err != nil { + log.Fatal(err) + } + computerName := reg.ReplaceAllString(r.Args.Name, "") + + sourceImg, err := virtual_machine.GetLatestAzureSourceImageReference(r.Args.ImageReference) + if err != nil { + return nil, err + } + + var vmSize string + if r.Args.AzureOverride.GetSize() != "" { + vmSize = r.Args.AzureOverride.GetSize() + } else { + vmSize = common.VMSIZE[r.Args.VmSize][r.GetCloud()] + } + + azResources = append( + azResources, virtual_machine.AzureVirtualMachine{ + AzResource: &common.AzResource{ + TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, + ResourceGroupName: rgName, + Name: r.Args.Name, + }, + Location: r.GetCloudSpecificLocation(), + Size: vmSize, + NetworkInterfaceIds: nicIds, + CustomData: r.Args.UserDataBase64, + OsDisk: virtual_machine.AzureOsDisk{ + Caching: "None", + StorageAccountType: "Standard_LRS", + }, + AdminUsername: "adminuser", + AdminPassword: vmPassword, + AdminSshKey: azureSshKey, + SourceImageReference: sourceImg, + DisablePasswordAuthentication: disablePassAuth, + Identity: virtual_machine.AzureIdentity{Type: "SystemAssigned"}, + ComputerName: computerName, + }, + ) + + return azResources, nil + +} + +func (r AzureVirtualMachine) GetMainResourceName() (string, error) { + return virtual_machine.AzureResourceName, nil +} diff --git a/resources/types/azure/virtual_network.go b/resources/types/azure/virtual_network.go new file mode 100644 index 00000000..74c123c5 --- /dev/null +++ b/resources/types/azure/virtual_network.go @@ -0,0 +1,88 @@ +package azure_resources + +import ( + "fmt" + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/flags" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/common" + "github.com/multycloud/multy/resources/output" + "github.com/multycloud/multy/resources/output/route_table" + "github.com/multycloud/multy/resources/output/virtual_network" + "github.com/multycloud/multy/resources/types" +) + +type AzureVirtualNetwork struct { + *types.VirtualNetwork +} + +func InitVirtualNetwork(vn *types.VirtualNetwork) resources.ResourceTranslator[*resourcespb.VirtualNetworkResource] { + return AzureVirtualNetwork{vn} +} + +func (r AzureVirtualNetwork) FromState(state *output.TfState) (*resourcespb.VirtualNetworkResource, error) { + if flags.DryRun { + return &resourcespb.VirtualNetworkResource{ + CommonParameters: &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + NeedsUpdate: false, + }, + Name: r.Args.Name, + CidrBlock: r.Args.CidrBlock, + }, nil + } + out := new(resourcespb.VirtualNetworkResource) + out.CommonParameters = &commonpb.CommonResourceParameters{ + ResourceId: r.ResourceId, + ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, + Location: r.Args.CommonParameters.Location, + CloudProvider: r.GetCloud(), + NeedsUpdate: false, + } + + id, err := resources.GetMainOutputRef(r) + if err != nil { + return nil, err + } + + stateResource, err := output.GetParsed[virtual_network.AzureVnet](state, id) + if err != nil { + return nil, err + } + out.Name = stateResource.Name + out.CidrBlock = stateResource.AddressSpace[0] + + return out, nil +} + +func (r AzureVirtualNetwork) Translate(resources.MultyContext) ([]output.TfBlock, error) { + return []output.TfBlock{virtual_network.AzureVnet{ + AzResource: common.NewAzResource( + r.ResourceId, r.Args.Name, GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId), + r.GetCloudSpecificLocation(), + ), + AddressSpace: []string{r.Args.CidrBlock}, + }, route_table.AzureRouteTable{ + AzResource: common.NewAzResource( + r.ResourceId, r.Args.Name, GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId), + r.GetCloudSpecificLocation(), + ), + Routes: []route_table.AzureRouteTableRoute{{ + Name: "local", + AddressPrefix: "0.0.0.0/0", + NextHopType: "VnetLocal", + }}, + }}, nil +} + +func (r AzureVirtualNetwork) GetMainResourceName() (string, error) { + return virtual_network.AzureResourceName, nil +} + +func (r AzureVirtualNetwork) GetAssociatedRouteTableId() string { + return fmt.Sprintf("%s.%s.id", route_table.AzureResourceName, r.ResourceId) +} diff --git a/resources/types/database.go b/resources/types/database.go index 4fa13f22..06617701 100644 --- a/resources/types/database.go +++ b/resources/types/database.go @@ -2,224 +2,63 @@ package types import ( "fmt" - "github.com/multycloud/multy/resources/output/network_security_group" - "strings" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" - "github.com/multycloud/multy/flags" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/database" "github.com/multycloud/multy/util" "github.com/multycloud/multy/validate" ) -var dbMetadata = resources.ResourceMetadata[*resourcespb.DatabaseArgs, *Database, *resourcespb.DatabaseResource]{ - CreateFunc: CreateDatabase, - UpdateFunc: UpdateDatabase, - ReadFromStateFunc: DatabaseFromState, - ExportFunc: func(r *Database, _ *resources.Resources) (*resourcespb.DatabaseArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewDatabase, - AbbreviatedName: "db", -} - type Database struct { resources.ResourceWithId[*resourcespb.DatabaseArgs] Subnets []*Subnet } -func (r *Database) GetMetadata() resources.ResourceMetadataInterface { - return &dbMetadata -} - -func NewDatabase(resourceId string, db *resourcespb.DatabaseArgs, others *resources.Resources) (*Database, error) { - subnets, err := util.MapSliceValuesErr(db.SubnetIds, func(subnetId string) (*Subnet, error) { - return resources.Get[*Subnet](resourceId, others, subnetId) - }) - if err != nil { - return nil, err - } - return &Database{ - ResourceWithId: resources.ResourceWithId[*resourcespb.DatabaseArgs]{ - ResourceId: resourceId, - Args: db, - }, - Subnets: subnets, - }, nil -} - -func CreateDatabase(resourceId string, args *resourcespb.DatabaseArgs, others *resources.Resources) (*Database, error) { +func (r *Database) Create(resourceId string, args *resourcespb.DatabaseArgs, others *resources.Resources) error { if args.CommonParameters.ResourceGroupId == "" { subnet, err := resources.Get[*Subnet](resourceId, others, args.SubnetIds[0]) if err != nil { - return nil, err + return err } rgId, err := NewRgFromParent("db", subnet.VirtualNetwork.Args.CommonParameters.ResourceGroupId, others, args.GetCommonParameters().GetLocation(), args.GetCommonParameters().GetCloudProvider()) if err != nil { - return nil, err + return err } args.CommonParameters.ResourceGroupId = rgId } - return NewDatabase(resourceId, args, others) + return NewDatabase(r, resourceId, args, others) } -func UpdateDatabase(resource *Database, vn *resourcespb.DatabaseArgs, others *resources.Resources) error { - resource.Args = vn +func (r *Database) Update(args *resourcespb.DatabaseArgs, _ *resources.Resources) error { + r.Args = args return nil } -func DatabaseFromState(resource *Database, state *output.TfState) (*resourcespb.DatabaseResource, error) { - var err error - host := "dyrun" - if !flags.DryRun { - host, err = getHost(resource.ResourceId, state, resource.Args.CommonParameters.CloudProvider, resource.Args.Engine) - if err != nil { - return nil, err - } - } - connectionUsername := resource.Args.Username - if resource.GetCloud() == commonpb.CloudProvider_AZURE { - connectionUsername = fmt.Sprintf("%s@%s", resource.Args.Username, host) - } - - return &resourcespb.DatabaseResource{ - CommonParameters: &commonpb.CommonResourceParameters{ - ResourceId: resource.ResourceId, - ResourceGroupId: resource.Args.CommonParameters.ResourceGroupId, - Location: resource.Args.CommonParameters.Location, - CloudProvider: resource.Args.CommonParameters.CloudProvider, - NeedsUpdate: false, - }, - Name: resource.Args.Name, - Engine: resource.Args.Engine, - EngineVersion: resource.Args.EngineVersion, - StorageGb: resource.Args.StorageGb, - Size: resource.Args.Size, - Username: resource.Args.Username, - Password: resource.Args.Password, - SubnetIds: resource.Args.SubnetIds, - Host: host, - ConnectionUsername: connectionUsername, - }, nil +func (r *Database) Import(resourceId string, args *resourcespb.DatabaseArgs, others *resources.Resources) error { + return NewDatabase(r, resourceId, args, others) } -func getHost(resourceId string, state *output.TfState, cloud commonpb.CloudProvider, engine resourcespb.DatabaseEngine) (string, error) { - switch cloud { - case commonpb.CloudProvider_AWS: - values, err := state.GetValues(database.AwsDbInstance{}, resourceId) - if err != nil { - return "", err - } - return values["address"].(string), nil - case commonpb.CloudProvider_AZURE: - var azureDatabaseEngine database.AzureDatabaseEngine - - if engine == resourcespb.DatabaseEngine_MYSQL { - azureDatabaseEngine = database.AzureMySqlServer{} - } else if engine == resourcespb.DatabaseEngine_POSTGRES { - azureDatabaseEngine = database.AzurePostgreSqlServer{} - } else if engine == resourcespb.DatabaseEngine_MARIADB { - azureDatabaseEngine = database.AzureMariaDbServer{} - } else { - return "", fmt.Errorf("unhandled engine %s", engine.String()) - } - - values, err := state.GetValues(azureDatabaseEngine, resourceId) - if err != nil { - return "", err - } - return values["fqdn"].(string), nil - } - - return "", fmt.Errorf("unknown cloud: %s", cloud.String()) +func (r *Database) Export(_ *resources.Resources) (*resourcespb.DatabaseArgs, bool, error) { + return r.Args, true, nil } -func (r *Database) Translate(resources.MultyContext) ([]output.TfBlock, error) { - subnetIds, err := util.MapSliceValuesErr(r.Subnets, func(v *Subnet) (string, error) { - return resources.GetMainOutputId(v) +func NewDatabase(r *Database, resourceId string, db *resourcespb.DatabaseArgs, others *resources.Resources) error { + subnets, err := util.MapSliceValuesErr(db.SubnetIds, func(subnetId string) (*Subnet, error) { + return resources.Get[*Subnet](resourceId, others, subnetId) }) if err != nil { - return nil, err + return err } - - // TODO validate subnet configuration (minimum 2 different AZs) - if r.GetCloud() == commonpb.CloudProvider_AWS { - vpcId, err := resources.GetMainOutputId(r.Subnets[0].VirtualNetwork) - if err != nil { - return nil, err - } - - name := common.RemoveSpecialChars(r.Args.Name) - dbSubnetGroup := database.AwsDbSubnetGroup{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - Name: r.Args.Name, - Description: "Managed by Multy", - SubnetIds: subnetIds, - } - nsg := network_security_group.AwsSecurityGroup{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - VpcId: vpcId, - Name: r.Args.Name, - Description: fmt.Sprintf("Default security group of %s", r.Args.Name), - Ingress: []network_security_group.AwsSecurityGroupRule{{ - Protocol: "-1", - FromPort: 0, - ToPort: 0, - CidrBlocks: []string{"0.0.0.0/0"}, - }}, - Egress: []network_security_group.AwsSecurityGroupRule{{ - Protocol: "-1", - FromPort: 0, - ToPort: 0, - CidrBlocks: []string{"0.0.0.0/0"}, - }}, - } - return []output.TfBlock{ - dbSubnetGroup, - nsg, - database.AwsDbInstance{ - AwsResource: common.NewAwsResource(r.ResourceId, name), - AllocatedStorage: int(r.Args.StorageGb), - Engine: strings.ToLower(r.Args.Engine.String()), - EngineVersion: r.Args.EngineVersion, - Username: r.Args.Username, - Password: r.Args.Password, - InstanceClass: common.DBSIZE[r.Args.Size][r.GetCloud()], - Identifier: r.Args.Name, - SkipFinalSnapshot: true, - DbSubnetGroupName: dbSubnetGroup.GetResourceName(), - PubliclyAccessible: true, - VpcSecurityGroupIds: []string{fmt.Sprintf("%s.%s.id", output.GetResourceName(nsg), nsg.ResourceId)}, - Port: int(r.Args.Port), - }, - }, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - return database.NewAzureDatabase( - database.AzureDbServer{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - Name: r.Args.Name, - ResourceGroupName: GetResourceGroupName(r.Args.GetCommonParameters().ResourceGroupId), - Location: r.GetCloudSpecificLocation(), - }, - Engine: strings.ToLower(r.Args.Engine.String()), - Version: r.Args.EngineVersion, - StorageMb: int(r.Args.StorageGb * 1024), - AdministratorLogin: r.Args.Username, - AdministratorLoginPassword: r.Args.Password, - SkuName: common.DBSIZE[r.Args.Size][r.GetCloud()], - SubnetIds: subnetIds, - }, - ), nil + r.Subnets = subnets + r.ResourceWithId = resources.ResourceWithId[*resourcespb.DatabaseArgs]{ + ResourceId: resourceId, + Args: db, } - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) + + return nil } func (r *Database) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { @@ -237,17 +76,3 @@ func (r *Database) Validate(ctx resources.MultyContext) (errs []validate.Validat // TODO validate DB Size return errs } - -func (r *Database) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case commonpb.CloudProvider_AWS: - return database.AwsResourceName, nil - case commonpb.CloudProvider_AZURE: - if r.Args.Engine == resourcespb.DatabaseEngine_MYSQL { - return database.AzureMysqlResourceName, nil - } - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } - return "", nil -} diff --git a/resources/types/kubernetes_node_pool.go b/resources/types/kubernetes_node_pool.go index 37d36e68..fa343e6f 100644 --- a/resources/types/kubernetes_node_pool.go +++ b/resources/types/kubernetes_node_pool.go @@ -6,24 +6,9 @@ import ( "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/iam" - "github.com/multycloud/multy/resources/output/kubernetes_node_pool" "github.com/multycloud/multy/validate" ) -var kubernetesNodePoolMetadata = resources.ResourceMetadata[*resourcespb.KubernetesNodePoolArgs, *KubernetesNodePool, *resourcespb.KubernetesNodePoolResource]{ - CreateFunc: CreateKubernetesNodePool, - UpdateFunc: UpdateKubernetesNodePool, - ReadFromStateFunc: KubernetesNodePoolFromState, - ExportFunc: func(r *KubernetesNodePool, _ *resources.Resources) (*resourcespb.KubernetesNodePoolArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewKubernetesNodePool, - AbbreviatedName: "ks", -} - type KubernetesNodePool struct { resources.ChildResourceWithId[*KubernetesCluster, *resourcespb.KubernetesNodePoolArgs] @@ -31,72 +16,46 @@ type KubernetesNodePool struct { Subnet *Subnet } -func (r *KubernetesNodePool) GetMetadata() resources.ResourceMetadataInterface { - return &kubernetesNodePoolMetadata +func (r *KubernetesNodePool) Create(resourceId string, args *resourcespb.KubernetesNodePoolArgs, others *resources.Resources) error { + return NewKubernetesNodePool(r, resourceId, args, others) } -func CreateKubernetesNodePool(resourceId string, args *resourcespb.KubernetesNodePoolArgs, others *resources.Resources) (*KubernetesNodePool, error) { - return NewKubernetesNodePool(resourceId, args, others) +func (r *KubernetesNodePool) Update(args *resourcespb.KubernetesNodePoolArgs, _ *resources.Resources) error { + r.Args = args + return nil } -func UpdateKubernetesNodePool(resource *KubernetesNodePool, vn *resourcespb.KubernetesNodePoolArgs, others *resources.Resources) error { - resource.Args = vn - return nil +func (r *KubernetesNodePool) Import(resourceId string, args *resourcespb.KubernetesNodePoolArgs, others *resources.Resources) error { + return NewKubernetesNodePool(r, resourceId, args, others) } -func NewKubernetesNodePool(resourceId string, args *resourcespb.KubernetesNodePoolArgs, others *resources.Resources) (*KubernetesNodePool, error) { +func (r *KubernetesNodePool) Export(_ *resources.Resources) (*resourcespb.KubernetesNodePoolArgs, bool, error) { + return r.Args, true, nil +} + +func NewKubernetesNodePool(r *KubernetesNodePool, resourceId string, args *resourcespb.KubernetesNodePoolArgs, others *resources.Resources) error { cluster, err := resources.Get[*KubernetesCluster](resourceId, others, args.ClusterId) if err != nil { - return nil, errors.ValidationErrors([]validate.ValidationError{{ - ErrorMessage: err.Error(), - ResourceId: resourceId, - FieldName: "cluster_id", - }}) + return errors.ValidationError(resources.NewError(err, r.ResourceId, "cluster_id")) } - return newKubernetesNodePool(resourceId, args, others, cluster) -} - -func KubernetesNodePoolFromState(resource *KubernetesNodePool, _ *output.TfState) (*resourcespb.KubernetesNodePoolResource, error) { - return &resourcespb.KubernetesNodePoolResource{ - CommonParameters: &commonpb.CommonChildResourceParameters{ - ResourceId: resource.ResourceId, - NeedsUpdate: false, - }, - Name: resource.Args.Name, - SubnetId: resource.Args.SubnetId, - ClusterId: resource.Args.ClusterId, - StartingNodeCount: resource.Args.StartingNodeCount, - MinNodeCount: resource.Args.MinNodeCount, - MaxNodeCount: resource.Args.MaxNodeCount, - VmSize: resource.Args.VmSize, - DiskSizeGb: resource.Args.DiskSizeGb, - Labels: resource.Args.Labels, - AwsOverride: resource.Args.AwsOverride, - AzureOverride: resource.Args.AzureOverride, - }, nil + return newKubernetesNodePool(r, resourceId, args, others, cluster) } -func newKubernetesNodePool(resourceId string, args *resourcespb.KubernetesNodePoolArgs, others *resources.Resources, cluster *KubernetesCluster) (*KubernetesNodePool, error) { - knp := &KubernetesNodePool{ - ChildResourceWithId: resources.ChildResourceWithId[*KubernetesCluster, *resourcespb.KubernetesNodePoolArgs]{ - ResourceId: resourceId, - Args: args, - }, - } +func newKubernetesNodePool(knp *KubernetesNodePool, resourceId string, args *resourcespb.KubernetesNodePoolArgs, others *resources.Resources, cluster *KubernetesCluster) error { + knp.ChildResourceWithId = resources.NewChildResource(resourceId, cluster, args) if args.StartingNodeCount == 0 { knp.Args.StartingNodeCount = args.MinNodeCount } - knp.Parent = cluster knp.KubernetesCluster = cluster subnet, err := resources.Get[*Subnet](resourceId, others, args.SubnetId) if err != nil { - return nil, err + return err } knp.Subnet = subnet - return knp, nil + return nil } func (r *KubernetesNodePool) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { @@ -121,122 +80,3 @@ func (r *KubernetesNodePool) Validate(ctx resources.MultyContext) (errs []valida return errs } - -func (r *KubernetesNodePool) GetMainResourceName() (string, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - return output.GetResourceName(kubernetes_node_pool.AwsKubernetesNodeGroup{}), nil - } - if r.GetCloud() == commonpb.CloudProvider_AZURE { - return output.GetResourceName(kubernetes_node_pool.AzureKubernetesNodePool{}), nil - } - return "", fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -func (r *KubernetesNodePool) Translate(resources.MultyContext) ([]output.TfBlock, error) { - subnetId, err := resources.GetMainOutputId(r.Subnet) - if err != nil { - return nil, err - } - if err != nil { - return nil, err - } - - var instanceTypes []string - if r.Args.AwsOverride.GetInstanceTypes() != nil { - instanceTypes = r.Args.AwsOverride.GetInstanceTypes() - } else { - instanceTypes = []string{common.VMSIZE[r.Args.VmSize][r.GetCloud()]} - } - - if r.GetCloud() == commonpb.CloudProvider_AWS { - roleName := fmt.Sprintf("multy-k8nodepool-%s-%s-role", r.KubernetesCluster.Args.Name, r.Args.Name) - role := iam.AwsIamRole{ - AwsResource: common.NewAwsResource(r.ResourceId, roleName), - Name: roleName, - AssumeRolePolicy: iam.NewAssumeRolePolicy("ec2.amazonaws.com"), - } - clusterId, err := resources.GetMainOutputId(r.KubernetesCluster) - if err != nil { - return nil, err - } - return []output.TfBlock{ - &role, - iam.AwsIamRolePolicyAttachment{ - AwsResource: common.NewAwsResourceWithIdOnly(fmt.Sprintf("%s_%s", r.ResourceId, "AmazonEKSWorkerNodePolicy")), - Role: fmt.Sprintf("aws_iam_role.%s.name", r.ResourceId), - PolicyArn: "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy", - }, - iam.AwsIamRolePolicyAttachment{ - AwsResource: common.NewAwsResourceWithIdOnly(fmt.Sprintf("%s_%s", r.ResourceId, "AmazonEKS_CNI_Policy")), - Role: fmt.Sprintf("aws_iam_role.%s.name", r.ResourceId), - PolicyArn: "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", - }, - iam.AwsIamRolePolicyAttachment{ - AwsResource: common.NewAwsResourceWithIdOnly(fmt.Sprintf("%s_%s", r.ResourceId, "AmazonEC2ContainerRegistryReadOnly")), - Role: fmt.Sprintf("aws_iam_role.%s.name", r.ResourceId), - PolicyArn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly", - }, - &kubernetes_node_pool.AwsKubernetesNodeGroup{ - AwsResource: common.NewAwsResourceWithIdOnly(r.ResourceId), - ClusterName: clusterId, - NodeGroupName: r.Args.Name, - NodeRoleArn: fmt.Sprintf("aws_iam_role.%s.arn", r.ResourceId), - SubnetIds: []string{subnetId}, - ScalingConfig: kubernetes_node_pool.ScalingConfig{ - DesiredSize: int(r.Args.StartingNodeCount), - MaxSize: int(r.Args.MaxNodeCount), - MinSize: int(r.Args.MinNodeCount), - }, - Labels: r.Args.Labels, - InstanceTypes: instanceTypes, - }, - }, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - pool, err := r.translateAzNodePool() - if err != nil { - return nil, err - } - return []output.TfBlock{ - pool, - }, nil - - } - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -func (r *KubernetesNodePool) translateAzNodePool() (*kubernetes_node_pool.AzureKubernetesNodePool, error) { - clusterId, err := resources.GetMainOutputId(r.KubernetesCluster) - if err != nil { - return nil, err - } - subnetId, err := resources.GetMainOutputId(r.Subnet) - if err != nil { - return nil, err - } - - var vmSize string - if r.Args.AzureOverride.GetVmSize() != "" { - vmSize = r.Args.AzureOverride.GetVmSize() - } else { - vmSize = common.VMSIZE[r.Args.VmSize][r.GetCloud()] - } - - return &kubernetes_node_pool.AzureKubernetesNodePool{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - Name: r.Args.Name, - }, - ClusterId: clusterId, - NodeCount: int(r.Args.StartingNodeCount), - MaxSize: int(r.Args.MaxNodeCount), - MinSize: int(r.Args.MinNodeCount), - Labels: r.Args.Labels, - EnableAutoScaling: true, - VmSize: vmSize, - VirtualNetworkSubnetId: subnetId, - }, nil -} - -func (r *KubernetesNodePool) GetCloudSpecificLocation() string { - return r.KubernetesCluster.GetCloudSpecificLocation() -} diff --git a/resources/types/kubernetes_service.go b/resources/types/kubernetes_service.go index d9dcf4c3..d81d29da 100644 --- a/resources/types/kubernetes_service.go +++ b/resources/types/kubernetes_service.go @@ -2,117 +2,62 @@ package types import ( "fmt" - "github.com/apparentlymart/go-cidr/cidr" - "github.com/multycloud/multy/api/errors" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" - "github.com/multycloud/multy/flags" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/iam" - "github.com/multycloud/multy/resources/output/kubernetes_service" - "github.com/multycloud/multy/resources/output/route_table" - "github.com/multycloud/multy/resources/output/route_table_association" - "github.com/multycloud/multy/resources/output/subnet" "github.com/multycloud/multy/validate" - "github.com/zclconf/go-cty/cty" - "gopkg.in/yaml.v3" - "net" ) -type KubeConfig struct { - ApiVersion string `yaml:"apiVersion"` - Clusters []NamedKubeConfigCluster `yaml:"clusters"` - Contexts []NamedKubeConfigContext `yaml:"contexts"` - CurrentContext string `yaml:"current-context"` - Users []KubeConfigUser `yaml:"users"` - Kind string `yaml:"kind"` -} - -type NamedKubeConfigCluster struct { - Name string `yaml:"name"` - Cluster KubeConfigCluster `yaml:"cluster"` -} - -type KubeConfigCluster struct { - CertificateAuthorityData string `yaml:"certificate-authority-data"` - Server string `yaml:"server"` -} - -type NamedKubeConfigContext struct { - Name string `yaml:"name"` - Context KubeConfigContext `yaml:"context"` -} - -type KubeConfigContext struct { - User string `yaml:"user"` - Cluster string `yaml:"cluster"` -} +type KubernetesCluster struct { + resources.ResourceWithId[*resourcespb.KubernetesClusterArgs] -type KubeConfigUser struct { - Name string `yaml:"name"` - User struct { - Exec KubeConfigExec `yaml:"exec"` - } `yaml:"user"` + VirtualNetwork *VirtualNetwork + DefaultNodePool *KubernetesNodePool } -type KubeConfigExec struct { - ApiVersion string `yaml:"apiVersion"` - Command string `yaml:"command"` - Args []string `yaml:"args"` - InteractiveMode string `yaml:"interactiveMode"` +func (r *KubernetesCluster) Create(resourceId string, args *resourcespb.KubernetesClusterArgs, others *resources.Resources) error { + return CreateKubernetesCluster(r, resourceId, args, others) } -var kubernetesClusterMetadata = resources.ResourceMetadata[*resourcespb.KubernetesClusterArgs, *KubernetesCluster, *resourcespb.KubernetesClusterResource]{ - CreateFunc: CreateKubernetesCluster, - UpdateFunc: UpdateKubernetesCluster, - ReadFromStateFunc: KubernetesClusterFromState, - ExportFunc: func(r *KubernetesCluster, _ *resources.Resources) (*resourcespb.KubernetesClusterArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewKubernetesCluster, - AbbreviatedName: "ks", +func (r *KubernetesCluster) Update(args *resourcespb.KubernetesClusterArgs, _ *resources.Resources) error { + r.Args = args + return nil } -type KubernetesCluster struct { - resources.ResourceWithId[*resourcespb.KubernetesClusterArgs] - - VirtualNetwork *VirtualNetwork - DefaultNodePool *KubernetesNodePool +func (r *KubernetesCluster) Import(resourceId string, args *resourcespb.KubernetesClusterArgs, others *resources.Resources) error { + return NewKubernetesCluster(r, resourceId, args, others) } -func (r *KubernetesCluster) GetMetadata() resources.ResourceMetadataInterface { - return &kubernetesClusterMetadata +func (r *KubernetesCluster) Export(_ *resources.Resources) (*resourcespb.KubernetesClusterArgs, bool, error) { + return r.Args, true, nil } -func NewKubernetesCluster(resourceId string, args *resourcespb.KubernetesClusterArgs, others *resources.Resources) (*KubernetesCluster, error) { +func NewKubernetesCluster(cluster *KubernetesCluster, resourceId string, args *resourcespb.KubernetesClusterArgs, others *resources.Resources) error { vn, err := resources.Get[*VirtualNetwork](resourceId, others, args.VirtualNetworkId) if err != nil { - return nil, err + return err } - cluster := &KubernetesCluster{ - ResourceWithId: resources.ResourceWithId[*resourcespb.KubernetesClusterArgs]{ - ResourceId: resourceId, - Args: args, - }, - VirtualNetwork: vn, + cluster.ResourceWithId = resources.ResourceWithId[*resourcespb.KubernetesClusterArgs]{ + ResourceId: resourceId, + Args: args, } - cluster.DefaultNodePool, err = newKubernetesNodePool(fmt.Sprintf("%s_default_pool", resourceId), args.DefaultNodePool, others, cluster) - return cluster, err + cluster.VirtualNetwork = vn + + cluster.DefaultNodePool = &KubernetesNodePool{} + err = newKubernetesNodePool(cluster.DefaultNodePool, fmt.Sprintf("%s_default_pool", resourceId), args.DefaultNodePool, others, cluster) + return err } -func CreateKubernetesCluster(resourceId string, args *resourcespb.KubernetesClusterArgs, others *resources.Resources) (*KubernetesCluster, error) { +func CreateKubernetesCluster(cluster *KubernetesCluster, resourceId string, args *resourcespb.KubernetesClusterArgs, others *resources.Resources) error { if args.CommonParameters.ResourceGroupId == "" { vn, err := resources.Get[*VirtualNetwork](resourceId, others, args.VirtualNetworkId) if err != nil { - return nil, err + return err } rgId, err := NewRgFromParent("ks", vn.Args.CommonParameters.ResourceGroupId, others, args.GetCommonParameters().GetLocation(), args.GetCommonParameters().GetCloudProvider()) if err != nil { - return nil, err + return err } args.CommonParameters.ResourceGroupId = rgId } @@ -120,113 +65,8 @@ func CreateKubernetesCluster(resourceId string, args *resourcespb.KubernetesClus args.ServiceCidr = "10.100.0.0/16" } - return NewKubernetesCluster(resourceId, args, others) -} - -func UpdateKubernetesCluster(resource *KubernetesCluster, vn *resourcespb.KubernetesClusterArgs, others *resources.Resources) error { - resource.Args = vn - return nil -} - -func createKubeConfig(clusterName string, certData string, endpoint string, awsRegion string) (string, error) { - username := fmt.Sprintf("clusterUser_%s", clusterName) - kubeConfig := &KubeConfig{ - ApiVersion: "v1", - Kind: "Config", - Clusters: []NamedKubeConfigCluster{ - { - Name: clusterName, - Cluster: KubeConfigCluster{ - CertificateAuthorityData: certData, - Server: endpoint, - }, - }, - }, - Contexts: []NamedKubeConfigContext{ - { - Name: clusterName, - Context: KubeConfigContext{ - User: username, - Cluster: clusterName, - }, - }, - }, - Users: []KubeConfigUser{ - { - Name: username, - User: struct { - Exec KubeConfigExec `yaml:"exec"` - }{ - Exec: KubeConfigExec{ - ApiVersion: "client.authentication.k8s.io/v1alpha1", - Command: "aws", - Args: []string{"--region", awsRegion, "eks", "get-token", "--cluster-name", clusterName}, - InteractiveMode: "IfAvailable", - }, - }, - }, - }, - CurrentContext: clusterName, - } - - s, err := yaml.Marshal(kubeConfig) - if err != nil { - return "", fmt.Errorf("unable to marshal kube config, %s", err) - } - - return string(s), nil + return NewKubernetesCluster(cluster, resourceId, args, others) } - -func KubernetesClusterFromState(resource *KubernetesCluster, state *output.TfState) (*resourcespb.KubernetesClusterResource, error) { - var err error - result := &resourcespb.KubernetesClusterResource{ - CommonParameters: &commonpb.CommonResourceParameters{ - ResourceId: resource.ResourceId, - ResourceGroupId: resource.Args.CommonParameters.ResourceGroupId, - Location: resource.Args.CommonParameters.Location, - CloudProvider: resource.Args.CommonParameters.CloudProvider, - NeedsUpdate: false, - }, - Name: resource.Args.Name, - ServiceCidr: resource.Args.ServiceCidr, - VirtualNetworkId: resource.Args.VirtualNetworkId, - } - result.Endpoint = "dryrun" - if !flags.DryRun { - switch resource.GetCloud() { - case commonpb.CloudProvider_AWS: - values, err := state.GetValues(kubernetes_service.AwsEksCluster{}, resource.ResourceId) - if err != nil { - return nil, err - } - result.Endpoint = values["endpoint"].(string) - result.CaCertificate = values["certificate_authority"].([]interface{})[0].(map[string]interface{})["data"].(string) - kubeCgfRaw, err := createKubeConfig(resource.Args.Name, result.CaCertificate, result.Endpoint, resource.GetCloudSpecificLocation()) - if err != nil { - return nil, err - } - result.KubeConfigRaw = kubeCgfRaw - case commonpb.CloudProvider_AZURE: - values, err := state.GetValues(kubernetes_service.AzureEksCluster{}, resource.ResourceId) - if err != nil { - return nil, err - } - result.Endpoint = values["kube_config"].([]interface{})[0].(map[string]interface{})["host"].(string) - result.CaCertificate = values["kube_config"].([]interface{})[0].(map[string]interface{})["cluster_ca_certificate"].(string) - result.KubeConfigRaw = values["kube_config_raw"].(string) - default: - return nil, fmt.Errorf("unknown cloud %s", resource.GetCloud()) - } - } - - result.DefaultNodePool, err = KubernetesNodePoolFromState(resource.DefaultNodePool, state) - if err != nil { - return nil, err - } - - return result, err -} - func (r *KubernetesCluster) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { errs = append(errs, r.ResourceWithId.Validate()...) if r.Args.GetDefaultNodePool() == nil { @@ -238,201 +78,3 @@ func (r *KubernetesCluster) Validate(ctx resources.MultyContext) (errs []validat errs = append(errs, r.DefaultNodePool.Validate(ctx)...) return errs } - -func (r *KubernetesCluster) GetMainResourceName() (string, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - return output.GetResourceName(kubernetes_service.AwsEksCluster{}), nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - return output.GetResourceName(kubernetes_service.AzureEksCluster{}), nil - } - return "", fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -func (r *KubernetesCluster) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - var outputs []output.TfBlock - defaultNodePoolResources, err := r.DefaultNodePool.Translate(ctx) - if err != nil { - return nil, err - } - outputs = append(outputs, defaultNodePoolResources...) - subnets, subnetResources, err := r.getAwsSubnets() - if err != nil { - return nil, err - } - var subnetIds []string - for _, s := range subnets { - subnetIds = append(subnetIds, fmt.Sprintf("%s.%s.id", output.GetResourceName(s), s.ResourceId)) - } - - outputs = append(outputs, subnetResources...) - var deps []string - for _, s := range subnetResources { - // todo: get the id without casting - deps = append(deps, fmt.Sprintf("%s.%s", output.GetResourceName(s), s.GetResourceId())) - } - - roleName := fmt.Sprintf("multy-k8cluster-%s-role", r.Args.Name) - role := iam.AwsIamRole{ - AwsResource: common.NewAwsResource(r.ResourceId, roleName), - Name: roleName, - AssumeRolePolicy: iam.NewAssumeRolePolicy("eks.amazonaws.com"), - } - - role.GetFullResourceRef() - - policy1Id := fmt.Sprintf("%s_%s", r.ResourceId, "AmazonEKSClusterPolicy") - policy1 := iam.AwsIamRolePolicyAttachment{ - AwsResource: common.NewAwsResourceWithIdOnly(policy1Id), - Role: fmt.Sprintf("aws_iam_role.%s.name", r.ResourceId), - PolicyArn: "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy", - } - policy2Id := fmt.Sprintf("%s_%s", r.ResourceId, "AmazonEKSVPCResourceController") - policy2 := iam.AwsIamRolePolicyAttachment{ - AwsResource: common.NewAwsResourceWithIdOnly(policy2Id), - Role: fmt.Sprintf("aws_iam_role.%s.name", r.ResourceId), - PolicyArn: "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController", - } - deps = append(deps, fmt.Sprintf("%s.%s", output.GetResourceName(policy1), policy1Id), - fmt.Sprintf("%s.%s", output.GetResourceName(policy1), policy2Id)) - - outputs = append(outputs, &role, - policy1, - policy2, - &kubernetes_service.AwsEksCluster{ - AwsResource: common.NewAwsResourceWithDeps(r.ResourceId, r.Args.Name, deps), - RoleArn: fmt.Sprintf("aws_iam_role.%s.arn", r.ResourceId), - VpcConfig: kubernetes_service.VpcConfig{SubnetIds: subnetIds, EndpointPrivateAccess: true}, - Name: r.Args.Name, - KubernetesNetworkConfig: kubernetes_service.KubernetesNetworkConfig{ - ServiceIpv4Cidr: r.Args.ServiceCidr, - }, - }) - return outputs, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - defaultPool, err := r.DefaultNodePool.translateAzNodePool() - if err != nil { - return nil, err - } - defaultPool.Name = defaultPool.AzResource.Name - defaultPool.AzResource = nil - defaultPool.ClusterId = "" - - return []output.TfBlock{ - &kubernetes_service.AzureEksCluster{ - AzResource: common.NewAzResource(r.ResourceId, r.Args.Name, GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId), r.GetCloudSpecificLocation()), - DefaultNodePool: defaultPool, - DnsPrefix: common.UniqueId(r.Args.Name, "aks", common.LowercaseAlphanumericFormatFunc), - Identity: kubernetes_service.AzureIdentity{Type: "SystemAssigned"}, - NetworkProfile: kubernetes_service.NetworkProfile{ - NetworkPlugin: "azure", - DnsServiceIp: "10.100.0.10", - DockerBridgeCidr: "172.17.0.1/16", - ServiceCidr: r.Args.ServiceCidr, - }, - }, - }, nil - } - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -func (r *KubernetesCluster) getAwsSubnets() ([]subnet.AwsSubnet, []output.TfBlock, error) { - block := r.VirtualNetwork.Args.CidrBlock - _, vnNet, err := net.ParseCIDR(block) - if err != nil { - return nil, nil, err - } - tempSubnet, _ := cidr.NextSubnet(vnNet, 31) - subnetBlock1, _ := cidr.PreviousSubnet(tempSubnet, 28) - subnetBlock2, _ := cidr.PreviousSubnet(subnetBlock1, 28) - validationError := validate.ValidationError{ - ErrorMessage: fmt.Sprintf("Not enough availabilty zones available in region %s. Kubernetes clusters in AWS require 2 availabilty zones.", r.VirtualNetwork.GetLocation()), - ResourceId: r.ResourceId, - FieldName: "virtual_network_id", - } - az1, err := common.GetAvailabilityZone(r.VirtualNetwork.GetLocation(), 1, r.GetCloud()) - if err != nil { - return nil, nil, errors.ValidationErrors([]validate.ValidationError{validationError}) - } - az2, err := common.GetAvailabilityZone(r.VirtualNetwork.GetLocation(), 2, r.GetCloud()) - if err != nil { - return nil, nil, errors.ValidationErrors([]validate.ValidationError{validationError}) - } - - vpcId, err := resources.GetMainOutputId(r.VirtualNetwork) - if err != nil { - return nil, nil, err - } - gtw, err := r.VirtualNetwork.GetAssociatedInternetGateway() - if err != nil { - return nil, nil, err - } - rt := route_table.AwsRouteTable{ - AwsResource: common.NewAwsResource(r.ResourceId+"_public_rt", r.Args.Name+"_public_rt"), - VpcId: vpcId, - Routes: []route_table.AwsRouteTableRoute{ - { - CidrBlock: "0.0.0.0/0", - GatewayId: gtw, - }, - }, - } - - subnet1 := subnet.AwsSubnet{ - AwsResource: common.NewAwsResource(r.ResourceId+"_public_subnet", r.Args.Name+"_public_subnet"), - CidrBlock: subnetBlock1.String(), - VpcId: r.VirtualNetwork.GetVirtualNetworkId(), - AvailabilityZone: az1, - } - subnet2 := subnet.AwsSubnet{ - AwsResource: common.NewAwsResource(r.ResourceId+"_private_subnet", r.Args.Name+"_private_subnet"), - CidrBlock: subnetBlock2.String(), - VpcId: r.VirtualNetwork.GetVirtualNetworkId(), - AvailabilityZone: az2, - } - - rta := route_table_association.AwsRouteTableAssociation{ - AwsResource: common.NewAwsResourceWithIdOnly(r.ResourceId + "_public_rta"), - SubnetId: fmt.Sprintf("%s.%s.id", output.GetResourceName(subnet1), subnet1.ResourceId), - RouteTableId: fmt.Sprintf("%s.%s.id", output.GetResourceName(rt), rt.ResourceId), - } - - return []subnet.AwsSubnet{subnet1, subnet2}, []output.TfBlock{subnet1, subnet2, rt, rta}, nil -} - -func (r *KubernetesCluster) GetOutputValues(cloud commonpb.CloudProvider) map[string]cty.Value { - switch cloud { - case common.AWS: - return map[string]cty.Value{ - "endpoint": cty.StringVal( - fmt.Sprintf( - "${%s.%s.endpoint}", output.GetResourceName(kubernetes_service.AwsEksCluster{}), - r.ResourceId, - ), - ), - "ca_certificate": cty.StringVal( - fmt.Sprintf( - "${%s.%s.certificate_authority[0].data}", output.GetResourceName(kubernetes_service.AwsEksCluster{}), - r.ResourceId, - ), - ), - } - case common.AZURE: - return map[string]cty.Value{ - "endpoint": cty.StringVal( - fmt.Sprintf( - "${%s.%s.kube_config.0.host}", output.GetResourceName(kubernetes_service.AzureEksCluster{}), - r.ResourceId, - ), - ), - "ca_certificate": cty.StringVal( - fmt.Sprintf( - "${%s.%s.kube_config.0.cluster_ca_certificate}", output.GetResourceName(kubernetes_service.AzureEksCluster{}), - r.ResourceId, - ), - ), - } - } - - return nil -} diff --git a/resources/types/lambda.go b/resources/types/lambda.go deleted file mode 100644 index 0e99461a..00000000 --- a/resources/types/lambda.go +++ /dev/null @@ -1,418 +0,0 @@ -package types - -import ( - "fmt" - "github.com/multycloud/multy/api/proto/commonpb" - "github.com/multycloud/multy/api/proto/resourcespb" - "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/iam" - "github.com/multycloud/multy/resources/output/lambda" - "github.com/multycloud/multy/resources/output/local_exec" - "github.com/multycloud/multy/resources/output/object_storage" - "github.com/multycloud/multy/resources/output/object_storage_object" - "github.com/multycloud/multy/validate" - "github.com/zclconf/go-cty/cty" - "time" -) - -var lambdaMetadata = resources.ResourceMetadata[*resourcespb.LambdaArgs, *Lambda, *resourcespb.LambdaResource]{ - CreateFunc: CreateLambda, - UpdateFunc: UpdateLambda, - ReadFromStateFunc: LambdaFromState, - ExportFunc: func(r *Lambda, _ *resources.Resources) (*resourcespb.LambdaArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewLambda, - AbbreviatedName: "func", -} - -type Lambda struct { - resources.ResourceWithId[*resourcespb.LambdaArgs] - - SourceCodeObject *ObjectStorageObject `mhcl:"ref=source_code_object,optional"` -} - -func (r *Lambda) GetMetadata() resources.ResourceMetadataInterface { - return &lambdaMetadata -} - -type lambdaZip struct { - *output.TerraformDataSource `hcl:",squash"` - Type string `hcl:"type"` - SourceDir string `hcl:"source_dir"` - OutputPath string `hcl:"output_path"` -} - -const SasExpirationDuration = time.Hour * 24 * 365 - -func CreateLambda(resourceId string, args *resourcespb.LambdaArgs, others *resources.Resources) (*Lambda, error) { - if args.CommonParameters.ResourceGroupId == "" { - rgId, err := NewRg("func", others, args.GetCommonParameters().GetLocation(), args.GetCommonParameters().GetCloudProvider()) - if err != nil { - return nil, err - } - args.CommonParameters.ResourceGroupId = rgId - } - - return NewLambda(resourceId, args, others) -} - -func UpdateLambda(resource *Lambda, vn *resourcespb.LambdaArgs, others *resources.Resources) error { - resource.Args = vn - return nil -} - -func LambdaFromState(resource *Lambda, state *output.TfState) (*resourcespb.LambdaResource, error) { - return &resourcespb.LambdaResource{ - CommonParameters: &commonpb.CommonResourceParameters{ - ResourceId: resource.ResourceId, - ResourceGroupId: resource.Args.CommonParameters.ResourceGroupId, - Location: resource.Args.CommonParameters.Location, - CloudProvider: resource.GetCloud(), - NeedsUpdate: false, - }, - Name: resource.Args.Name, - Runtime: resource.Args.Runtime, - SourceCodeObjectId: resource.Args.SourceCodeObjectId, - }, nil -} - -func NewLambda(resourceId string, args *resourcespb.LambdaArgs, others *resources.Resources) (*Lambda, error) { - obj, _, err := resources.GetOptional[*ObjectStorageObject](resourceId, others, args.SourceCodeObjectId) - if err != nil { - return nil, err - } - - return &Lambda{ - ResourceWithId: resources.ResourceWithId[*resourcespb.LambdaArgs]{ - ResourceId: resourceId, - Args: args, - }, - SourceCodeObject: obj, - }, nil -} - -func (r *Lambda) Translate(resources.MultyContext) ([]output.TfBlock, error) { - sourceCodeDir := "" - if r.GetCloud() == commonpb.CloudProvider_AWS { - var result []output.TfBlock - - function := lambda.AwsLambdaFunction{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - FunctionName: r.Args.Name, - Runtime: r.Args.Runtime, - Role: fmt.Sprintf("aws_iam_role.%s.arn", r.getAwsIamRoleName()), - Handler: "lambda_function.lambda_handler", - } - - if false { - result = append( - result, lambdaZip{ - TerraformDataSource: &output.TerraformDataSource{ - ResourceName: "archive_file", - ResourceId: r.ResourceId, - }, - - Type: "zip", - SourceDir: sourceCodeDir, - OutputPath: r.getSourceCodeZip(), - }, - ) - function.SourceCodeHash = fmt.Sprintf("data.archive_file.%s.output_base64sha256", r.ResourceId) - function.Filename = r.getSourceCodeZip() - } else { - function.S3Bucket = r.SourceCodeObject.ObjectStorage.GetResourceName() - function.S3Key = r.SourceCodeObject.GetS3Key() - } - result = append(result, function) - result = append( - result, - iam.AwsIamRole{ - AwsResource: common.NewAwsResource(r.getAwsIamRoleName(), r.getAwsIamRoleName()), - Name: r.getAwsIamRoleName(), - AssumeRolePolicy: lambda.DefaultLambdaPolicy, - }, - // this gives permission to write cloudwatch logs - iam.AwsIamRolePolicyAttachment{ - AwsResource: &common.AwsResource{TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}}, - Role: fmt.Sprintf( - "%s.%s.name", output.GetResourceName(iam.AwsIamRole{}), r.getAwsIamRoleName(), - ), - PolicyArn: lambda.LambdaBasicExecutionRole, - }, - // https://registry.terraform.io/providers/hashicorp/aws/2.34.0/docs/guides/serverless-with-aws-lambda-and-api-gateway - lambda.AwsApiGatewayRestApi{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - Name: r.Args.Name, - }, - lambda.AwsApiGatewayResource{ - AwsResource: &common.AwsResource{TerraformResource: output.TerraformResource{ResourceId: fmt.Sprintf( - "%s_proxy", r.ResourceId, - )}}, - RestApiId: r.getAwsRestApiId(), - ParentId: r.getAwsRestRootId(), - PathPart: "{proxy+}", - }, - lambda.AwsApiGatewayMethod{ - AwsResource: &common.AwsResource{TerraformResource: output.TerraformResource{ResourceId: fmt.Sprintf( - "%s_proxy", r.ResourceId, - )}}, - RestApiId: r.getAwsRestApiId(), - ResourceId: fmt.Sprintf( - "%s.%s.id", output.GetResourceName(lambda.AwsApiGatewayResource{}), - fmt.Sprintf("%s_proxy", r.ResourceId), - ), - HttpMethod: "ANY", - Authorization: "NONE", - }, - lambda.AwsApiGatewayIntegration{ - AwsResource: &common.AwsResource{TerraformResource: output.TerraformResource{ResourceId: fmt.Sprintf( - "%s_proxy", r.ResourceId, - )}}, - RestApiId: r.getAwsRestApiId(), - ResourceId: fmt.Sprintf( - "%s.%s.resource_id", output.GetResourceName(lambda.AwsApiGatewayMethod{}), - fmt.Sprintf("%s_proxy", r.ResourceId), - ), - HttpMethod: fmt.Sprintf( - "%s.%s.http_method", - output.GetResourceName(lambda.AwsApiGatewayMethod{}), - fmt.Sprintf("%s_proxy", r.ResourceId), - ), - IntegrationHttpMethod: "POST", - Type: "AWS_PROXY", - Uri: fmt.Sprintf( - "%s.%s.invoke_arn", output.GetResourceName(lambda.AwsLambdaFunction{}), - r.ResourceId, - ), - }, - lambda.AwsApiGatewayMethod{ - AwsResource: &common.AwsResource{TerraformResource: output.TerraformResource{ResourceId: fmt.Sprintf( - "%s_proxy_root", r.ResourceId, - )}}, - RestApiId: r.getAwsRestApiId(), - ResourceId: r.getAwsRestRootId(), - HttpMethod: "ANY", - Authorization: "NONE", - }, - lambda.AwsApiGatewayIntegration{ - AwsResource: &common.AwsResource{TerraformResource: output.TerraformResource{ResourceId: fmt.Sprintf( - "%s_proxy_root", r.ResourceId, - )}}, - RestApiId: r.getAwsRestApiId(), - ResourceId: fmt.Sprintf( - "%s.%s.resource_id", output.GetResourceName(lambda.AwsApiGatewayMethod{}), - fmt.Sprintf("%s_proxy_root", r.ResourceId), - ), - HttpMethod: fmt.Sprintf( - "%s.%s.http_method", - output.GetResourceName(lambda.AwsApiGatewayMethod{}), - fmt.Sprintf("%s_proxy_root", r.ResourceId), - ), - IntegrationHttpMethod: "POST", - Type: "AWS_PROXY", - Uri: fmt.Sprintf( - "%s.%s.invoke_arn", output.GetResourceName(lambda.AwsLambdaFunction{}), - r.ResourceId, - ), - }, - lambda.AwsApiGatewayDeployment{ - AwsResource: &common.AwsResource{TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}}, - RestApiId: r.getAwsRestApiId(), - StageName: "api", - DependsOn: []string{ - fmt.Sprintf( - "%s.%s", output.GetResourceName(lambda.AwsApiGatewayIntegration{}), - r.ResourceId+"_proxy", - ), - fmt.Sprintf( - "%s.%s", output.GetResourceName(lambda.AwsApiGatewayIntegration{}), - r.ResourceId+"_proxy_root", - ), - }, - }, - lambda.AwsLambdaPermission{ - AwsResource: &common.AwsResource{TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}}, - StatementId: "AllowAPIGatewayInvoke", - Action: "lambda:InvokeFunction", - FunctionName: r.Args.Name, - Principal: "apigateway.amazonaws.com", - SourceArn: fmt.Sprintf( - "${%s}/*/*", fmt.Sprintf( - "%s.%s.execution_arn", output.GetResourceName(lambda.AwsApiGatewayRestApi{}), - r.ResourceId, - ), - ), - }, - ) - return result, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - rgName := GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId) - var result []output.TfBlock - function := lambda.AzureFunctionApp{ - AzResource: common.NewAzResource( - r.ResourceId, common.AlphanumericFormatFunc(r.Args.Name), rgName, - r.GetCloudSpecificLocation(), - ), - // AWS only supports linux - OperatingSystem: "linux", - AppServicePlanId: fmt.Sprintf("%s.%s.id", lambda.AzureAppServicePlanResourceName, r.ResourceId), - } - if false { - result = append( - result, lambdaZip{ - TerraformDataSource: &output.TerraformDataSource{ - ResourceName: "archive_file", - ResourceId: r.ResourceId, - }, - Type: "zip", - SourceDir: sourceCodeDir, - OutputPath: r.getSourceCodeZip(), - }, - ) - result = append( - result, object_storage.AzureStorageAccount{ - AzResource: common.NewAzResource( - r.ResourceId, - common.UniqueId(r.Args.Name, "stac", common.LowercaseAlphanumericFormatFunc), rgName, - r.GetCloudSpecificLocation(), - ), - AccountTier: "Standard", - AccountReplicationType: "LRS", - }, - ) - function.StorageAccountName = fmt.Sprintf( - "%s.%s.name", object_storage.AzureResourceName, r.ResourceId, - ) - function.StorageAccountAccessKey = fmt.Sprintf( - "%s.%s.primary_access_key", object_storage.AzureResourceName, r.ResourceId, - ) - function.LocalExec = local_exec.New( - local_exec.LocalExec{ - Command: fmt.Sprintf( - "az functionapp deployment source config-zip -g ${self.resource_group_name} -n ${self."+ - "name} --src ${data.archive_file.%s.output_path}", - r.ResourceId, - ), - }, - ) - } else { - function.StorageAccountName = r.SourceCodeObject.ObjectStorage.GetResourceName() - function.StorageAccountAccessKey = fmt.Sprintf( - "%s.%s.primary_access_key", object_storage.AzureResourceName, - r.SourceCodeObject.ObjectStorage.ResourceId, - ) - if r.SourceCodeObject.IsPrivate() { - sas := object_storage_object.AzureStorageAccountBlobSas{ - TerraformDataSource: &output.TerraformDataSource{ResourceId: r.ResourceId}, - ConnectionString: fmt.Sprintf( - "azurerm_storage_account.%s.primary_connection_string", - r.SourceCodeObject.ObjectStorage.ResourceId, - ), - ContainerName: r.SourceCodeObject.ObjectStorage.GetAssociatedPrivateContainerResourceName(), - Start: time.Now().Add(-24 * time.Hour).Format("2006-01-02T15:04:05Z"), - Expiry: time.Now().Add(SasExpirationDuration).Format("2006-01-02T15:04:05Z"), - AzureStorageAccountBlobSasPermissions: object_storage_object.AzureStorageAccountBlobSasPermissions{ - Read: true, - }, - } - result = append(result, sas) - function.AppSettings = map[string]string{ - "WEBSITE_RUN_FROM_PACKAGE": sas.GetSignedUrl( - r.SourceCodeObject.ObjectStorage.GetResourceName(), - r.SourceCodeObject.ObjectStorage.GetAssociatedPrivateContainerResourceName(), - r.SourceCodeObject.GetAzureBlobName(), - ), - } - } else { - function.AppSettings = map[string]string{ - "WEBSITE_RUN_FROM_PACKAGE": fmt.Sprintf("${%s}", r.SourceCodeObject.GetAzureBlobUrl()), - } - } - } - - result = append(result, function) - - result = append( - result, lambda.AzureAppServicePlan{ - AzResource: common.NewAzResource( - r.ResourceId, - common.UniqueId(r.Args.Name, "svpl", common.LowercaseAlphanumericFormatFunc), rgName, - r.GetCloudSpecificLocation(), - ), - Kind: "Linux", - Reserved: true, - Sku: lambda.AzureSku{ - Tier: "Dynamic", - Size: "Y1", - }, - }, - ) - return result, nil - } - return nil, nil -} - -func (r *Lambda) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { - errs = append(errs, r.ResourceWithId.Validate()...) - if r.SourceCodeObject == nil { - errs = append(errs, r.NewValidationError(fmt.Errorf("one of source_code_dir or source_code_object must be set"), "source_code_object_id")) - } - return errs -} - -func (r *Lambda) getAwsIamRoleName() string { - return fmt.Sprintf("iam_for_lambda_%s", r.ResourceId) -} - -func (r *Lambda) getAwsRestApiId() string { - return fmt.Sprintf("%s.%s.id", output.GetResourceName(lambda.AwsApiGatewayRestApi{}), r.ResourceId) -} - -func (r *Lambda) getAwsRestRootId() string { - return fmt.Sprintf( - "%s.%s.root_resource_id", output.GetResourceName(lambda.AwsApiGatewayRestApi{}), - r.ResourceId, - ) -} - -func (r *Lambda) getSourceCodeZip() string { - return fmt.Sprintf(".multy/tmp/%s.zip", r.Args.Name) -} - -func (r *Lambda) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case common.AWS: - return lambda.AwsResourceName, nil - case common.AZURE: - return lambda.AzureResourceName, nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} - -func (r *Lambda) GetOutputValues(cloud commonpb.CloudProvider) map[string]cty.Value { - switch cloud { - case common.AWS: - return map[string]cty.Value{ - "url": cty.StringVal( - fmt.Sprintf( - "${%s.%s.invoke_url}", output.GetResourceName(lambda.AwsApiGatewayDeployment{}), - r.ResourceId, - ), - ), - } - case common.AZURE: - return map[string]cty.Value{ - "url": cty.StringVal( - fmt.Sprintf( - "https://${%s.%s.default_hostname}", - output.GetResourceName(lambda.AzureFunctionApp{}), r.ResourceId, - ), - ), - } - } - return nil -} diff --git a/resources/types/metadata/all_resources_metadata.go b/resources/types/metadata/all_resources_metadata.go new file mode 100644 index 00000000..f7a84535 --- /dev/null +++ b/resources/types/metadata/all_resources_metadata.go @@ -0,0 +1,158 @@ +package metadata + +import ( + "github.com/multycloud/multy/api/proto/commonpb" + "github.com/multycloud/multy/api/proto/resourcespb" + "github.com/multycloud/multy/resources" + "github.com/multycloud/multy/resources/types" + aws_resources "github.com/multycloud/multy/resources/types/aws" + azure_resources "github.com/multycloud/multy/resources/types/azure" + "google.golang.org/protobuf/proto" +) + +var Metadatas = map[proto.Message]resources.ResourceMetadataInterface{ + &resourcespb.VirtualNetworkArgs{}: &resources.ResourceMetadata[*resourcespb.VirtualNetworkArgs, *types.VirtualNetwork, *resourcespb.VirtualNetworkResource]{ + AbbreviatedName: "vn", + ResourceType: "virtual_network", + Translators: map[commonpb.CloudProvider]func(*types.VirtualNetwork) resources.ResourceTranslator[*resourcespb.VirtualNetworkResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitVirtualNetwork, + commonpb.CloudProvider_AZURE: azure_resources.InitVirtualNetwork, + }, + }, + &resourcespb.SubnetArgs{}: &resources.ResourceMetadata[*resourcespb.SubnetArgs, *types.Subnet, *resourcespb.SubnetResource]{ + AbbreviatedName: "vn", + ResourceType: "subnet", + Translators: map[commonpb.CloudProvider]func(*types.Subnet) resources.ResourceTranslator[*resourcespb.SubnetResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitSubnet, + commonpb.CloudProvider_AZURE: azure_resources.InitSubnet, + }, + }, + &resourcespb.DatabaseArgs{}: &resources.ResourceMetadata[*resourcespb.DatabaseArgs, *types.Database, *resourcespb.DatabaseResource]{ + AbbreviatedName: "db", + ResourceType: "database", + Translators: map[commonpb.CloudProvider]func(*types.Database) resources.ResourceTranslator[*resourcespb.DatabaseResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitDatabase, + commonpb.CloudProvider_AZURE: azure_resources.InitDatabase, + }, + }, + &resourcespb.PublicIpArgs{}: &resources.ResourceMetadata[*resourcespb.PublicIpArgs, *types.PublicIp, *resourcespb.PublicIpResource]{ + AbbreviatedName: "pip", + ResourceType: "public_ip", + Translators: map[commonpb.CloudProvider]func(*types.PublicIp) resources.ResourceTranslator[*resourcespb.PublicIpResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitPublicIp, + commonpb.CloudProvider_AZURE: azure_resources.InitPublicIp, + }, + }, + &resourcespb.RouteTableArgs{}: &resources.ResourceMetadata[*resourcespb.RouteTableArgs, *types.RouteTable, *resourcespb.RouteTableResource]{ + AbbreviatedName: "rt", + ResourceType: "route_table", + Translators: map[commonpb.CloudProvider]func(*types.RouteTable) resources.ResourceTranslator[*resourcespb.RouteTableResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitRouteTable, + commonpb.CloudProvider_AZURE: azure_resources.InitRouteTable, + }, + }, + &resourcespb.RouteTableAssociationArgs{}: &resources.ResourceMetadata[*resourcespb.RouteTableAssociationArgs, *types.RouteTableAssociation, *resourcespb.RouteTableAssociationResource]{ + AbbreviatedName: "rt", + ResourceType: "route_table_association", + Translators: map[commonpb.CloudProvider]func(*types.RouteTableAssociation) resources.ResourceTranslator[*resourcespb.RouteTableAssociationResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitRouteTableAssociation, + commonpb.CloudProvider_AZURE: azure_resources.InitRouteTableAssociation, + }, + }, + &resourcespb.KubernetesNodePoolArgs{}: &resources.ResourceMetadata[*resourcespb.KubernetesNodePoolArgs, *types.KubernetesNodePool, *resourcespb.KubernetesNodePoolResource]{ + AbbreviatedName: "ks", + ResourceType: "kubernetes_node_pool", + Translators: map[commonpb.CloudProvider]func(*types.KubernetesNodePool) resources.ResourceTranslator[*resourcespb.KubernetesNodePoolResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitKubernetesNodePool, + commonpb.CloudProvider_AZURE: azure_resources.InitKubernetesNodePool, + }, + }, + &resourcespb.KubernetesClusterArgs{}: &resources.ResourceMetadata[*resourcespb.KubernetesClusterArgs, *types.KubernetesCluster, *resourcespb.KubernetesClusterResource]{ + AbbreviatedName: "ks", + ResourceType: "kubernetes_cluster", + Translators: map[commonpb.CloudProvider]func(*types.KubernetesCluster) resources.ResourceTranslator[*resourcespb.KubernetesClusterResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitKubernetesCluster, + commonpb.CloudProvider_AZURE: azure_resources.InitKubernetesCluster, + }, + }, + &resourcespb.NetworkInterfaceArgs{}: &resources.ResourceMetadata[*resourcespb.NetworkInterfaceArgs, *types.NetworkInterface, *resourcespb.NetworkInterfaceResource]{ + AbbreviatedName: "nic", + ResourceType: "network_interface", + Translators: map[commonpb.CloudProvider]func(*types.NetworkInterface) resources.ResourceTranslator[*resourcespb.NetworkInterfaceResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitNetworkInterface, + commonpb.CloudProvider_AZURE: azure_resources.InitNetworkInterface, + }, + }, + &resourcespb.NetworkInterfaceSecurityGroupAssociationArgs{}: &resources.ResourceMetadata[*resourcespb.NetworkInterfaceSecurityGroupAssociationArgs, *types.NetworkInterfaceSecurityGroupAssociation, *resourcespb.NetworkInterfaceSecurityGroupAssociationResource]{ + AbbreviatedName: "nic", + ResourceType: "network_interface_security_group_association", + Translators: map[commonpb.CloudProvider]func(*types.NetworkInterfaceSecurityGroupAssociation) resources.ResourceTranslator[*resourcespb.NetworkInterfaceSecurityGroupAssociationResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitNetworkInterfaceSecurityGroupAssociation, + commonpb.CloudProvider_AZURE: azure_resources.InitNetworkInterfaceSecurityGroupAssociation, + }, + }, + &resourcespb.NetworkSecurityGroupArgs{}: &resources.ResourceMetadata[*resourcespb.NetworkSecurityGroupArgs, *types.NetworkSecurityGroup, *resourcespb.NetworkSecurityGroupResource]{ + AbbreviatedName: "nsg", + ResourceType: "network_security_group", + Translators: map[commonpb.CloudProvider]func(*types.NetworkSecurityGroup) resources.ResourceTranslator[*resourcespb.NetworkSecurityGroupResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitNetworkSecurityGroup, + commonpb.CloudProvider_AZURE: azure_resources.InitNetworkSecurityGroup, + }, + }, + &resourcespb.ObjectStorageArgs{}: &resources.ResourceMetadata[*resourcespb.ObjectStorageArgs, *types.ObjectStorage, *resourcespb.ObjectStorageResource]{ + AbbreviatedName: "st", + ResourceType: "object_storage", + Translators: map[commonpb.CloudProvider]func(*types.ObjectStorage) resources.ResourceTranslator[*resourcespb.ObjectStorageResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitObjectStorage, + commonpb.CloudProvider_AZURE: azure_resources.InitObjectStorage, + }, + }, + &resourcespb.ObjectStorageObjectArgs{}: &resources.ResourceMetadata[*resourcespb.ObjectStorageObjectArgs, *types.ObjectStorageObject, *resourcespb.ObjectStorageObjectResource]{ + AbbreviatedName: "st", + ResourceType: "object_storage_object", + Translators: map[commonpb.CloudProvider]func(*types.ObjectStorageObject) resources.ResourceTranslator[*resourcespb.ObjectStorageObjectResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitObjectStorageObject, + commonpb.CloudProvider_AZURE: azure_resources.InitObjectStorageObject, + }, + }, + &resourcespb.VaultArgs{}: &resources.ResourceMetadata[*resourcespb.VaultArgs, *types.Vault, *resourcespb.VaultResource]{ + AbbreviatedName: "kv", + ResourceType: "vault", + Translators: map[commonpb.CloudProvider]func(*types.Vault) resources.ResourceTranslator[*resourcespb.VaultResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitVault, + commonpb.CloudProvider_AZURE: azure_resources.InitVault, + }, + }, + &resourcespb.VaultAccessPolicyArgs{}: &resources.ResourceMetadata[*resourcespb.VaultAccessPolicyArgs, *types.VaultAccessPolicy, *resourcespb.VaultAccessPolicyResource]{ + AbbreviatedName: "kv", + ResourceType: "vault_access_policy", + Translators: map[commonpb.CloudProvider]func(*types.VaultAccessPolicy) resources.ResourceTranslator[*resourcespb.VaultAccessPolicyResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitVaultAccessPolicy, + commonpb.CloudProvider_AZURE: azure_resources.InitVaultAccessPolicy, + }, + }, + &resourcespb.VaultSecretArgs{}: &resources.ResourceMetadata[*resourcespb.VaultSecretArgs, *types.VaultSecret, *resourcespb.VaultSecretResource]{ + AbbreviatedName: "kv", + ResourceType: "vault_secret", + Translators: map[commonpb.CloudProvider]func(secret *types.VaultSecret) resources.ResourceTranslator[*resourcespb.VaultSecretResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitVaultSecret, + commonpb.CloudProvider_AZURE: azure_resources.InitVaultSecret, + }, + }, + &resourcespb.VirtualMachineArgs{}: &resources.ResourceMetadata[*resourcespb.VirtualMachineArgs, *types.VirtualMachine, *resourcespb.VirtualMachineResource]{ + AbbreviatedName: "vm", + ResourceType: "virtual_machine", + Translators: map[commonpb.CloudProvider]func(*types.VirtualMachine) resources.ResourceTranslator[*resourcespb.VirtualMachineResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitVirtualMachine, + commonpb.CloudProvider_AZURE: azure_resources.InitVirtualMachine, + }, + }, + &resourcespb.ResourceGroupArgs{}: &resources.ResourceMetadata[*resourcespb.ResourceGroupArgs, *types.ResourceGroup, *resourcespb.ResourceGroupResource]{ + AbbreviatedName: "rg", + ResourceType: "resource_group", + Translators: map[commonpb.CloudProvider]func(*types.ResourceGroup) resources.ResourceTranslator[*resourcespb.ResourceGroupResource]{ + commonpb.CloudProvider_AWS: aws_resources.InitResourceGroup, + commonpb.CloudProvider_AZURE: azure_resources.InitResourceGroup, + }, + }, +} diff --git a/resources/types/network_interface.go b/resources/types/network_interface.go index ef2de982..38031c81 100644 --- a/resources/types/network_interface.go +++ b/resources/types/network_interface.go @@ -1,27 +1,11 @@ package types import ( - "fmt" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/network_interface" "github.com/multycloud/multy/validate" ) -var networkInterfaceMetadata = resources.ResourceMetadata[*resourcespb.NetworkInterfaceArgs, *NetworkInterface, *resourcespb.NetworkInterfaceResource]{ - CreateFunc: CreateNetworkInterface, - UpdateFunc: UpdateNetworkInterface, - ReadFromStateFunc: NetworkInterfaceFromState, - ExportFunc: func(r *NetworkInterface, _ *resources.Resources) (*resourcespb.NetworkInterfaceArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewNetworkInterface, - AbbreviatedName: "nic", -} - type NetworkInterface struct { resources.ResourceWithId[*resourcespb.NetworkInterfaceArgs] @@ -29,146 +13,55 @@ type NetworkInterface struct { PublicIp *PublicIp } -func (r *NetworkInterface) GetMetadata() resources.ResourceMetadataInterface { - return &networkInterfaceMetadata -} - -func CreateNetworkInterface(resourceId string, args *resourcespb.NetworkInterfaceArgs, others *resources.Resources) (*NetworkInterface, error) { +func (r *NetworkInterface) Create(resourceId string, args *resourcespb.NetworkInterfaceArgs, others *resources.Resources) error { if args.CommonParameters.ResourceGroupId == "" { subnet, err := resources.Get[*Subnet](resourceId, others, args.SubnetId) if err != nil { - return nil, err + return err } rgId, err := NewRgFromParent("nic", subnet.VirtualNetwork.Args.CommonParameters.ResourceGroupId, others, args.GetCommonParameters().GetLocation(), args.GetCommonParameters().GetCloudProvider()) if err != nil { - return nil, err + return err } args.CommonParameters.ResourceGroupId = rgId } - return NewNetworkInterface(resourceId, args, others) + return NewNetworkInterface(r, resourceId, args, others) } -func UpdateNetworkInterface(resource *NetworkInterface, vn *resourcespb.NetworkInterfaceArgs, others *resources.Resources) error { - resource.Args = vn +func (r *NetworkInterface) Update(args *resourcespb.NetworkInterfaceArgs, _ *resources.Resources) error { + r.Args = args return nil } -func NetworkInterfaceFromState(resource *NetworkInterface, _ *output.TfState) (*resourcespb.NetworkInterfaceResource, error) { - return &resourcespb.NetworkInterfaceResource{ - CommonParameters: &commonpb.CommonResourceParameters{ - ResourceId: resource.ResourceId, - ResourceGroupId: resource.Args.CommonParameters.ResourceGroupId, - Location: resource.Args.CommonParameters.Location, - CloudProvider: resource.Args.CommonParameters.CloudProvider, - NeedsUpdate: false, - }, - Name: resource.Args.Name, - SubnetId: resource.Args.SubnetId, - PublicIpId: resource.Args.PublicIpId, - }, nil +func (r *NetworkInterface) Import(resourceId string, args *resourcespb.NetworkInterfaceArgs, others *resources.Resources) error { + return NewNetworkInterface(r, resourceId, args, others) } -func NewNetworkInterface(resourceId string, args *resourcespb.NetworkInterfaceArgs, others *resources.Resources) (*NetworkInterface, error) { +func (r *NetworkInterface) Export(_ *resources.Resources) (*resourcespb.NetworkInterfaceArgs, bool, error) { + return r.Args, true, nil +} +func NewNetworkInterface(r *NetworkInterface, resourceId string, args *resourcespb.NetworkInterfaceArgs, others *resources.Resources) error { subnet, err := resources.Get[*Subnet](resourceId, others, args.SubnetId) if err != nil { - return nil, err + return err } + r.Subnet = subnet pIp, _, err := resources.GetOptional[*PublicIp](resourceId, others, args.PublicIpId) if err != nil { - return nil, err + return err } - return &NetworkInterface{ - ResourceWithId: resources.ResourceWithId[*resourcespb.NetworkInterfaceArgs]{ - ResourceId: resourceId, - Args: args, - }, - Subnet: subnet, - PublicIp: pIp, - }, nil -} + r.PublicIp = pIp -func (r *NetworkInterface) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { - var pIpId string - subnetId, err := resources.GetMainOutputId(r.Subnet) - if err != nil { - return nil, err + r.ResourceWithId = resources.ResourceWithId[*resourcespb.NetworkInterfaceArgs]{ + ResourceId: resourceId, + Args: args, } - if r.PublicIp != nil { - pIpId, err = resources.GetMainOutputId(r.PublicIp) - if err != nil { - return nil, err - } - } - - if r.GetCloud() == commonpb.CloudProvider_AWS { - var res []output.TfBlock - nic := network_interface.AwsNetworkInterface{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - SubnetId: subnetId, - } - if pIpId != "" { - res = append(res, network_interface.AwsEipAssociation{ - AwsResource: &common.AwsResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - }, - AllocationId: pIpId, - NetworkInterfaceId: fmt.Sprintf("%s.%s.id", output.GetResourceName(nic), nic.ResourceId), - }) - } - - res = append(res, nic) - - return res, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - rgName := GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId) - nic := network_interface.AzureNetworkInterface{ - AzResource: common.NewAzResource( - r.ResourceId, r.Args.Name, rgName, - r.GetCloudSpecificLocation(), - ), - // by default, virtual_machine will have a private ip - IpConfigurations: []network_interface.AzureIpConfiguration{{ - Name: "internal", // this name shouldn't be vm.name - PrivateIpAddressAllocation: "Dynamic", - SubnetId: subnetId, - Primary: true, - }}, - } - // associate a public ip configuration in case a public_ip resource references this network_interface - if pIpId != "" { - nic.IpConfigurations = []network_interface.AzureIpConfiguration{{ - Name: fmt.Sprintf("external-%s", r.Args.Name), - PrivateIpAddressAllocation: "Dynamic", - PublicIpAddressId: pIpId, - SubnetId: subnetId, - Primary: true, - }} - } - return []output.TfBlock{nic}, nil - } - - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -func (r *NetworkInterface) GetId(cloud commonpb.CloudProvider) string { - types := map[commonpb.CloudProvider]string{common.AWS: network_interface.AwsResourceName, common.AZURE: network_interface.AzureResourceName} - return fmt.Sprintf("%s.%s.id", types[cloud], r.ResourceId) + return nil } func (r *NetworkInterface) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { errs = append(errs, r.ResourceWithId.Validate()...) return errs } - -func (r *NetworkInterface) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case commonpb.CloudProvider_AWS: - return network_interface.AwsResourceName, nil - case commonpb.CloudProvider_AZURE: - return network_interface.AzureResourceName, nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} diff --git a/resources/types/network_interface_security_group_association.go b/resources/types/network_interface_security_group_association.go index efe48261..f734f224 100644 --- a/resources/types/network_interface_security_group_association.go +++ b/resources/types/network_interface_security_group_association.go @@ -1,29 +1,12 @@ package types import ( - "fmt" "github.com/multycloud/multy/api/errors" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/network_interface" - "github.com/multycloud/multy/resources/output/network_interface_security_group_association" "github.com/multycloud/multy/validate" ) -var networkInterfaceSecurityGroupAssociationMetadata = resources.ResourceMetadata[*resourcespb.NetworkInterfaceSecurityGroupAssociationArgs, *NetworkInterfaceSecurityGroupAssociation, *resourcespb.NetworkInterfaceSecurityGroupAssociationResource]{ - CreateFunc: CreateNetworkInterfaceSecurityGroupAssociation, - UpdateFunc: UpdateNetworkInterfaceSecurityGroupAssociation, - ReadFromStateFunc: NetworkInterfaceSecurityGroupAssociationFromState, - ExportFunc: func(r *NetworkInterfaceSecurityGroupAssociation, _ *resources.Resources) (*resourcespb.NetworkInterfaceSecurityGroupAssociationArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewNetworkInterfaceSecurityGroupAssociation, - AbbreviatedName: "nic", -} - type NetworkInterfaceSecurityGroupAssociation struct { resources.ChildResourceWithId[*NetworkInterface, *resourcespb.NetworkInterfaceSecurityGroupAssociationArgs] @@ -31,101 +14,39 @@ type NetworkInterfaceSecurityGroupAssociation struct { NetworkSecurityGroup *NetworkSecurityGroup } -func (r *NetworkInterfaceSecurityGroupAssociation) GetMetadata() resources.ResourceMetadataInterface { - return &networkInterfaceSecurityGroupAssociationMetadata +func (r *NetworkInterfaceSecurityGroupAssociation) Create(resourceId string, args *resourcespb.NetworkInterfaceSecurityGroupAssociationArgs, others *resources.Resources) error { + return NewNetworkInterfaceSecurityGroupAssociation(r, resourceId, args, others) } -func CreateNetworkInterfaceSecurityGroupAssociation(resourceId string, args *resourcespb.NetworkInterfaceSecurityGroupAssociationArgs, others *resources.Resources) (*NetworkInterfaceSecurityGroupAssociation, error) { - return NewNetworkInterfaceSecurityGroupAssociation(resourceId, args, others) +func (r *NetworkInterfaceSecurityGroupAssociation) Update(vn *resourcespb.NetworkInterfaceSecurityGroupAssociationArgs, others *resources.Resources) error { + r.Args = vn + return nil } -func UpdateNetworkInterfaceSecurityGroupAssociation(resource *NetworkInterfaceSecurityGroupAssociation, vn *resourcespb.NetworkInterfaceSecurityGroupAssociationArgs, others *resources.Resources) error { - resource.Args = vn - return nil +func (r *NetworkInterfaceSecurityGroupAssociation) Import(resourceId string, args *resourcespb.NetworkInterfaceSecurityGroupAssociationArgs, others *resources.Resources) error { + return NewNetworkInterfaceSecurityGroupAssociation(r, resourceId, args, others) } -func NetworkInterfaceSecurityGroupAssociationFromState(resource *NetworkInterfaceSecurityGroupAssociation, _ *output.TfState) (*resourcespb.NetworkInterfaceSecurityGroupAssociationResource, error) { - return &resourcespb.NetworkInterfaceSecurityGroupAssociationResource{ - CommonParameters: &commonpb.CommonChildResourceParameters{ - ResourceId: resource.ResourceId, - NeedsUpdate: false, - }, - NetworkInterfaceId: resource.Args.NetworkInterfaceId, - SecurityGroupId: resource.Args.SecurityGroupId, - }, nil +func (r *NetworkInterfaceSecurityGroupAssociation) Export(_ *resources.Resources) (*resourcespb.NetworkInterfaceSecurityGroupAssociationArgs, bool, error) { + return r.Args, true, nil } -func NewNetworkInterfaceSecurityGroupAssociation(resourceId string, args *resourcespb.NetworkInterfaceSecurityGroupAssociationArgs, others *resources.Resources) (*NetworkInterfaceSecurityGroupAssociation, error) { - nicNsgAssociation := &NetworkInterfaceSecurityGroupAssociation{ - ChildResourceWithId: resources.ChildResourceWithId[*NetworkInterface, *resourcespb.NetworkInterfaceSecurityGroupAssociationArgs]{ - ResourceId: resourceId, - Args: args, - }, - } +func NewNetworkInterfaceSecurityGroupAssociation(r *NetworkInterfaceSecurityGroupAssociation, resourceId string, args *resourcespb.NetworkInterfaceSecurityGroupAssociationArgs, others *resources.Resources) error { nic, err := resources.Get[*NetworkInterface](resourceId, others, args.NetworkInterfaceId) if err != nil { - return nil, errors.ValidationErrors([]validate.ValidationError{nicNsgAssociation.NewValidationError(err, "network_interface_id")}) + return errors.ValidationError(resources.NewError(err, r.ResourceId, "network_interface_id")) } - nicNsgAssociation.Parent = nic - nicNsgAssociation.NetworkInterface = nic + r.ChildResourceWithId = resources.NewChildResource(resourceId, nic, args) + r.NetworkInterface = nic nsg, err := resources.Get[*NetworkSecurityGroup](resourceId, others, args.SecurityGroupId) if err != nil { - return nil, errors.ValidationErrors([]validate.ValidationError{nicNsgAssociation.NewValidationError(err, "security_group_id")}) - } - nicNsgAssociation.NetworkSecurityGroup = nsg - return nicNsgAssociation, nil -} - -func (r *NetworkInterfaceSecurityGroupAssociation) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { - nicId, err := resources.GetMainOutputId(r.NetworkInterface) - if err != nil { - return nil, err - } - nsgId, err := resources.GetMainOutputId(r.NetworkSecurityGroup) - if err != nil { - return nil, err - } - - if r.GetCloud() == commonpb.CloudProvider_AWS { - return []output.TfBlock{ - network_interface_security_group_association.AwsNetworkInterfaceSecurityGroupAssociation{ - AwsResource: &common.AwsResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - }, - NetworkInterfaceId: nicId, - NetworkSecurityGroupId: nsgId, - }, - }, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - return []output.TfBlock{network_interface_security_group_association.AzureNetworkInterfaceSecurityGroupAssociation{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - }, - NetworkInterfaceId: nicId, - SecurityGroupId: nsgId, - }}, nil + return errors.ValidationError(resources.NewError(err, r.ResourceId, "security_group_id")) } - - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -func (r *NetworkInterfaceSecurityGroupAssociation) GetId(cloud commonpb.CloudProvider) string { - types := map[commonpb.CloudProvider]string{common.AWS: network_interface.AwsResourceName, common.AZURE: network_interface.AzureResourceName} - return fmt.Sprintf("%s.%s.id", types[cloud], r.ResourceId) + r.NetworkSecurityGroup = nsg + return nil } func (r *NetworkInterfaceSecurityGroupAssociation) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { return nil } - -func (r *NetworkInterfaceSecurityGroupAssociation) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case commonpb.CloudProvider_AWS: - return network_interface_security_group_association.AwsResourceName, nil - case commonpb.CloudProvider_AZURE: - return network_interface_security_group_association.AzureResourceName, nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} diff --git a/resources/types/network_security_group.go b/resources/types/network_security_group.go index b1dee52a..0898ada1 100644 --- a/resources/types/network_security_group.go +++ b/resources/types/network_security_group.go @@ -2,15 +2,9 @@ package types import ( "fmt" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/network_security_group" "github.com/multycloud/multy/validate" - "strconv" - "strings" ) /* @@ -20,61 +14,40 @@ When NSG is applied, only rules specified are allowed. AWS: VPC traffic is always added as an extra rule */ -var networkSecurityGroupMetadata = resources.ResourceMetadata[*resourcespb.NetworkSecurityGroupArgs, *NetworkSecurityGroup, *resourcespb.NetworkSecurityGroupResource]{ - CreateFunc: CreateNetworkSecurityGroup, - UpdateFunc: UpdateNetworkSecurityGroup, - ReadFromStateFunc: NetworkSecurityGroupFromState, - ExportFunc: func(r *NetworkSecurityGroup, _ *resources.Resources) (*resourcespb.NetworkSecurityGroupArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewNetworkSecurityGroup, - AbbreviatedName: "nsg", -} - type NetworkSecurityGroup struct { resources.ResourceWithId[*resourcespb.NetworkSecurityGroupArgs] VirtualNetwork *VirtualNetwork } -func (r *NetworkSecurityGroup) GetMetadata() resources.ResourceMetadataInterface { - return &networkSecurityGroupMetadata -} - -func CreateNetworkSecurityGroup(resourceId string, args *resourcespb.NetworkSecurityGroupArgs, others *resources.Resources) (*NetworkSecurityGroup, error) { +func (r *NetworkSecurityGroup) Create(resourceId string, args *resourcespb.NetworkSecurityGroupArgs, others *resources.Resources) error { if args.CommonParameters.ResourceGroupId == "" { vn, err := resources.Get[*VirtualNetwork](resourceId, others, args.VirtualNetworkId) if err != nil { - return nil, err + return err } rgId, err := NewRgFromParent("nsg", vn.Args.CommonParameters.ResourceGroupId, others, args.GetCommonParameters().GetLocation(), args.GetCommonParameters().GetCloudProvider()) if err != nil { - return nil, err + return err } args.CommonParameters.ResourceGroupId = rgId } - return NewNetworkSecurityGroup(resourceId, args, others) + return NewNetworkSecurityGroup(r, resourceId, args, others) } -func UpdateNetworkSecurityGroup(resource *NetworkSecurityGroup, vn *resourcespb.NetworkSecurityGroupArgs, others *resources.Resources) error { - resource.Args = vn + +func (r *NetworkSecurityGroup) Update(args *resourcespb.NetworkSecurityGroupArgs, _ *resources.Resources) error { + r.Args = args return nil } -func NetworkSecurityGroupFromState(resource *NetworkSecurityGroup, _ *output.TfState) (*resourcespb.NetworkSecurityGroupResource, error) { - return &resourcespb.NetworkSecurityGroupResource{ - CommonParameters: &commonpb.CommonResourceParameters{ - ResourceId: resource.ResourceId, - ResourceGroupId: resource.Args.CommonParameters.ResourceGroupId, - Location: resource.Args.CommonParameters.Location, - CloudProvider: resource.Args.CommonParameters.CloudProvider, - NeedsUpdate: false, - }, - Name: resource.Args.Name, - VirtualNetworkId: resource.Args.VirtualNetworkId, - Rules: resource.Args.Rules, - }, nil +func (r *NetworkSecurityGroup) Import(resourceId string, args *resourcespb.NetworkSecurityGroupArgs, others *resources.Resources) error { + return NewNetworkSecurityGroup(r, resourceId, args, others) +} + +func (r *NetworkSecurityGroup) Export(_ *resources.Resources) (*resourcespb.NetworkSecurityGroupArgs, bool, error) { + return r.Args, true, nil } type RuleType struct { @@ -86,201 +59,18 @@ type RuleType struct { Direction string `cty:"direction"` } -const ( - INGRESS = "ingress" - EGRESS = "egress" - BOTH = "both" - ALLOW = "allow" - DENY = "deny" -) - -func NewNetworkSecurityGroup(resourceId string, args *resourcespb.NetworkSecurityGroupArgs, others *resources.Resources) (*NetworkSecurityGroup, error) { +func NewNetworkSecurityGroup(nsg *NetworkSecurityGroup, resourceId string, args *resourcespb.NetworkSecurityGroupArgs, others *resources.Resources) error { vn, err := resources.Get[*VirtualNetwork](resourceId, others, args.VirtualNetworkId) if err != nil { - return nil, err - } - return &NetworkSecurityGroup{ - ResourceWithId: resources.ResourceWithId[*resourcespb.NetworkSecurityGroupArgs]{ - ResourceId: resourceId, - Args: args, - }, - VirtualNetwork: vn, - }, nil -} - -func (r *NetworkSecurityGroup) Translate(resources.MultyContext) ([]output.TfBlock, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - awsRules := translateAwsNsgRules(r.Args.Rules) - - allowVpcTraffic := network_security_group.AwsSecurityGroupRule{ - Protocol: "-1", - FromPort: 0, - ToPort: 0, - CidrBlocks: []string{r.VirtualNetwork.Args.CidrBlock}, - } - - awsRules[INGRESS] = append(awsRules[INGRESS], allowVpcTraffic) - awsRules[EGRESS] = append(awsRules[EGRESS], allowVpcTraffic) - - vnId, err := resources.GetMainOutputId(r.VirtualNetwork) - if err != nil { - return nil, err - } - return []output.TfBlock{ - network_security_group.AwsSecurityGroup{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - VpcId: vnId, - Name: r.Args.Name, - Description: "Managed by Multy", - Ingress: awsRules["ingress"], - Egress: awsRules["egress"], - }, - }, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - return []output.TfBlock{ - network_security_group.AzureNsg{ - AzResource: common.NewAzResource( - r.ResourceId, r.Args.Name, GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId), - r.GetCloudSpecificLocation(), - ), - Rules: translateAzNsgRules(r.Args.Rules), - }, - }, nil - } - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -func translateAwsNsgRules(rules []*resourcespb.NetworkSecurityRule) map[string][]network_security_group.AwsSecurityGroupRule { - awsRules := map[string][]network_security_group.AwsSecurityGroupRule{} - - for _, rule := range rules { - awsFromPort := int(rule.PortRange.From) - awsToPort := int(rule.PortRange.To) - - awsProtocol := rule.Protocol - if rule.Protocol == "*" { - awsProtocol = "-1" - awsFromPort = 0 - awsToPort = 0 - } - - if rule.Direction == resourcespb.Direction_BOTH_DIRECTIONS { - awsRules[INGRESS] = append( - awsRules[INGRESS], network_security_group.AwsSecurityGroupRule{ - Protocol: awsProtocol, - FromPort: awsFromPort, - ToPort: awsToPort, - CidrBlocks: []string{rule.CidrBlock}, - }, - ) - awsRules[EGRESS] = append( - awsRules[EGRESS], network_security_group.AwsSecurityGroupRule{ - Protocol: awsProtocol, - FromPort: awsFromPort, - ToPort: awsToPort, - CidrBlocks: []string{rule.CidrBlock}, - }, - ) - } else if rule.Direction == resourcespb.Direction_EGRESS { - awsRules[EGRESS] = append( - awsRules[EGRESS], network_security_group.AwsSecurityGroupRule{ - Protocol: awsProtocol, - FromPort: awsFromPort, - ToPort: awsToPort, - CidrBlocks: []string{rule.CidrBlock}, - }, - ) - } else if rule.Direction == resourcespb.Direction_INGRESS { - awsRules[INGRESS] = append( - awsRules[INGRESS], network_security_group.AwsSecurityGroupRule{ - Protocol: awsProtocol, - FromPort: awsFromPort, - ToPort: awsToPort, - CidrBlocks: []string{rule.CidrBlock}, - }, - ) - } - } - return awsRules -} - -func translatePortRange(pr *resourcespb.PortRange) string { - from := "*" - if pr.From != 0 { - from = strconv.Itoa(int(pr.From)) + return err } - to := "*" - if pr.To != 0 { - to = strconv.Itoa(int(pr.To)) + nsg.VirtualNetwork = vn + nsg.ResourceWithId = resources.ResourceWithId[*resourcespb.NetworkSecurityGroupArgs]{ + ResourceId: resourceId, + Args: args, } - return fmt.Sprintf("%s-%s", from, to) -} - -func translateAzNsgRules(rules []*resourcespb.NetworkSecurityRule) []network_security_group.AzureRule { - m := map[resourcespb.Direction]string{ - resourcespb.Direction_INGRESS: "Inbound", - resourcespb.Direction_EGRESS: "Outbound", - } - - var rls []network_security_group.AzureRule - - for _, rule := range rules { - protocol := strings.Title(strings.ToLower(rule.Protocol)) - if rule.Direction == resourcespb.Direction_BOTH_DIRECTIONS { - rls = append( - rls, network_security_group.AzureRule{ - Name: strconv.Itoa(len(rls)), - Protocol: protocol, - Priority: int(rule.Priority), - Access: "Allow", - SourcePortRange: "*", - SourceAddressPrefix: "*", - DestinationPortRange: translatePortRange(rule.PortRange), - DestinationAddressPrefix: "*", - Direction: m[resourcespb.Direction_INGRESS], - }, - ) - rls = append( - rls, network_security_group.AzureRule{ - Name: strconv.Itoa(len(rls)), - Protocol: protocol, - Priority: int(rule.Priority), - Access: "Allow", - SourcePortRange: "*", - SourceAddressPrefix: "*", - DestinationPortRange: translatePortRange(rule.PortRange), - DestinationAddressPrefix: "*", - Direction: m[resourcespb.Direction_EGRESS], - }, - ) - } else { - rls = append( - rls, network_security_group.AzureRule{ - Name: strconv.Itoa(len(rls)), - Protocol: protocol, - Priority: int(rule.Priority), - Access: "Allow", - SourcePortRange: "*", - SourceAddressPrefix: "*", - DestinationPortRange: translatePortRange(rule.PortRange), - DestinationAddressPrefix: "*", - Direction: m[rule.Direction], - }, - ) - } - } - - return rls -} - -func validateRuleDirection(s string) bool { - return s == INGRESS || s == EGRESS || s == BOTH -} - -func validateRuleAction(s string) bool { - return s == ALLOW || s == DENY + return nil } - func validatePort(port int32) bool { return port >= 0 && port <= 65535 } @@ -300,14 +90,3 @@ func (r *NetworkSecurityGroup) Validate(ctx resources.MultyContext) (errs []vali // TODO validate location matches with VN location return errs } - -func (r *NetworkSecurityGroup) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case commonpb.CloudProvider_AWS: - return network_security_group.AwsSecurityGroupResourceName, nil - case commonpb.CloudProvider_AZURE: - return network_security_group.AzureNetworkSecurityGroupResourceName, nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} diff --git a/resources/types/object_storage.go b/resources/types/object_storage.go index 0eaf1cec..fffec706 100644 --- a/resources/types/object_storage.go +++ b/resources/types/object_storage.go @@ -1,176 +1,53 @@ package types import ( - "fmt" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/object_storage" - "github.com/multycloud/multy/resources/output/object_storage_object" "github.com/multycloud/multy/validate" ) -var objectStorageMetadata = resources.ResourceMetadata[*resourcespb.ObjectStorageArgs, *ObjectStorage, *resourcespb.ObjectStorageResource]{ - CreateFunc: CreateObjectStorage, - UpdateFunc: UpdateObjectStorage, - ReadFromStateFunc: ObjectStorageFromState, - ExportFunc: func(r *ObjectStorage, _ *resources.Resources) (*resourcespb.ObjectStorageArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewObjectStorage, - AbbreviatedName: "st", -} - type ObjectStorage struct { resources.ResourceWithId[*resourcespb.ObjectStorageArgs] } -func (r *ObjectStorage) GetMetadata() resources.ResourceMetadataInterface { - return &objectStorageMetadata +func (r *ObjectStorage) Create(resourceId string, args *resourcespb.ObjectStorageArgs, others *resources.Resources) error { + return CreateObjectStorage(r, resourceId, args, others) } -func NewObjectStorage(resourceId string, db *resourcespb.ObjectStorageArgs, _ *resources.Resources) (*ObjectStorage, error) { - return &ObjectStorage{ - ResourceWithId: resources.ResourceWithId[*resourcespb.ObjectStorageArgs]{ - ResourceId: resourceId, - Args: db, - }, - }, nil -} - -func CreateObjectStorage(resourceId string, args *resourcespb.ObjectStorageArgs, others *resources.Resources) (*ObjectStorage, error) { - if args.CommonParameters.ResourceGroupId == "" { - rgId, err := NewRg("st", others, args.GetCommonParameters().GetLocation(), args.GetCommonParameters().GetCloudProvider()) - if err != nil { - return nil, err - } - args.CommonParameters.ResourceGroupId = rgId - } - - return NewObjectStorage(resourceId, args, others) -} - -func UpdateObjectStorage(resource *ObjectStorage, vn *resourcespb.ObjectStorageArgs, others *resources.Resources) error { - resource.Args = vn +func (r *ObjectStorage) Update(args *resourcespb.ObjectStorageArgs, _ *resources.Resources) error { + r.Args = args return nil } -type AclRules struct{} - -func ObjectStorageFromState(resource *ObjectStorage, _ *output.TfState) (*resourcespb.ObjectStorageResource, error) { - return &resourcespb.ObjectStorageResource{ - CommonParameters: &commonpb.CommonResourceParameters{ - ResourceId: resource.ResourceId, - ResourceGroupId: resource.Args.CommonParameters.ResourceGroupId, - Location: resource.Args.CommonParameters.Location, - CloudProvider: resource.Args.CommonParameters.CloudProvider, - NeedsUpdate: false, - }, - Name: resource.Args.Name, - Versioning: resource.Args.Versioning, - }, nil +func (r *ObjectStorage) Import(resourceId string, args *resourcespb.ObjectStorageArgs, others *resources.Resources) error { + return NewObjectStorage(r, resourceId, args, others) } -func (r *ObjectStorage) Translate(resources.MultyContext) ([]output.TfBlock, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - var awsResources []output.TfBlock - s3Bucket := object_storage.AwsS3Bucket{ - AwsResource: &common.AwsResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - }, - Bucket: r.Args.Name, - } - awsResources = append(awsResources, s3Bucket) - - if r.Args.Versioning { - awsResources = append(awsResources, object_storage.AwsS3BucketVersioning{ - AwsResource: &common.AwsResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - }, - BucketId: s3Bucket.GetBucketId(), - VersioningConfiguration: object_storage.VersioningConfiguration{Status: "Enabled"}, - }) - } - return awsResources, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - rgName := GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId) - - storageAccount := object_storage.AzureStorageAccount{ - AzResource: common.NewAzResource( - r.ResourceId, common.RemoveSpecialChars(r.Args.Name), rgName, - r.GetCloudSpecificLocation(), - ), - AccountTier: "Standard", - AccountReplicationType: "GZRS", - AllowNestedItemsToBePublic: true, - BlobProperties: object_storage.BlobProperties{ - VersioningEnabled: r.Args.Versioning, - }, - } - - return []output.TfBlock{ - storageAccount, - object_storage_object.AzureStorageContainer{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ - ResourceId: fmt.Sprintf("%s_%s", r.ResourceId, "public"), - }, - Name: "public", - }, - StorageAccountName: storageAccount.GetResourceName(), - ContainerAccessType: "blob", - }, object_storage_object.AzureStorageContainer{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ - ResourceId: fmt.Sprintf("%s_%s", r.ResourceId, "private"), - }, - Name: "private", - }, - StorageAccountName: storageAccount.GetResourceName(), - ContainerAccessType: "private", - }}, nil - } - - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) +func (r *ObjectStorage) Export(_ *resources.Resources) (*resourcespb.ObjectStorageArgs, bool, error) { + return r.Args, true, nil } -func (r *ObjectStorage) GetAssociatedPublicContainerResourceName() string { - if r.GetCloud() == commonpb.CloudProvider_AZURE { - return fmt.Sprintf("azurerm_storage_container.%s_public.name", r.ResourceId) +func NewObjectStorage(r *ObjectStorage, resourceId string, db *resourcespb.ObjectStorageArgs, _ *resources.Resources) error { + r.ResourceWithId = resources.ResourceWithId[*resourcespb.ObjectStorageArgs]{ + ResourceId: resourceId, + Args: db, } - return "" + return nil } -func (r *ObjectStorage) GetAssociatedPrivateContainerResourceName() string { - if r.GetCloud() == commonpb.CloudProvider_AZURE { - return fmt.Sprintf("azurerm_storage_container.%s_private.name", r.ResourceId) +func CreateObjectStorage(r *ObjectStorage, resourceId string, args *resourcespb.ObjectStorageArgs, others *resources.Resources) error { + if args.CommonParameters.ResourceGroupId == "" { + rgId, err := NewRg("st", others, args.GetCommonParameters().GetLocation(), args.GetCommonParameters().GetCloudProvider()) + if err != nil { + return err + } + args.CommonParameters.ResourceGroupId = rgId } - return "" -} -func (r *ObjectStorage) GetResourceName() string { - if r.GetCloud() == commonpb.CloudProvider_AWS { - return fmt.Sprintf("aws_s3_bucket.%s.id", r.ResourceId) - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - return fmt.Sprintf("azurerm_storage_account.%s.name", r.ResourceId) - } - return "" + return NewObjectStorage(r, resourceId, args, others) } func (r *ObjectStorage) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { errs = append(errs, r.ResourceWithId.Validate()...) return errs } - -func (r *ObjectStorage) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case commonpb.CloudProvider_AWS: - return "aws_s3_bucket", nil - case commonpb.CloudProvider_AZURE: - return "azurerm_storage_account", nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} diff --git a/resources/types/object_storage_object.go b/resources/types/object_storage_object.go index e6376133..977b2524 100644 --- a/resources/types/object_storage_object.go +++ b/resources/types/object_storage_object.go @@ -3,167 +3,46 @@ package types import ( "fmt" "github.com/multycloud/multy/api/errors" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" - "github.com/multycloud/multy/flags" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/object_storage" - "github.com/multycloud/multy/resources/output/object_storage_object" - "github.com/multycloud/multy/resources/output/terraform" "github.com/multycloud/multy/validate" ) // AWS: aws_s3_object // Azure: azurerm_storage_blob -var objectStorageObjectMetadata = resources.ResourceMetadata[*resourcespb.ObjectStorageObjectArgs, *ObjectStorageObject, *resourcespb.ObjectStorageObjectResource]{ - CreateFunc: CreateObjectStorageObject, - UpdateFunc: UpdateObjectStorageObject, - ReadFromStateFunc: ObjectStorageObjectFromState, - ExportFunc: func(r *ObjectStorageObject, _ *resources.Resources) (*resourcespb.ObjectStorageObjectArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewObjectStorageObject, - AbbreviatedName: "st", -} - type ObjectStorageObject struct { resources.ChildResourceWithId[*ObjectStorage, *resourcespb.ObjectStorageObjectArgs] ObjectStorage *ObjectStorage `mhcl:"ref=object_storage"` } -func (r *ObjectStorageObject) GetMetadata() resources.ResourceMetadataInterface { - return &objectStorageObjectMetadata +func (r *ObjectStorageObject) Create(resourceId string, args *resourcespb.ObjectStorageObjectArgs, others *resources.Resources) error { + return NewObjectStorageObject(r, resourceId, args, others) } -func CreateObjectStorageObject(resourceId string, args *resourcespb.ObjectStorageObjectArgs, others *resources.Resources) (*ObjectStorageObject, error) { - return NewObjectStorageObject(resourceId, args, others) -} - -func UpdateObjectStorageObject(resource *ObjectStorageObject, vn *resourcespb.ObjectStorageObjectArgs, others *resources.Resources) error { - resource.Args = vn +func (r *ObjectStorageObject) Update(args *resourcespb.ObjectStorageObjectArgs, _ *resources.Resources) error { + r.Args = args return nil } -func NewObjectStorageObject(resourceId string, args *resourcespb.ObjectStorageObjectArgs, others *resources.Resources) (*ObjectStorageObject, error) { - o := &ObjectStorageObject{ - ChildResourceWithId: resources.ChildResourceWithId[*ObjectStorage, *resourcespb.ObjectStorageObjectArgs]{ - ResourceId: resourceId, - Args: args, - }, - } - obj, err := resources.Get[*ObjectStorage](resourceId, others, args.ObjectStorageId) - if err != nil { - return nil, errors.ValidationErrors([]validate.ValidationError{o.NewValidationError(err, "object_storage_id")}) - } - o.Parent = obj - o.ObjectStorage = obj - return o, nil +func (r *ObjectStorageObject) Import(resourceId string, args *resourcespb.ObjectStorageObjectArgs, others *resources.Resources) error { + return NewObjectStorageObject(r, resourceId, args, others) } -func ObjectStorageObjectFromState(r *ObjectStorageObject, state *output.TfState) (*resourcespb.ObjectStorageObjectResource, error) { - out := new(resourcespb.ObjectStorageObjectResource) - out.CommonParameters = &commonpb.CommonChildResourceParameters{ - ResourceId: r.ResourceId, - NeedsUpdate: false, - } - - id, err := resources.GetMainOutputRef(r.Parent) - if err != nil { - return nil, err - } - - out.Name = r.Args.Name - out.ContentBase64 = r.Args.ContentBase64 - out.ContentType = r.Args.ContentType - out.ObjectStorageId = r.Args.ObjectStorageId - out.Acl = r.Args.Acl - out.Source = r.Args.Source - - if !flags.DryRun { - switch r.GetCloud() { - case common.AWS: - stateResource, err := output.GetParsed[object_storage.AwsS3Bucket](state, id) - if err != nil { - return nil, err - } - out.Url = fmt.Sprintf("https://%s.s3.amazonaws.com/%s", stateResource.Bucket, r.Args.Name) - case common.AZURE: - stateResource, err := output.GetParsed[object_storage.AzureStorageAccount](state, id) - if err != nil { - return nil, err - } - out.Url = fmt.Sprintf("https://%s.blob.core.windows.net/public/%s", stateResource.AzResource.Name, r.Args.Name) - } - } else { - out.Url = "dryrun" - } - - return out, nil +func (r *ObjectStorageObject) Export(_ *resources.Resources) (*resourcespb.ObjectStorageObjectArgs, bool, error) { + return r.Args, true, nil } -func (r *ObjectStorageObject) Translate(resources.MultyContext) ([]output.TfBlock, error) { - var acl string - if r.Args.Acl == resourcespb.ObjectStorageObjectAcl_PUBLIC_READ { - acl = "public-read" - } else { - acl = "private" - } - if r.GetCloud() == commonpb.CloudProvider_AWS { - return []output.TfBlock{object_storage_object.AwsS3BucketObject{ - AwsResource: &common.AwsResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - }, - Bucket: r.ObjectStorage.GetResourceName(), - Key: r.Args.Name, - Acl: acl, - ContentBase64: r.Args.ContentBase64, - ContentType: r.Args.ContentType, - Source: r.Args.Source, - }}, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - var containerName string - if r.Args.Acl == resourcespb.ObjectStorageObjectAcl_PUBLIC_READ { - containerName = r.ObjectStorage.GetAssociatedPublicContainerResourceName() - } else { - containerName = r.ObjectStorage.GetAssociatedPrivateContainerResourceName() - } - contentFile := terraform.NewLocalFile(r.ResourceId, r.Args.ContentBase64) - return []output.TfBlock{ - contentFile, - object_storage_object.AzureStorageAccountBlob{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - Name: r.Args.Name, - }, - StorageAccountName: r.ObjectStorage.GetResourceName(), - StorageContainerName: containerName, - Type: "Block", - Source: contentFile.GetFilename(), - ContentType: r.Args.ContentType, - }}, nil +func NewObjectStorageObject(r *ObjectStorageObject, resourceId string, args *resourcespb.ObjectStorageObjectArgs, others *resources.Resources) error { + obj, err := resources.Get[*ObjectStorage](resourceId, others, args.ObjectStorageId) + if err != nil { + return errors.ValidationError(resources.NewError(err, r.ResourceId, "object_storage_id")) } - - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -func (r *ObjectStorageObject) GetS3Key() string { - return fmt.Sprintf("%s.%s.key", "aws_s3_object", r.ResourceId) -} - -func (r *ObjectStorageObject) GetAzureBlobName() string { - return fmt.Sprintf("%s.%s.name", "azurerm_storage_blob", r.ResourceId) -} - -func (r *ObjectStorageObject) GetAzureBlobUrl() string { - return fmt.Sprintf("%s.%s.url", "azurerm_storage_blob", r.ResourceId) -} - -func (r *ObjectStorageObject) IsPrivate() bool { - return r.Args.Acl == resourcespb.ObjectStorageObjectAcl_PRIVATE + r.ChildResourceWithId = resources.NewChildResource(resourceId, obj, args) + r.Parent = obj + r.ObjectStorage = obj + return nil } func (r *ObjectStorageObject) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { @@ -172,18 +51,3 @@ func (r *ObjectStorageObject) Validate(ctx resources.MultyContext) (errs []valid } return errs } - -func (r *ObjectStorageObject) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case commonpb.CloudProvider_AWS: - return "aws_s3_object", nil - case commonpb.CloudProvider_AZURE: - return "azurerm_storage_blob", nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} - -func (r *ObjectStorageObject) GetCloudSpecificLocation() string { - return r.ObjectStorage.GetCloudSpecificLocation() -} diff --git a/resources/types/public_ip.go b/resources/types/public_ip.go index d0a24ec9..02289763 100644 --- a/resources/types/public_ip.go +++ b/resources/types/public_ip.go @@ -1,14 +1,8 @@ package types import ( - "fmt" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" - "github.com/multycloud/multy/flags" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/public_ip" "github.com/multycloud/multy/validate" ) @@ -18,134 +12,45 @@ AWS: NIC association done on public_ip Azure: NIC association done on NIC creation */ -var publicIpMetadata = resources.ResourceMetadata[*resourcespb.PublicIpArgs, *PublicIp, *resourcespb.PublicIpResource]{ - CreateFunc: CreatePublicIp, - UpdateFunc: UpdatePublicIp, - ReadFromStateFunc: PublicIpFromState, - ExportFunc: func(r *PublicIp, _ *resources.Resources) (*resourcespb.PublicIpArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewPublicIp, - AbbreviatedName: "pip", -} - type PublicIp struct { resources.ResourceWithId[*resourcespb.PublicIpArgs] } -func (r *PublicIp) GetMetadata() resources.ResourceMetadataInterface { - return &publicIpMetadata -} - -func CreatePublicIp(resourceId string, args *resourcespb.PublicIpArgs, others *resources.Resources) (*PublicIp, error) { +func (r *PublicIp) Create(resourceId string, args *resourcespb.PublicIpArgs, others *resources.Resources) error { if args.CommonParameters.ResourceGroupId == "" { // todo: maybe put in the same RG as VM? rgId, err := NewRg("pip", others, args.GetCommonParameters().GetLocation(), args.GetCommonParameters().GetCloudProvider()) if err != nil { - return nil, err + return err } args.CommonParameters.ResourceGroupId = rgId } - return NewPublicIp(resourceId, args, others) + return NewPublicIp(r, resourceId, args) } -func UpdatePublicIp(resource *PublicIp, vn *resourcespb.PublicIpArgs, others *resources.Resources) error { - resource.Args = vn +func (r *PublicIp) Update(args *resourcespb.PublicIpArgs, _ *resources.Resources) error { + r.Args = args return nil } -func PublicIpFromState(resource *PublicIp, state *output.TfState) (*resourcespb.PublicIpResource, error) { - var err error - ip := "dryrun" - if !flags.DryRun { - ip, err = getIp(resource.ResourceId, state, resource.Args.CommonParameters.CloudProvider) - if err != nil { - return nil, err - } - } - - return &resourcespb.PublicIpResource{ - CommonParameters: &commonpb.CommonResourceParameters{ - ResourceId: resource.ResourceId, - ResourceGroupId: resource.Args.CommonParameters.ResourceGroupId, - Location: resource.Args.CommonParameters.Location, - CloudProvider: resource.Args.CommonParameters.CloudProvider, - NeedsUpdate: false, - }, - Name: resource.Args.Name, - - Ip: ip, - }, nil +func (r *PublicIp) Import(resourceId string, args *resourcespb.PublicIpArgs, _ *resources.Resources) error { + return NewPublicIp(r, resourceId, args) } -func NewPublicIp(resourceId string, args *resourcespb.PublicIpArgs, others *resources.Resources) (*PublicIp, error) { - return &PublicIp{ - ResourceWithId: resources.ResourceWithId[*resourcespb.PublicIpArgs]{ - ResourceId: resourceId, - Args: args, - }, - }, nil +func (r *PublicIp) Export(_ *resources.Resources) (*resourcespb.PublicIpArgs, bool, error) { + return r.Args, true, nil } -func (r *PublicIp) Translate(resources.MultyContext) ([]output.TfBlock, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - return []output.TfBlock{ - public_ip.AwsElasticIp{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - //Vpc: true, - }, - }, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - return []output.TfBlock{ - public_ip.AzurePublicIp{ - AzResource: common.NewAzResource( - r.ResourceId, r.Args.Name, GetResourceGroupName(r.Args.GetCommonParameters().ResourceGroupId), - r.GetCloudSpecificLocation(), - ), - AllocationMethod: "Static", - }, - }, nil +func NewPublicIp(r *PublicIp, resourceId string, args *resourcespb.PublicIpArgs) error { + r.ResourceWithId = resources.ResourceWithId[*resourcespb.PublicIpArgs]{ + ResourceId: resourceId, + Args: args, } - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -func (r *PublicIp) GetId(cloud commonpb.CloudProvider) string { - types := map[commonpb.CloudProvider]string{common.AWS: public_ip.AwsResourceName, common.AZURE: public_ip.AzureResourceName} - return fmt.Sprintf("%s.%s.id", types[cloud], r.ResourceId) -} - -func getIp(resourceId string, state *output.TfState, cloud commonpb.CloudProvider) (string, error) { - switch cloud { - case commonpb.CloudProvider_AWS: - values, err := state.GetValues(public_ip.AwsElasticIp{}, resourceId) - if err != nil { - return "", err - } - return values["public_ip"].(string), nil - case commonpb.CloudProvider_AZURE: - values, err := state.GetValues(public_ip.AzurePublicIp{}, resourceId) - if err != nil { - return "", err - } - return values["ip_address"].(string), nil - } - - return "", fmt.Errorf("unknown cloud: %s", cloud.String()) + return nil } func (r *PublicIp) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { errs = append(errs, r.ResourceWithId.Validate()...) return errs } - -func (r *PublicIp) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case commonpb.CloudProvider_AWS: - return public_ip.AwsResourceName, nil - case commonpb.CloudProvider_AZURE: - return public_ip.AzureResourceName, nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} diff --git a/resources/types/resource_group.go b/resources/types/resource_group.go index f6b42a86..9f114964 100644 --- a/resources/types/resource_group.go +++ b/resources/types/resource_group.go @@ -6,75 +6,45 @@ import ( "github.com/multycloud/multy/api/proto/resourcespb" "github.com/multycloud/multy/resources" "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" "github.com/multycloud/multy/validate" "regexp" ) -var resourceGroupMetadata = resources.ResourceMetadata[*resourcespb.ResourceGroupArgs, *ResourceGroup, *resourcespb.ResourceGroupResource]{ - CreateFunc: CreateResourceGroup, - UpdateFunc: UpdateResourceGroup, - ReadFromStateFunc: ResourceGroupFromState, - ExportFunc: func(r *ResourceGroup, others *resources.Resources) (*resourcespb.ResourceGroupArgs, bool, error) { - if len(getAllDependentResources(r, others)) == 0 { - return nil, false, nil - } - - return &resourcespb.ResourceGroupArgs{ - CommonParameters: &commonpb.ResourceCommonArgs{ - Location: r.Location, - CloudProvider: r.Cloud, - }, - Name: r.Name, - }, true, nil - }, - ImportFunc: ImportResourceGroup, - AbbreviatedName: "rg", +func (r *ResourceGroup) Create(_ string, args *resourcespb.ResourceGroupArgs, _ *resources.Resources) error { + return ImportResourceGroup(r, args) } -// https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations -type ResourceGroup struct { - ResourceId string - Name string `hcl:"name"` - App string `hcl:"app"` - Location commonpb.Location - Cloud commonpb.CloudProvider - - ImplictlyCreated bool +func (r *ResourceGroup) Update(args *resourcespb.ResourceGroupArgs, _ *resources.Resources) error { + return fmt.Errorf("updates to resource groups are not supported") } -func (r *ResourceGroup) GetMetadata() resources.ResourceMetadataInterface { - return &resourceGroupMetadata +func (r *ResourceGroup) Import(_ string, args *resourcespb.ResourceGroupArgs, _ *resources.Resources) error { + return ImportResourceGroup(r, args) } -func CreateResourceGroup(resourceId string, args *resourcespb.ResourceGroupArgs, others *resources.Resources) (*ResourceGroup, error) { - return ImportResourceGroup(resourceId, args, others) +func (r *ResourceGroup) Export(others *resources.Resources) (*resourcespb.ResourceGroupArgs, bool, error) { + if len(r.GetAllDependentResources(others)) == 0 { + return nil, false, nil + } + return &resourcespb.ResourceGroupArgs{ + CommonParameters: &commonpb.ResourceCommonArgs{ + Location: r.Args.CommonParameters.Location, + CloudProvider: r.Args.CommonParameters.CloudProvider, + }, + Name: r.Args.Name, + }, true, nil } -func UpdateResourceGroup(resource *ResourceGroup, vn *resourcespb.ResourceGroupArgs, others *resources.Resources) error { - return fmt.Errorf("can't update resource group") -} +// https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations +type ResourceGroup struct { + resources.ResourceWithId[*resourcespb.ResourceGroupArgs] -func ResourceGroupFromState(resource *ResourceGroup, _ *output.TfState) (*resourcespb.ResourceGroupResource, error) { - return &resourcespb.ResourceGroupResource{ - CommonParameters: &commonpb.CommonResourceParameters{ - ResourceId: resource.ResourceId, - Location: resource.Location, - CloudProvider: resource.Cloud, - NeedsUpdate: false, - }, - Name: resource.Name, - }, nil + ImplictlyCreated bool } -func ImportResourceGroup(_ string, args *resourcespb.ResourceGroupArgs, _ *resources.Resources) (*ResourceGroup, error) { - return &ResourceGroup{ - ResourceId: args.Name, - Name: args.Name, - Location: args.GetCommonParameters().GetLocation(), - Cloud: args.GetCommonParameters().GetCloudProvider(), - ImplictlyCreated: false, - }, nil +func ImportResourceGroup(rg *ResourceGroup, args *resourcespb.ResourceGroupArgs) error { + rg.ResourceWithId = resources.NewResource(args.Name, args) + return nil } type AzureResourceGroup struct { @@ -87,7 +57,7 @@ const AzureResourceName = "azurerm_resource_group" func NewRgFromParent(resourceType string, parentResourceGroupId string, r *resources.Resources, location commonpb.Location, cloud commonpb.CloudProvider) (string, error) { var rgId string if rg, exists, err := resources.GetOptional[*ResourceGroup]("", r, parentResourceGroupId); exists && err == nil { - if matches := regexp.MustCompile("\\w+-(\\w+)-rg").FindStringSubmatch(rg.Name); len(matches) >= 2 { + if matches := regexp.MustCompile("\\w+-(\\w+)-rg").FindStringSubmatch(rg.ResourceId); len(matches) >= 2 { rgId = getDefaultResourceGroupIdString(resourceType, matches[1]) if !rgNameExists(r, rgId) { return rgId, r.Add(NewResourceGroup(rgId, location, cloud)) @@ -109,7 +79,7 @@ func NewRg(resourceType string, r *resources.Resources, location commonpb.Locati func rgNameExists(r *resources.Resources, rgName string) bool { for _, resource := range r.GetAll() { if rg, ok := resource.(*ResourceGroup); ok { - if rg.Name == rgName { + if rg.ResourceId == rgName { return true } } @@ -119,38 +89,18 @@ func rgNameExists(r *resources.Resources, rgName string) bool { func NewResourceGroup(name string, location commonpb.Location, cloud commonpb.CloudProvider) *ResourceGroup { return &ResourceGroup{ - ResourceId: name, - Name: name, - Location: location, - Cloud: cloud, - - ImplictlyCreated: true, - } -} - -func (rg *ResourceGroup) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { - allDeps := getAllDependentResources(rg, ctx.Resources) - if len(allDeps) == 0 { - // if no resources are in this group, just don't output anything - return nil, nil - } - for _, r := range allDeps { - ctx.Resources.AddDependency(rg.ResourceId, r) - } - - if rg.GetCloud() == common.AZURE { - return []output.TfBlock{AzureResourceGroup{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ResourceId: rg.ResourceId, ResourceName: AzureResourceName}, - Name: rg.Name, + ResourceWithId: resources.ResourceWithId[*resourcespb.ResourceGroupArgs]{ + ResourceId: name, + Args: &resourcespb.ResourceGroupArgs{ + CommonParameters: &commonpb.ResourceCommonArgs{ + Location: location, + CloudProvider: cloud, + }, + Name: name, }, - Location: rg.GetCloudSpecificLocation(), - }}, nil - } else if rg.GetCloud() == common.AWS { - return nil, nil + }, + ImplictlyCreated: true, } - - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", rg.GetCloud()) } func GetResourceGroupName(name string) string { @@ -160,52 +110,11 @@ func getDefaultResourceGroupIdString(resourceType string, groupId string) string return fmt.Sprintf("%s-%s-rg", resourceType, groupId) } -func (rg *ResourceGroup) GetResourceId() string { - return rg.ResourceId -} - -func (rg *ResourceGroup) GetCloudSpecificLocation() string { - if result, err := common.GetCloudLocation(rg.Location, rg.GetCloud()); err != nil { - validate.LogInternalError(err.Error()) - return "" - } else { - return result - } -} - func (rg *ResourceGroup) Validate(ctx resources.MultyContext) []validate.ValidationError { - if _, err := common.GetCloudLocation(rg.Location, rg.GetCloud()); err != nil { - return []validate.ValidationError{ - { - ErrorMessage: err.Error(), - ResourceId: rg.ResourceId, - FieldName: "location", - }, - } - } - return nil -} - -func (rg *ResourceGroup) GetMainResourceName() (string, error) { - switch rg.GetCloud() { - case common.AWS: - return "", nil - case common.AZURE: - return "AzureResourceName", nil - default: - return "", fmt.Errorf("unknown cloud %s", rg.GetCloud()) - } -} - -func (rg *ResourceGroup) GetCloud() commonpb.CloudProvider { - return rg.Cloud -} - -func (rg *ResourceGroup) GetCommonArgs() any { return nil } -func getAllDependentResources(r *ResourceGroup, others *resources.Resources) (res []string) { +func (r *ResourceGroup) GetAllDependentResources(others *resources.Resources) (res []string) { for _, other := range others.GetAll() { if wrg, ok := other.(interface{ GetResourceGroupId() string }); ok { if wrg.GetResourceGroupId() == r.GetResourceId() { diff --git a/resources/types/route_table.go b/resources/types/route_table.go index 37576191..99fe5ba6 100644 --- a/resources/types/route_table.go +++ b/resources/types/route_table.go @@ -4,13 +4,8 @@ import ( "fmt" "github.com/multycloud/multy/api/errors" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" - "github.com/multycloud/multy/flags" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/route_table" "github.com/multycloud/multy/validate" ) @@ -20,183 +15,39 @@ AWS: Internet route to IGW Azure: Internet route nextHop Internet */ -var routeTableMetadata = resources.ResourceMetadata[*resourcespb.RouteTableArgs, *RouteTable, *resourcespb.RouteTableResource]{ - CreateFunc: CreateRouteTable, - UpdateFunc: UpdateRouteTable, - ReadFromStateFunc: RouteTableFromState, - ExportFunc: func(r *RouteTable, _ *resources.Resources) (*resourcespb.RouteTableArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewRouteTable, - AbbreviatedName: "rt", -} - type RouteTable struct { resources.ChildResourceWithId[*VirtualNetwork, *resourcespb.RouteTableArgs] VirtualNetwork *VirtualNetwork `mhcl:"ref=virtual_network"` } -type RouteTableRoute struct { - CidrBlock string `cty:"cidr_block"` - Destination string `cty:"destination"` // allowed: Internet -} - -const ( - INTERNET = "Internet" - VIRTUALNETWORK = "VirtualNetwork" -) - -func (r *RouteTable) GetMetadata() resources.ResourceMetadataInterface { - return &routeTableMetadata -} - -func CreateRouteTable(resourceId string, args *resourcespb.RouteTableArgs, others *resources.Resources) (*RouteTable, error) { - return NewRouteTable(resourceId, args, others) +func (r *RouteTable) Create(resourceId string, args *resourcespb.RouteTableArgs, others *resources.Resources) error { + return NewRouteTable(r, resourceId, args, others) } -func UpdateRouteTable(resource *RouteTable, vn *resourcespb.RouteTableArgs, others *resources.Resources) error { - resource.Args = vn +func (r *RouteTable) Update(args *resourcespb.RouteTableArgs, _ *resources.Resources) error { + r.Args = args return nil } -func RouteTableFromState(resource *RouteTable, state *output.TfState) (*resourcespb.RouteTableResource, error) { - if flags.DryRun { - return &resourcespb.RouteTableResource{ - CommonParameters: &commonpb.CommonChildResourceParameters{ - ResourceId: resource.ResourceId, - NeedsUpdate: false, - }, - Name: resource.Args.Name, - VirtualNetworkId: resource.Args.VirtualNetworkId, - Routes: resource.Args.Routes, - }, nil - } - out := new(resourcespb.RouteTableResource) - out.CommonParameters = &commonpb.CommonChildResourceParameters{ - ResourceId: resource.ResourceId, - NeedsUpdate: false, - } - - id, err := resources.GetMainOutputRef(resource) - if err != nil { - return nil, err - } - - switch resource.GetCloud() { - case common.AWS: - stateResource, err := output.GetParsed[route_table.AwsRouteTable](state, id) - if err != nil { - return nil, err - } - out.Name = stateResource.AwsResource.Tags["Name"] - out.VirtualNetworkId = resource.Args.VirtualNetworkId - var routes []*resourcespb.Route - for _, r := range stateResource.Routes { - route := &resourcespb.Route{ - CidrBlock: r.CidrBlock, - Destination: resourcespb.RouteDestination_INTERNET, - } - routes = append(routes, route) - } - out.Routes = routes - case common.AZURE: - stateResource, err := output.GetParsed[route_table.AzureRouteTable](state, id) - if err != nil { - return nil, err - } - out.Name = stateResource.Name - out.VirtualNetworkId = resource.Args.VirtualNetworkId - var routes []*resourcespb.Route - for _, r := range stateResource.Routes { - route := &resourcespb.Route{ - CidrBlock: r.AddressPrefix, - Destination: resourcespb.RouteDestination_INTERNET, - } - routes = append(routes, route) - } - out.Routes = routes - } +func (r *RouteTable) Import(resourceId string, args *resourcespb.RouteTableArgs, others *resources.Resources) error { + return NewRouteTable(r, resourceId, args, others) +} - return out, nil +func (r *RouteTable) Export(_ *resources.Resources) (*resourcespb.RouteTableArgs, bool, error) { + return r.Args, true, nil } -func NewRouteTable(resourceId string, args *resourcespb.RouteTableArgs, others *resources.Resources) (*RouteTable, error) { - rt := &RouteTable{ - ChildResourceWithId: resources.ChildResourceWithId[*VirtualNetwork, *resourcespb.RouteTableArgs]{ - ResourceId: resourceId, - Args: args, - }, - } +func NewRouteTable(rt *RouteTable, resourceId string, args *resourcespb.RouteTableArgs, others *resources.Resources) error { vn, err := resources.Get[*VirtualNetwork](resourceId, others, args.VirtualNetworkId) if err != nil { - return nil, errors.ValidationErrors([]validate.ValidationError{rt.NewValidationError(err, "virtual_network_id")}) + return errors.ValidationError(resources.NewError(err, rt.ResourceId, "virtual_network_id")) } + rt.ChildResourceWithId = resources.NewChildResource(resourceId, vn, args) rt.Parent = vn rt.VirtualNetwork = vn - return rt, nil -} - -func (r *RouteTable) Translate(resources.MultyContext) ([]output.TfBlock, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - vpcId, err := resources.GetMainOutputId(r.VirtualNetwork) - if err != nil { - return nil, err - } - rt := route_table.AwsRouteTable{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - VpcId: vpcId, - } - gtw, err := r.VirtualNetwork.GetAssociatedInternetGateway() - if err != nil { - return nil, err - } - - var routes []route_table.AwsRouteTableRoute - for _, route := range r.Args.Routes { - if route.Destination == resourcespb.RouteDestination_INTERNET { - routes = append( - routes, route_table.AwsRouteTableRoute{ - CidrBlock: route.CidrBlock, - GatewayId: gtw, - }, - ) - } - } - rt.Routes = routes - - return []output.TfBlock{rt}, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - rt := route_table.AzureRouteTable{ - AzResource: common.NewAzResource( - r.ResourceId, r.Args.Name, GetResourceGroupName(r.VirtualNetwork.Args.GetCommonParameters().ResourceGroupId), - r.GetCloudSpecificLocation(), - ), - } - - var routes []route_table.AzureRouteTableRoute - for _, route := range r.Args.Routes { - if route.Destination == resourcespb.RouteDestination_INTERNET { - routes = append( - routes, route_table.AzureRouteTableRoute{ - Name: "internet", - AddressPrefix: route.CidrBlock, - NextHopType: INTERNET, - }, - ) - } - } - rt.Routes = routes - return []output.TfBlock{rt}, nil - } - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -func (r *RouteTable) GetId(cloud commonpb.CloudProvider) string { - types := map[commonpb.CloudProvider]string{common.AWS: route_table.AwsResourceName, common.AZURE: route_table.AzureResourceName} - return fmt.Sprintf("%s.%s.id", types[cloud], r.ResourceId) + return nil } - func (r *RouteTable) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { if len(r.Args.Routes) > 20 { errs = append(errs, r.NewValidationError(fmt.Errorf("\"%d\" exceeds routes limit is 20", len(r.Args.Routes)), "routes")) @@ -209,14 +60,3 @@ func (r *RouteTable) Validate(ctx resources.MultyContext) (errs []validate.Valid } return errs } - -func (r *RouteTable) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case commonpb.CloudProvider_AWS: - return route_table.AwsResourceName, nil - case commonpb.CloudProvider_AZURE: - return route_table.AzureResourceName, nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} diff --git a/resources/types/route_table_association.go b/resources/types/route_table_association.go index 139b6475..1bb3501e 100644 --- a/resources/types/route_table_association.go +++ b/resources/types/route_table_association.go @@ -3,26 +3,11 @@ package types import ( "fmt" "github.com/multycloud/multy/api/errors" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/route_table_association" "github.com/multycloud/multy/validate" ) -var routeTableAssociationMetadata = resources.ResourceMetadata[*resourcespb.RouteTableAssociationArgs, *RouteTableAssociation, *resourcespb.RouteTableAssociationResource]{ - CreateFunc: CreateRouteTableAssociation, - UpdateFunc: UpdateRouteTableAssociation, - ReadFromStateFunc: RouteTableAssociationFromState, - ExportFunc: func(r *RouteTableAssociation, _ *resources.Resources) (*resourcespb.RouteTableAssociationArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewRouteTableAssociation, - AbbreviatedName: "rt", -} - type RouteTableAssociation struct { resources.ChildResourceWithId[*RouteTable, *resourcespb.RouteTableAssociationArgs] @@ -30,88 +15,39 @@ type RouteTableAssociation struct { Subnet *Subnet } -func (r *RouteTableAssociation) GetMetadata() resources.ResourceMetadataInterface { - return &routeTableAssociationMetadata +func (r *RouteTableAssociation) Create(resourceId string, args *resourcespb.RouteTableAssociationArgs, others *resources.Resources) error { + return NewRouteTableAssociation(r, resourceId, args, others) } -func CreateRouteTableAssociation(resourceId string, args *resourcespb.RouteTableAssociationArgs, others *resources.Resources) (*RouteTableAssociation, error) { - return NewRouteTableAssociation(resourceId, args, others) +func (r *RouteTableAssociation) Update(args *resourcespb.RouteTableAssociationArgs, _ *resources.Resources) error { + r.Args = args + return nil } -func UpdateRouteTableAssociation(resource *RouteTableAssociation, vn *resourcespb.RouteTableAssociationArgs, others *resources.Resources) error { - resource.Args = vn - return nil +func (r *RouteTableAssociation) Import(resourceId string, args *resourcespb.RouteTableAssociationArgs, others *resources.Resources) error { + return NewRouteTableAssociation(r, resourceId, args, others) } -func RouteTableAssociationFromState(resource *RouteTableAssociation, _ *output.TfState) (*resourcespb.RouteTableAssociationResource, error) { - return &resourcespb.RouteTableAssociationResource{ - CommonParameters: &commonpb.CommonChildResourceParameters{ - ResourceId: resource.ResourceId, - NeedsUpdate: false, - }, - SubnetId: resource.Args.SubnetId, - RouteTableId: resource.Args.RouteTableId, - }, nil +func (r *RouteTableAssociation) Export(_ *resources.Resources) (*resourcespb.RouteTableAssociationArgs, bool, error) { + return r.Args, true, nil } -func NewRouteTableAssociation(resourceId string, args *resourcespb.RouteTableAssociationArgs, others *resources.Resources) (*RouteTableAssociation, error) { - rta := &RouteTableAssociation{ - ChildResourceWithId: resources.ChildResourceWithId[*RouteTable, *resourcespb.RouteTableAssociationArgs]{ - ResourceId: resourceId, - Args: args, - }, - } +func NewRouteTableAssociation(rta *RouteTableAssociation, resourceId string, args *resourcespb.RouteTableAssociationArgs, others *resources.Resources) error { rt, err := resources.Get[*RouteTable](resourceId, others, args.RouteTableId) if err != nil { - return nil, errors.ValidationErrors([]validate.ValidationError{rta.NewValidationError(err, "virtual_network_id")}) + return errors.ValidationErrors([]validate.ValidationError{rta.NewValidationError(err, "virtual_network_id")}) } + rta.ChildResourceWithId = resources.NewChildResource(resourceId, rt, args) rta.Parent = rt rta.RouteTable = rt subnet, err := resources.Get[*Subnet](resourceId, others, args.SubnetId) if err != nil { - return nil, errors.ValidationErrors([]validate.ValidationError{rta.NewValidationError(err, "subnet_id")}) + return errors.ValidationErrors([]validate.ValidationError{rta.NewValidationError(err, "subnet_id")}) } rta.Subnet = subnet - return rta, nil -} - -func (r *RouteTableAssociation) Translate(resources.MultyContext) ([]output.TfBlock, error) { - rtId, err := resources.GetMainOutputId(r.RouteTable) - if err != nil { - return nil, err - } - subnetId, err := resources.GetMainOutputId(r.Subnet) - if err != nil { - return nil, err - } - if r.GetCloud() == commonpb.CloudProvider_AWS { - return []output.TfBlock{ - route_table_association.AwsRouteTableAssociation{ - AwsResource: &common.AwsResource{ - TerraformResource: output.TerraformResource{ResourceId: r.Subnet.ResourceId}, - }, - RouteTableId: rtId, - SubnetId: subnetId, - }, - }, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - return []output.TfBlock{ - route_table_association.AzureRouteTableAssociation{ - // Here we use the subnet id so that it is the same as the one that is created by default in the subnet. - // This ensures that if a RTA is created after the default RTA is created, they will have the same ID and - // terraform will either update it in place or destroy it before creating it. - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ResourceId: r.Subnet.ResourceId}, - }, - RouteTableId: rtId, - SubnetId: subnetId, - }, - }, nil - } - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) + return nil } - func (r *RouteTableAssociation) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { if r.RouteTable.VirtualNetwork.ResourceId != r.Subnet.VirtualNetwork.ResourceId { errs = append(errs, r.NewValidationError(fmt.Errorf( @@ -121,14 +57,3 @@ func (r *RouteTableAssociation) Validate(ctx resources.MultyContext) (errs []val } return errs } - -func (r *RouteTableAssociation) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case commonpb.CloudProvider_AWS: - return route_table_association.AwsResourceName, nil - case commonpb.CloudProvider_AZURE: - return route_table_association.AzureResourceName, nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} diff --git a/resources/types/subnet.go b/resources/types/subnet.go index bb424ce5..cced7b4c 100644 --- a/resources/types/subnet.go +++ b/resources/types/subnet.go @@ -2,17 +2,9 @@ package types import ( "fmt" - "github.com/multycloud/multy/api/errors" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" - "github.com/multycloud/multy/flags" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/route_table_association" - "github.com/multycloud/multy/resources/output/subnet" - "github.com/multycloud/multy/util" "github.com/multycloud/multy/validate" ) @@ -21,178 +13,38 @@ Notes: Azure: New subnets will be associated with a default route table to block traffic to internet */ -var subnetMetadata = resources.ResourceMetadata[*resourcespb.SubnetArgs, *Subnet, *resourcespb.SubnetResource]{ - ImportFunc: NewSubnet, - CreateFunc: CreateSubnet, - UpdateFunc: UpdateSubnet, - ReadFromStateFunc: SubnetFromState, - ExportFunc: func(r *Subnet, _ *resources.Resources) (*resourcespb.SubnetArgs, bool, error) { - return r.Args, true, nil - }, - AbbreviatedName: "vn", -} - type Subnet struct { resources.ChildResourceWithId[*VirtualNetwork, *resourcespb.SubnetArgs] VirtualNetwork *VirtualNetwork } -func CreateSubnet(resourceId string, args *resourcespb.SubnetArgs, others *resources.Resources) (*Subnet, error) { - return NewSubnet(resourceId, args, others) +func (r *Subnet) Create(resourceId string, args *resourcespb.SubnetArgs, others *resources.Resources) error { + return NewSubnet(r, resourceId, args, others) } -func UpdateSubnet(resource *Subnet, vn *resourcespb.SubnetArgs, others *resources.Resources) error { - resource.Args = vn +func (r *Subnet) Update(args *resourcespb.SubnetArgs, _ *resources.Resources) error { + r.Args = args return nil } -func NewSubnet(resourceId string, subnet *resourcespb.SubnetArgs, others *resources.Resources) (*Subnet, error) { - s := &Subnet{ - ChildResourceWithId: resources.ChildResourceWithId[*VirtualNetwork, *resourcespb.SubnetArgs]{ - ResourceId: resourceId, - Args: subnet, - }, - } - vn, err := resources.Get[*VirtualNetwork](resourceId, others, subnet.VirtualNetworkId) - if err != nil { - return nil, errors.ValidationErrors([]validate.ValidationError{s.NewValidationError(err, "virtual_network_id")}) - } - s.Parent = vn - s.VirtualNetwork = vn - return s, nil -} - -func SubnetFromState(r *Subnet, state *output.TfState) (*resourcespb.SubnetResource, error) { - if flags.DryRun { - return &resourcespb.SubnetResource{ - CommonParameters: &commonpb.CommonChildResourceParameters{ - ResourceId: r.ResourceId, - NeedsUpdate: false, - }, - Name: r.Args.Name, - CidrBlock: r.Args.CidrBlock, - AvailabilityZone: r.Args.AvailabilityZone, - VirtualNetworkId: r.Args.VirtualNetworkId, - }, nil - } - out := new(resourcespb.SubnetResource) - out.CommonParameters = &commonpb.CommonChildResourceParameters{ - ResourceId: r.ResourceId, - NeedsUpdate: false, - } - out.AvailabilityZone = r.Args.AvailabilityZone - out.VirtualNetworkId = r.Args.GetVirtualNetworkId() - - id, err := resources.GetMainOutputRef(r) - if err != nil { - return nil, err - } - switch r.GetCloud() { - case common.AWS: - stateResource, err := output.GetParsed[subnet.AwsSubnet](state, id) - if err != nil { - return nil, err - } - out.Name = stateResource.AwsResource.Tags["Name"] - out.CidrBlock = stateResource.CidrBlock - case common.AZURE: - stateResource, err := output.GetParsed[subnet.AzureSubnet](state, id) - if err != nil { - return nil, err - } - out.Name = stateResource.Name - out.CidrBlock = stateResource.AddressPrefixes[0] - } - - return out, nil -} - -func (r *Subnet) GetMetadata() resources.ResourceMetadataInterface { - return &subnetMetadata -} - -func (r *Subnet) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - location := r.VirtualNetwork.GetLocation() - az, err := common.GetAvailabilityZone(location, int(r.Args.AvailabilityZone), r.GetCloud()) - if err != nil { - return nil, err - } - awsSubnet := subnet.AwsSubnet{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - CidrBlock: r.Args.CidrBlock, - VpcId: r.VirtualNetwork.GetVirtualNetworkId(), - AvailabilityZone: az, - } - // This flag needs to be set so that eks nodes can connect to the kubernetes cluster - // https://aws.amazon.com/blogs/containers/upcoming-changes-to-ip-assignment-for-eks-managed-node-groups/ - // How to tell if this subnet is private? - if len(resources.GetAllResourcesWithRef(ctx, func(k *KubernetesNodePool) *Subnet { return k.Subnet }, r)) > 0 { - awsSubnet.MapPublicIpOnLaunch = true - } - if len(resources.GetAllResourcesWithRef(ctx, func(k *KubernetesCluster) *Subnet { return k.DefaultNodePool.Subnet }, r)) > 0 { - awsSubnet.MapPublicIpOnLaunch = true - } - return []output.TfBlock{awsSubnet}, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - var azResources []output.TfBlock - azSubnet := subnet.AzureSubnet{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - Name: r.Args.Name, - ResourceGroupName: GetResourceGroupName(r.VirtualNetwork.Args.GetCommonParameters().GetResourceGroupId()), - }, - AddressPrefixes: []string{r.Args.CidrBlock}, - VirtualNetworkName: r.VirtualNetwork.GetVirtualNetworkName(), - } - azSubnet.ServiceEndpoints = getServiceEndpointSubnetReferences(ctx, r) - azResources = append(azResources, azSubnet) - - // there must be a better way to do this - if !checkSubnetRouteTableAssociated(ctx, r) { - rt, err := r.VirtualNetwork.GetAssociatedRouteTableId() - if err != nil { - return nil, err - } - subnetId, err := resources.GetMainOutputId(r) - if err != nil { - return nil, err - } - rtAssociation := route_table_association.AzureRouteTableAssociation{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - }, - SubnetId: subnetId, - RouteTableId: rt, - } - azResources = append(azResources, rtAssociation) - } - - return azResources, nil - } - return nil, fmt.Errorf("cloud %r is not supported for this resource type ", r.GetCloud().String()) +func (r *Subnet) Import(resourceId string, args *resourcespb.SubnetArgs, others *resources.Resources) error { + return NewSubnet(r, resourceId, args, others) } -func (r *Subnet) GetId() string { - t, _ := r.GetMainResourceName() - return fmt.Sprintf("%s.%s.id", t, r.ResourceId) +func (r *Subnet) Export(_ *resources.Resources) (*resourcespb.SubnetArgs, bool, error) { + return r.Args, true, nil } -func getServiceEndpointSubnetReferences(ctx resources.MultyContext, r *Subnet) []string { - const ( - DATABASE = "Microsoft.Sql" - ) - - serviceEndpoints := map[string]bool{} - if len(resources.GetAllResourcesWithListRef(ctx, func(db *Database) []*Subnet { return db.Subnets }, r)) > 0 { - serviceEndpoints[DATABASE] = true +func NewSubnet(s *Subnet, resourceId string, subnet *resourcespb.SubnetArgs, others *resources.Resources) error { + vn, err := resources.Get[*VirtualNetwork](resourceId, others, subnet.VirtualNetworkId) + if err != nil { + return errors.ValidationError(resources.NewError(err, s.ResourceId, "virtual_network_id")) } - return util.SortedKeys(serviceEndpoints) -} - -func checkSubnetRouteTableAssociated(ctx resources.MultyContext, r *Subnet) bool { - return len(resources.GetAllResourcesWithRef(ctx, func(rt *RouteTableAssociation) *Subnet { return rt.Subnet }, r)) > 0 + s.ChildResourceWithId = resources.NewChildResource(resourceId, vn, subnet) + s.Parent = vn + s.VirtualNetwork = vn + return nil } func (r *Subnet) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { @@ -206,18 +58,3 @@ func (r *Subnet) Validate(ctx resources.MultyContext) (errs []validate.Validatio return errs } - -func (r *Subnet) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case common.AWS: - return subnet.AwsResourceName, nil - case common.AZURE: - return subnet.AzureResourceName, nil - default: - return "", fmt.Errorf("unknown cloud %r", r.GetCloud().String()) - } -} - -func (r *Subnet) GetCloudSpecificLocation() string { - return r.VirtualNetwork.GetCloudSpecificLocation() -} diff --git a/resources/types/vault.go b/resources/types/vault.go index 58f2ce23..ab604862 100644 --- a/resources/types/vault.go +++ b/resources/types/vault.go @@ -1,127 +1,50 @@ package types import ( - "fmt" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/vault" "github.com/multycloud/multy/validate" ) -var vaultMetadata = resources.ResourceMetadata[*resourcespb.VaultArgs, *Vault, *resourcespb.VaultResource]{ - CreateFunc: CreateVault, - UpdateFunc: UpdateVault, - ReadFromStateFunc: VaultFromState, - ExportFunc: func(r *Vault, _ *resources.Resources) (*resourcespb.VaultArgs, bool, error) { return r.Args, true, nil }, - ImportFunc: NewVault, - AbbreviatedName: "kv", -} - type Vault struct { resources.ResourceWithId[*resourcespb.VaultArgs] } -func (r *Vault) GetMetadata() resources.ResourceMetadataInterface { - return &vaultMetadata -} - -func CreateVault(resourceId string, args *resourcespb.VaultArgs, others *resources.Resources) (*Vault, error) { - if args.CommonParameters.ResourceGroupId == "" { - rgId, err := NewRg("kv", others, args.GetCommonParameters().GetLocation(), args.GetCommonParameters().GetCloudProvider()) - if err != nil { - return nil, err - } - args.CommonParameters.ResourceGroupId = rgId - } - - return NewVault(resourceId, args, others) +func (r *Vault) Create(resourceId string, args *resourcespb.VaultArgs, others *resources.Resources) error { + return CreateVault(r, resourceId, args, others) } -func UpdateVault(resource *Vault, vn *resourcespb.VaultArgs, others *resources.Resources) error { - resource.Args = vn +func (r *Vault) Update(args *resourcespb.VaultArgs, _ *resources.Resources) error { + r.Args = args return nil } -func VaultFromState(resource *Vault, _ *output.TfState) (*resourcespb.VaultResource, error) { - return &resourcespb.VaultResource{ - CommonParameters: &commonpb.CommonResourceParameters{ - ResourceId: resource.ResourceId, - ResourceGroupId: resource.Args.CommonParameters.ResourceGroupId, - Location: resource.Args.CommonParameters.Location, - CloudProvider: resource.Args.CommonParameters.CloudProvider, - NeedsUpdate: false, - }, - Name: resource.Args.Name, - }, nil -} - -func NewVault(resourceId string, args *resourcespb.VaultArgs, _ *resources.Resources) (*Vault, error) { - return &Vault{ - ResourceWithId: resources.ResourceWithId[*resourcespb.VaultArgs]{resourceId, args}, - }, nil +func (r *Vault) Import(resourceId string, args *resourcespb.VaultArgs, others *resources.Resources) error { + return NewVault(r, resourceId, args, others) } -type AzureClientConfig struct { - *output.TerraformDataSource `hcl:",squash" default:"name=azurerm_client_config"` +func (r *Vault) Export(_ *resources.Resources) (*resourcespb.VaultArgs, bool, error) { + return r.Args, true, nil } -func (r *Vault) Translate(resources.MultyContext) ([]output.TfBlock, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - return []output.TfBlock{}, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - return []output.TfBlock{ - AzureClientConfig{TerraformDataSource: &output.TerraformDataSource{ResourceId: r.ResourceId}}, - vault.AzureKeyVault{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - Name: r.Args.Name, - ResourceGroupName: GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId), - Location: r.GetCloudSpecificLocation(), - }, - Sku: "standard", - TenantId: fmt.Sprintf("data.azurerm_client_config.%s.tenant_id", r.ResourceId), - AccessPolicy: []vault.AzureKeyVaultAccessPolicyInline{{ - TenantId: fmt.Sprintf( - "data.azurerm_client_config.%s.tenant_id", r.ResourceId, - ), - ObjectId: fmt.Sprintf( - "data.azurerm_client_config.%s.object_id", r.ResourceId, - ), - AzureKeyVaultPermissions: &vault.AzureKeyVaultPermissions{ - CertificatePermissions: []string{}, - KeyPermissions: []string{}, - SecretPermissions: []string{"List", "Get", "Set", "Delete", "Recover", "Backup", "Restore", "Purge"}, - }, - }}, - }}, nil +func CreateVault(r *Vault, resourceId string, args *resourcespb.VaultArgs, others *resources.Resources) error { + if args.CommonParameters.ResourceGroupId == "" { + rgId, err := NewRg("kv", others, args.GetCommonParameters().GetLocation(), args.GetCommonParameters().GetCloudProvider()) + if err != nil { + return err + } + args.CommonParameters.ResourceGroupId = rgId } - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) + + return NewVault(r, resourceId, args, others) } -func (r *Vault) GetVaultId() (string, error) { - switch r.GetCloud() { - case common.AZURE: - return fmt.Sprintf("%s.%s.id", vault.AzureResourceName, r.ResourceId), nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } +func NewVault(r *Vault, resourceId string, args *resourcespb.VaultArgs, _ *resources.Resources) error { + r.ResourceWithId = resources.NewResource(resourceId, args) + return nil } func (r *Vault) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { errs = append(errs, r.ResourceWithId.Validate()...) return errs } - -func (r *Vault) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case common.AWS: - return "", nil - case common.AZURE: - return vault.AzureResourceName, nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} diff --git a/resources/types/vault_access_policy.go b/resources/types/vault_access_policy.go index c0b2595e..e7bacc3c 100644 --- a/resources/types/vault_access_policy.go +++ b/resources/types/vault_access_policy.go @@ -3,69 +3,39 @@ package types import ( "fmt" "github.com/multycloud/multy/api/errors" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/vault" - "github.com/multycloud/multy/resources/output/vault_access_policy" - "github.com/multycloud/multy/resources/output/vault_secret" "github.com/multycloud/multy/validate" ) -var vaultAccessPolicyMetadata = resources.ResourceMetadata[*resourcespb.VaultAccessPolicyArgs, *VaultAccessPolicy, *resourcespb.VaultAccessPolicyResource]{ - CreateFunc: CreateVaultAccessPolicy, - UpdateFunc: UpdateVaultAccessPolicy, - ReadFromStateFunc: VaultAccessPolicyFromState, - ExportFunc: func(r *VaultAccessPolicy, _ *resources.Resources) (*resourcespb.VaultAccessPolicyArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewVaultAccessPolicy, - AbbreviatedName: "kv", -} - type VaultAccessPolicy struct { resources.ChildResourceWithId[*Vault, *resourcespb.VaultAccessPolicyArgs] Vault *Vault } -func (r *VaultAccessPolicy) GetMetadata() resources.ResourceMetadataInterface { - return &vaultAccessPolicyMetadata +func (r *VaultAccessPolicy) Create(resourceId string, args *resourcespb.VaultAccessPolicyArgs, others *resources.Resources) error { + return NewVaultAccessPolicy(r, resourceId, args, others) } -func CreateVaultAccessPolicy(resourceId string, args *resourcespb.VaultAccessPolicyArgs, others *resources.Resources) (*VaultAccessPolicy, error) { - return NewVaultAccessPolicy(resourceId, args, others) +func (r *VaultAccessPolicy) Update(args *resourcespb.VaultAccessPolicyArgs, _ *resources.Resources) error { + r.Args = args + return nil } -func UpdateVaultAccessPolicy(resource *VaultAccessPolicy, vn *resourcespb.VaultAccessPolicyArgs, others *resources.Resources) error { - resource.Args = vn - return nil +func (r *VaultAccessPolicy) Import(resourceId string, args *resourcespb.VaultAccessPolicyArgs, others *resources.Resources) error { + return NewVaultAccessPolicy(r, resourceId, args, others) } -func VaultAccessPolicyFromState(resource *VaultAccessPolicy, _ *output.TfState) (*resourcespb.VaultAccessPolicyResource, error) { - return &resourcespb.VaultAccessPolicyResource{ - CommonParameters: &commonpb.CommonChildResourceParameters{ - ResourceId: resource.ResourceId, - NeedsUpdate: false, - }, - VaultId: resource.Args.VaultId, - Identity: resource.Args.Identity, - Access: resource.Args.Access, - }, nil +func (r *VaultAccessPolicy) Export(_ *resources.Resources) (*resourcespb.VaultAccessPolicyArgs, bool, error) { + return r.Args, true, nil } -func NewVaultAccessPolicy(resourceId string, args *resourcespb.VaultAccessPolicyArgs, others *resources.Resources) (*VaultAccessPolicy, error) { - vap := &VaultAccessPolicy{ - ChildResourceWithId: resources.ChildResourceWithId[*Vault, *resourcespb.VaultAccessPolicyArgs]{ - ResourceId: resourceId, - Args: args, - }, - } +func NewVaultAccessPolicy(vap *VaultAccessPolicy, resourceId string, args *resourcespb.VaultAccessPolicyArgs, others *resources.Resources) error { v, err := resources.Get[*Vault](resourceId, others, args.VaultId) if err != nil { - return nil, errors.ValidationErrors([]validate.ValidationError{vap.NewValidationError(err, "vault_id")}) + return errors.ValidationError(resources.NewError(err, vap.ResourceId, "vault_id")) } + vap.ChildResourceWithId = resources.NewChildResource(resourceId, v, args) vap.Parent = v vap.Vault = v @@ -76,83 +46,6 @@ func NewVaultAccessPolicy(resourceId string, args *resourcespb.VaultAccessPolicy } } - return vap, nil -} - -func (r *VaultAccessPolicy) Translate(resources.MultyContext) ([]output.TfBlock, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - //return []output.TfBlock{ - // vault_secret.AwsSsmParameter{ - // AwsResource: &common.AwsResource{ - // TerraformResource: output.TerraformResource{ResourceId: r.GetTfResourceId(cloud)}, - // }, - // Name: fmt.Sprintf("/%s/%s", r.Vault.Name, r.Name), - // ResourceGroup: "SecureString", - // Value: r.Value, - // }, - //} - return nil, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - vaultId, err := r.Vault.GetVaultId() - if err != nil { - return nil, err - } - return []output.TfBlock{ - AzureClientConfig{TerraformDataSource: &output.TerraformDataSource{ResourceId: r.ResourceId}}, - vault_access_policy.AzureKeyVaultAccessPolicy{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - }, - KeyVaultId: vaultId, - AzureKeyVaultAccessPolicyInline: &vault.AzureKeyVaultAccessPolicyInline{ - TenantId: fmt.Sprintf( - "data.azurerm_client_config.%s.tenant_id", r.ResourceId, - ), - ObjectId: "\"" + r.Args.Identity + "\"", - // fixme - AzureKeyVaultPermissions: r.GetAccessPolicyRules(), - }, - }, - }, nil - } - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -// fix return -func (r *VaultAccessPolicy) GetAccessPolicyRules() *vault.AzureKeyVaultPermissions { - switch r.GetCloud() { - case common.AWS: - switch r.Args.Access { - case resourcespb.VaultAccess_READ: - return &vault.AzureKeyVaultPermissions{} - case resourcespb.VaultAccess_WRITE: - return &vault.AzureKeyVaultPermissions{} - case resourcespb.VaultAccess_OWNER: - return &vault.AzureKeyVaultPermissions{} - } - case common.AZURE: - switch r.Args.Access { - case resourcespb.VaultAccess_READ: - return &vault.AzureKeyVaultPermissions{ - CertificatePermissions: []string{}, - KeyPermissions: []string{}, - SecretPermissions: []string{"List", "Get"}, - } - case resourcespb.VaultAccess_WRITE: - return &vault.AzureKeyVaultPermissions{ - CertificatePermissions: []string{}, - KeyPermissions: []string{}, - SecretPermissions: []string{"Set", "Delete"}, - } - case resourcespb.VaultAccess_OWNER: - return &vault.AzureKeyVaultPermissions{ - CertificatePermissions: []string{}, - KeyPermissions: []string{}, - SecretPermissions: []string{"List", "Get", "Set", "Delete", "Recover", "Backup", "Restore", "Purge"}, - } - } - - } return nil } @@ -162,18 +55,3 @@ func (r *VaultAccessPolicy) Validate(ctx resources.MultyContext) (errs []validat } return errs } - -func (r *VaultAccessPolicy) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case common.AWS: - return vault_secret.AwsResourceName, nil - case common.AZURE: - return vault_secret.AzureResourceName, nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} - -func (r *VaultAccessPolicy) GetCloudSpecificLocation() string { - return r.Vault.GetCloudSpecificLocation() -} diff --git a/resources/types/vault_secret.go b/resources/types/vault_secret.go index 97051480..6dae274e 100644 --- a/resources/types/vault_secret.go +++ b/resources/types/vault_secret.go @@ -1,121 +1,46 @@ package types import ( - "fmt" "github.com/multycloud/multy/api/errors" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/vault_secret" "github.com/multycloud/multy/validate" ) -var vaultSecretMetadata = resources.ResourceMetadata[*resourcespb.VaultSecretArgs, *VaultSecret, *resourcespb.VaultSecretResource]{ - CreateFunc: CreateVaultSecret, - UpdateFunc: UpdateVaultSecret, - ReadFromStateFunc: VaultSecretFromState, - ExportFunc: func(r *VaultSecret, _ *resources.Resources) (*resourcespb.VaultSecretArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewVaultSecret, - AbbreviatedName: "kv", -} - type VaultSecret struct { resources.ChildResourceWithId[*Vault, *resourcespb.VaultSecretArgs] Vault *Vault } -func (r *VaultSecret) GetMetadata() resources.ResourceMetadataInterface { - return &vaultSecretMetadata +func (r *VaultSecret) Create(resourceId string, args *resourcespb.VaultSecretArgs, others *resources.Resources) error { + return NewVaultSecret(r, resourceId, args, others) } -func CreateVaultSecret(resourceId string, args *resourcespb.VaultSecretArgs, others *resources.Resources) (*VaultSecret, error) { - return NewVaultSecret(resourceId, args, others) +func (r *VaultSecret) Update(args *resourcespb.VaultSecretArgs, _ *resources.Resources) error { + r.Args = args + return nil } -func UpdateVaultSecret(resource *VaultSecret, vn *resourcespb.VaultSecretArgs, others *resources.Resources) error { - resource.Args = vn - return nil +func (r *VaultSecret) Import(resourceId string, args *resourcespb.VaultSecretArgs, others *resources.Resources) error { + return NewVaultSecret(r, resourceId, args, others) } -func VaultSecretFromState(resource *VaultSecret, _ *output.TfState) (*resourcespb.VaultSecretResource, error) { - return &resourcespb.VaultSecretResource{ - CommonParameters: &commonpb.CommonChildResourceParameters{ - ResourceId: resource.ResourceId, - NeedsUpdate: false, - }, - Name: resource.Args.Name, - Value: resource.Args.Value, - VaultId: resource.Args.VaultId, - }, nil +func (r *VaultSecret) Export(_ *resources.Resources) (*resourcespb.VaultSecretArgs, bool, error) { + return r.Args, true, nil } -func NewVaultSecret(resourceId string, args *resourcespb.VaultSecretArgs, others *resources.Resources) (*VaultSecret, error) { - vap := &VaultSecret{ - ChildResourceWithId: resources.ChildResourceWithId[*Vault, *resourcespb.VaultSecretArgs]{ - ResourceId: resourceId, - Args: args, - }, - } +func NewVaultSecret(vs *VaultSecret, resourceId string, args *resourcespb.VaultSecretArgs, others *resources.Resources) error { v, err := resources.Get[*Vault](resourceId, others, args.VaultId) if err != nil { - return nil, errors.ValidationErrors([]validate.ValidationError{vap.NewValidationError(err, "vault_id")}) - } - vap.Parent = v - vap.Vault = v - return vap, nil -} - -func (r *VaultSecret) Translate(resources.MultyContext) ([]output.TfBlock, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - return []output.TfBlock{ - vault_secret.AwsSsmParameter{ - AwsResource: &common.AwsResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - }, - Name: fmt.Sprintf("/%s/%s", r.Vault.Args.Name, r.Args.Name), - Type: "SecureString", - Value: r.Args.Value, - }, - }, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - vaultId, err := r.Vault.GetVaultId() - if err != nil { - return nil, err - } - return []output.TfBlock{ - vault_secret.AzureKeyVaultSecret{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - Name: r.Args.Name, - }, - KeyVaultId: vaultId, - Value: r.Args.Value, - }, - }, nil + return errors.ValidationError(resources.NewError(err, vs.ResourceId, "vault_id")) } - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) + vs.ChildResourceWithId = resources.NewChildResource(resourceId, v, args) + vs.Parent = v + vs.Vault = v + return nil } func (r *VaultSecret) Validate(ctx resources.MultyContext) (errs []validate.ValidationError) { return errs } - -func (r *VaultSecret) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case common.AWS: - return vault_secret.AwsResourceName, nil - case common.AZURE: - return vault_secret.AzureResourceName, nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} - -func (r *VaultSecret) GetCloudSpecificLocation() string { - return r.Vault.GetCloudSpecificLocation() -} diff --git a/resources/types/virtual_machine.go b/resources/types/virtual_machine.go index 60f5a30b..2154544d 100644 --- a/resources/types/virtual_machine.go +++ b/resources/types/virtual_machine.go @@ -1,25 +1,11 @@ package types import ( - "encoding/json" "fmt" - "github.com/multy-dev/hclencoder" - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" - "github.com/multycloud/multy/flags" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/iam" - "github.com/multycloud/multy/resources/output/network_interface" - "github.com/multycloud/multy/resources/output/network_security_group" - "github.com/multycloud/multy/resources/output/public_ip" - "github.com/multycloud/multy/resources/output/terraform" - "github.com/multycloud/multy/resources/output/virtual_machine" "github.com/multycloud/multy/util" "github.com/multycloud/multy/validate" - "log" - "regexp" ) /* @@ -30,17 +16,6 @@ Azure: To have a private IP by default, if no NIC is passed, one will be created NSG_NIC association */ -var virtualMachineMetadata = resources.ResourceMetadata[*resourcespb.VirtualMachineArgs, *VirtualMachine, *resourcespb.VirtualMachineResource]{ - CreateFunc: CreateVirtualMachine, - UpdateFunc: UpdateVirtualMachine, - ReadFromStateFunc: VirtualMachineFromState, - ExportFunc: func(r *VirtualMachine, _ *resources.Resources) (*resourcespb.VirtualMachineArgs, bool, error) { - return r.Args, true, nil - }, - ImportFunc: NewVirtualMachine, - AbbreviatedName: "vm", -} - type VirtualMachine struct { resources.ResourceWithId[*resourcespb.VirtualMachineArgs] @@ -50,464 +25,76 @@ type VirtualMachine struct { PublicIp *PublicIp } -func (r *VirtualMachine) GetMetadata() resources.ResourceMetadataInterface { - return &virtualMachineMetadata -} - -func CreateVirtualMachine(resourceId string, args *resourcespb.VirtualMachineArgs, others *resources.Resources) (*VirtualMachine, error) { +func (r *VirtualMachine) Create(resourceId string, args *resourcespb.VirtualMachineArgs, others *resources.Resources) error { if args.CommonParameters.ResourceGroupId == "" { subnet, err := resources.Get[*Subnet](resourceId, others, args.SubnetId) if err != nil { - return nil, err + return err } rgId, err := NewRgFromParent("vm", subnet.VirtualNetwork.Args.CommonParameters.ResourceGroupId, others, args.GetCommonParameters().GetLocation(), args.GetCommonParameters().GetCloudProvider()) if err != nil { - return nil, err + return err } args.CommonParameters.ResourceGroupId = rgId } - - return NewVirtualMachine(resourceId, args, others) + return NewVirtualMachine(r, resourceId, args, others) } -func UpdateVirtualMachine(resource *VirtualMachine, vn *resourcespb.VirtualMachineArgs, others *resources.Resources) error { - resource.Args = vn +func (r *VirtualMachine) Update(args *resourcespb.VirtualMachineArgs, _ *resources.Resources) error { + r.Args = args return nil } -func VirtualMachineFromState(resource *VirtualMachine, state *output.TfState) (*resourcespb.VirtualMachineResource, error) { - var err error - var ip string - identityId := "dryrun" - if resource.Args.GeneratePublicIp { - ip = "dryrun" - } - - if !flags.DryRun { - if resource.Args.GeneratePublicIp { - ip, err = getPublicIp(resource.ResourceId, state, resource.Args.CommonParameters.CloudProvider) - if err != nil { - return nil, err - } - } - identityId, err = getIdentityId(resource.ResourceId, state, resource.Args.CommonParameters.CloudProvider) - if err != nil { - return nil, err - } - } - - // TODO: handle default values on create - if resource.Args.ImageReference == nil { - resource.Args.ImageReference = &resourcespb.ImageReference{ - Os: resourcespb.ImageReference_UBUNTU, - Version: "16.04", - } - } - - return &resourcespb.VirtualMachineResource{ - CommonParameters: &commonpb.CommonResourceParameters{ - ResourceId: resource.ResourceId, - ResourceGroupId: resource.Args.CommonParameters.ResourceGroupId, - Location: resource.Args.CommonParameters.Location, - CloudProvider: resource.Args.CommonParameters.CloudProvider, - NeedsUpdate: false, - }, - Name: resource.Args.Name, - NetworkInterfaceIds: resource.Args.NetworkInterfaceIds, - NetworkSecurityGroupIds: resource.Args.NetworkSecurityGroupIds, - VmSize: resource.Args.VmSize, - UserDataBase64: resource.Args.UserDataBase64, - SubnetId: resource.Args.SubnetId, - PublicSshKey: resource.Args.PublicSshKey, - PublicIpId: resource.Args.PublicIpId, - GeneratePublicIp: resource.Args.GeneratePublicIp, - ImageReference: resource.Args.ImageReference, - AwsOverride: resource.Args.AwsOverride, - AzureOverride: resource.Args.AzureOverride, - - PublicIp: ip, - IdentityId: identityId, - }, nil +func (r *VirtualMachine) Import(resourceId string, args *resourcespb.VirtualMachineArgs, others *resources.Resources) error { + return NewVirtualMachine(r, resourceId, args, others) } -func getPublicIp(resourceId string, state *output.TfState, cloud commonpb.CloudProvider) (string, error) { - switch cloud { - case commonpb.CloudProvider_AWS: - values, err := state.GetValues(virtual_machine.AwsEC2{}, resourceId) - if err != nil { - return "", err - } - return values["public_ip"].(string), nil - case commonpb.CloudProvider_AZURE: - values, err := state.GetValues(public_ip.AzurePublicIp{}, resourceId) - if err != nil { - return "", err - } - return values["ip_address"].(string), nil - } - - return "", fmt.Errorf("unknown cloud: %s", cloud.String()) +func (r *VirtualMachine) Export(_ *resources.Resources) (*resourcespb.VirtualMachineArgs, bool, error) { + return r.Args, true, nil } -func getIdentityId(resourceId string, state *output.TfState, cloud commonpb.CloudProvider) (string, error) { - switch cloud { - case commonpb.CloudProvider_AWS: - values, err := state.GetValues(iam.AwsIamRole{}, resourceId) - if err != nil { - return "", err - } - return values["id"].(string), nil - case commonpb.CloudProvider_AZURE: - values, err := state.GetValues(virtual_machine.AzureVirtualMachine{}, resourceId) - if err != nil { - return "", err - } - return values["identity"].([]interface{})[0].(map[string]interface{})["principal_id"].(string), nil - } - - return "", fmt.Errorf("unknown cloud: %s", cloud.String()) -} - -func NewVirtualMachine(resourceId string, args *resourcespb.VirtualMachineArgs, others *resources.Resources) (*VirtualMachine, error) { +func NewVirtualMachine(vm *VirtualMachine, resourceId string, args *resourcespb.VirtualMachineArgs, others *resources.Resources) error { networkInterfaces, err := util.MapSliceValuesErr(args.NetworkInterfaceIds, func(id string) (*NetworkInterface, error) { return resources.Get[*NetworkInterface](resourceId, others, id) }) if err != nil { - return nil, err + return err } + vm.NetworkInterface = networkInterfaces + networkSecurityGroups, err := util.MapSliceValuesErr(args.NetworkSecurityGroupIds, func(id string) (*NetworkSecurityGroup, error) { return resources.Get[*NetworkSecurityGroup](resourceId, others, id) }) if err != nil { - return nil, err + return err } + vm.NetworkSecurityGroups = networkSecurityGroups + subnet, err := resources.Get[*Subnet](resourceId, others, args.SubnetId) if err != nil { - return nil, err + return err } + vm.Subnet = subnet + publicIp, _, err := resources.GetOptional[*PublicIp](resourceId, others, args.PublicIpId) if err != nil { - return nil, err + return err } + vm.PublicIp = publicIp + if args.GetImageReference() == nil { args.ImageReference = &resourcespb.ImageReference{ Os: resourcespb.ImageReference_UBUNTU, Version: "16.04", } } - return &VirtualMachine{ - ResourceWithId: resources.ResourceWithId[*resourcespb.VirtualMachineArgs]{ - ResourceId: resourceId, - Args: args, - }, - NetworkInterface: networkInterfaces, - NetworkSecurityGroups: networkSecurityGroups, - Subnet: subnet, - PublicIp: publicIp, - }, nil -} - -type AwsCallerIdentityData struct { - *output.TerraformDataSource `hcl:",squash" default:"name=aws_caller_identity"` -} - -type AwsRegionData struct { - *output.TerraformDataSource `hcl:",squash" default:"name=aws_region"` -} - -func (r *VirtualMachine) Translate(ctx resources.MultyContext) ([]output.TfBlock, error) { - if r.Args.UserDataBase64 != "" { - r.Args.UserDataBase64 = fmt.Sprintf(hclencoder.EscapeString(r.Args.UserDataBase64)) - } - - subnetId, err := resources.GetMainOutputId(r.Subnet) - if err != nil { - return nil, err - } - - if r.GetCloud() == commonpb.CloudProvider_AWS { - var awsResources []output.TfBlock - var ec2NicIds []virtual_machine.AwsEc2NetworkInterface - for i, ni := range r.NetworkInterface { - niId, err := resources.GetMainOutputId(ni) - if err != nil { - return nil, err - } - ec2NicIds = append( - ec2NicIds, virtual_machine.AwsEc2NetworkInterface{ - NetworkInterfaceId: niId, - DeviceIndex: i, - }, - ) - } - - nsgIds, err := util.MapSliceValuesErr(r.NetworkSecurityGroups, func(v *NetworkSecurityGroup) (string, error) { - return resources.GetMainOutputId(v) - }) - if err != nil { - return nil, err - } - - var vmSize string - if r.Args.AwsOverride.GetInstanceType() != "" { - vmSize = r.Args.AwsOverride.GetInstanceType() - } else { - vmSize = common.VMSIZE[r.Args.VmSize][r.GetCloud()] - } - - ec2 := virtual_machine.AwsEC2{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - InstanceType: vmSize, - AssociatePublicIpAddress: r.Args.GeneratePublicIp, - UserDataBase64: r.Args.UserDataBase64, - SubnetId: subnetId, - NetworkInterfaces: ec2NicIds, - SecurityGroupIds: nsgIds, - } - - if len(ec2NicIds) != 0 { - ec2.SubnetId = "" - ec2.AssociatePublicIpAddress = false - } - - iamRole := iam.AwsIamRole{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - Name: r.GetAwsIdentity(), - AssumeRolePolicy: iam.NewAssumeRolePolicy("ec2.amazonaws.com"), - } - - if vault := getVaultAssociatedIdentity(ctx, r.GetAwsIdentity()); vault != nil { - awsResources = append(awsResources, - AwsCallerIdentityData{TerraformDataSource: &output.TerraformDataSource{ResourceId: r.ResourceId}}, - AwsRegionData{TerraformDataSource: &output.TerraformDataSource{ResourceId: r.ResourceId}}) - - policy, err := json.Marshal(iam.AwsIamPolicy{ - Statement: []iam.AwsIamPolicyStatement{{ - Action: []string{"ssm:GetParameter*"}, - Effect: "Allow", - Resource: fmt.Sprintf("arn:aws:ssm:${data.aws_region.%s.name}:${data.aws_caller_identity.%s.account_id}:parameter/%s/*", r.ResourceId, r.ResourceId, vault.Args.Name), - }, { - Action: []string{"ssm:DescribeParameters"}, - Effect: "Allow", - Resource: "*", - }}, - Version: "2012-10-17", - }) - - if err != nil { - return nil, fmt.Errorf("unable to encode aws policy: %s", err.Error()) - } - - iamRole.InlinePolicy = iam.AwsIamRoleInlinePolicy{ - Name: "vault_policy", - // we need to have an expression here because we use template strings within the policy json - Policy: fmt.Sprintf("\"%s\"", hclencoder.EscapeString(string(policy))), - } - } - - iamInstanceProfile := iam.AwsIamInstanceProfile{ - AwsResource: &common.AwsResource{TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}}, - Name: r.GetAwsIdentity(), - Role: fmt.Sprintf( - "%s.%s.name", output.GetResourceName(iam.AwsIamRole{}), iamRole.ResourceId, - ), - } - - ec2.IamInstanceProfile = iamInstanceProfile.GetId() - - awsResources = append(awsResources, - iamInstanceProfile, - iamRole, - ) - - // this gives permission to write cloudwatch logs - - // adding ssh key to r requires aws key pair resource - // key pair will be added and referenced via key_name parameter - if r.Args.PublicSshKey != "" { - keyPair := virtual_machine.AwsKeyPair{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - KeyName: fmt.Sprintf("%s_multy", r.ResourceId), - PublicKey: r.Args.PublicSshKey, - } - ec2.KeyName, err = r.GetAssociatedKeyPairName() - if err != nil { - return nil, err - } - awsResources = append(awsResources, keyPair) - } - - awsAmi, err := virtual_machine.LatestAwsAmi(r.Args.ImageReference, r.ResourceId) - if err != nil { - return nil, err - } - - ec2.Ami = fmt.Sprintf("%s.id", awsAmi.GetFullResourceRef()) - - awsResources = append(awsResources, awsAmi, ec2) - return awsResources, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - // TODO validate that NIC is on the same VNET - var azResources []output.TfBlock - rgName := GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId) - nicIds, err := util.MapSliceValuesErr(r.NetworkInterface, func(v *NetworkInterface) (string, error) { - return resources.GetMainOutputId(v) - }) - if err != nil { - return nil, err - } - - if len(r.NetworkInterface) == 0 { - nic := network_interface.AzureNetworkInterface{ - AzResource: common.NewAzResource( - r.ResourceId, r.Args.Name, rgName, - r.GetCloudSpecificLocation(), - ), - IpConfigurations: []network_interface.AzureIpConfiguration{{ - Name: "internal", // this name shouldn't be r.name - PrivateIpAddressAllocation: "Dynamic", - SubnetId: subnetId, - Primary: true, - }}, - } - - if r.Args.GeneratePublicIp { - pIp := public_ip.AzurePublicIp{ - AzResource: common.NewAzResource( - r.ResourceId, r.Args.Name, rgName, - r.GetCloudSpecificLocation(), - ), - AllocationMethod: "Static", - } - nic.IpConfigurations = []network_interface.AzureIpConfiguration{{ - Name: "external", // this name shouldn't be r.name - PrivateIpAddressAllocation: "Dynamic", - SubnetId: subnetId, - PublicIpAddressId: pIp.GetId(), - Primary: true, - }} - azResources = append(azResources, &pIp) - } - azResources = append(azResources, nic) - nicIds = append(nicIds, fmt.Sprintf("%s.%s.id", output.GetResourceName(nic), nic.ResourceId)) - } - - // TODO change this to multy nsg_nic_attachment resource and use aws_network_interface_sg_attachment - if len(r.NetworkSecurityGroups) != 0 { - for _, nsg := range r.NetworkSecurityGroups { - for _, nicId := range nicIds { - nsgId, err := resources.GetMainOutputId(nsg) - if err != nil { - return nil, err - } - azResources = append( - azResources, network_security_group.AzureNetworkInterfaceSecurityGroupAssociation{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ - ResourceId: r.ResourceId, - }, - }, - NetworkInterfaceId: nicId, - NetworkSecurityGroupId: nsgId, - }, - ) - } - } - } - - // if ssh key is specified, add admin_ssh param - // ssh authentication will replace password authentication - // if no ssh key is passed, password is required - // random_password will be used - var azureSshKey virtual_machine.AzureAdminSshKey - var vmPassword string - disablePassAuth := false - if r.Args.PublicSshKey != "" { - azureSshKey = virtual_machine.AzureAdminSshKey{ - Username: "adminuser", - PublicKey: r.Args.PublicSshKey, - } - disablePassAuth = true - } else { - randomPassword := terraform.RandomPassword{ - TerraformResource: &output.TerraformResource{ - ResourceId: r.ResourceId, - }, - Length: 16, - Special: true, - Upper: true, - Lower: true, - Number: true, - } - vmPassword = randomPassword.GetResult() - azResources = append(azResources, randomPassword) - } - - reg, err := regexp.Compile("[^a-zA-Z0-9]+") - if err != nil { - log.Fatal(err) - } - computerName := reg.ReplaceAllString(r.Args.Name, "") - - sourceImg, err := virtual_machine.GetLatestAzureSourceImageReference(r.Args.ImageReference) - if err != nil { - return nil, err - } - - var vmSize string - if r.Args.AzureOverride.GetSize() != "" { - vmSize = r.Args.AzureOverride.GetSize() - } else { - vmSize = common.VMSIZE[r.Args.VmSize][r.GetCloud()] - } - - azResources = append( - azResources, virtual_machine.AzureVirtualMachine{ - AzResource: &common.AzResource{ - TerraformResource: output.TerraformResource{ResourceId: r.ResourceId}, - ResourceGroupName: rgName, - Name: r.Args.Name, - }, - Location: r.GetCloudSpecificLocation(), - Size: vmSize, - NetworkInterfaceIds: nicIds, - CustomData: r.Args.UserDataBase64, - OsDisk: virtual_machine.AzureOsDisk{ - Caching: "None", - StorageAccountType: "Standard_LRS", - }, - AdminUsername: "adminuser", - AdminPassword: vmPassword, - AdminSshKey: azureSshKey, - SourceImageReference: sourceImg, - DisablePasswordAuthentication: disablePassAuth, - Identity: virtual_machine.AzureIdentity{Type: "SystemAssigned"}, - ComputerName: computerName, - }, - ) - - return azResources, nil - } - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -func (r *VirtualMachine) GetAssociatedKeyPairName() (string, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - return fmt.Sprintf("%s.%s.key_name", virtual_machine.AwsKeyPairResourceName, r.ResourceId), nil + vm.ResourceWithId = resources.ResourceWithId[*resourcespb.VirtualMachineArgs]{ + ResourceId: resourceId, + Args: args, } - return "", fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} -// check if VM identity is associated with vault (vault_access_policy) -// get Vault name by vault_access_policy that uses VM identity -func getVaultAssociatedIdentity(ctx resources.MultyContext, identity string) *Vault { - for _, resource := range resources.GetAllResources[*VaultAccessPolicy](ctx) { - if identity == resource.Args.Identity { - return resource.Vault - } - } return nil } @@ -525,21 +112,6 @@ func (r *VirtualMachine) Validate(ctx resources.MultyContext) (errs []validate.V return errs } -func (r *VirtualMachine) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case common.AWS: - return virtual_machine.AwsResourceName, nil - case common.AZURE: - return virtual_machine.AzureResourceName, nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} - func (r *VirtualMachine) GetAwsIdentity() string { - return getIdentity(r.ResourceId) -} - -func getIdentity(resourceId string) string { - return fmt.Sprintf("multy-vm-%s-role", resourceId) + return fmt.Sprintf("multy-vm-%s-role", r.ResourceId) } diff --git a/resources/types/virtual_network.go b/resources/types/virtual_network.go index 94863065..46496557 100644 --- a/resources/types/virtual_network.go +++ b/resources/types/virtual_network.go @@ -1,17 +1,8 @@ package types import ( - "fmt" - - "github.com/multycloud/multy/api/proto/commonpb" "github.com/multycloud/multy/api/proto/resourcespb" - "github.com/multycloud/multy/flags" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/common" - "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/output/network_security_group" - "github.com/multycloud/multy/resources/output/route_table" - "github.com/multycloud/multy/resources/output/virtual_network" "github.com/multycloud/multy/validate" ) @@ -23,175 +14,38 @@ AZ: Route table created to restrict traffic on vnet */ -var virtualNetworkMetadata = resources.ResourceMetadata[*resourcespb.VirtualNetworkArgs, *VirtualNetwork, *resourcespb.VirtualNetworkResource]{ - CreateFunc: CreateVirtualNetwork, - UpdateFunc: UpdateVirtualNetwork, - ReadFromStateFunc: VirtualNetworkFromState, - ImportFunc: NewVirtualNetwork, - ExportFunc: ExportVirtualNetwork, - AbbreviatedName: "vn", -} - type VirtualNetwork struct { resources.ResourceWithId[*resourcespb.VirtualNetworkArgs] } -func CreateVirtualNetwork(resourceId string, args *resourcespb.VirtualNetworkArgs, others *resources.Resources) (*VirtualNetwork, error) { +func (r *VirtualNetwork) Create(resourceId string, args *resourcespb.VirtualNetworkArgs, others *resources.Resources) error { if args.GetCommonParameters().GetResourceGroupId() == "" { rgId, err := NewRg("vn", others, args.GetCommonParameters().GetLocation(), args.GetCommonParameters().GetCloudProvider()) if err != nil { - return nil, err + return err } args.CommonParameters.ResourceGroupId = rgId } - return NewVirtualNetwork(resourceId, args, others) + return NewVirtualNetwork(r, resourceId, args) } -func UpdateVirtualNetwork(resource *VirtualNetwork, vn *resourcespb.VirtualNetworkArgs, others *resources.Resources) error { - resource.Args = vn +func (r *VirtualNetwork) Update(args *resourcespb.VirtualNetworkArgs, _ *resources.Resources) error { + r.Args = args return nil } -func NewVirtualNetwork(resourceId string, vn *resourcespb.VirtualNetworkArgs, _ *resources.Resources) (*VirtualNetwork, error) { - return &VirtualNetwork{ - ResourceWithId: resources.ResourceWithId[*resourcespb.VirtualNetworkArgs]{ - ResourceId: resourceId, - Args: vn, - }, - }, nil +func (r *VirtualNetwork) Import(resourceId string, args *resourcespb.VirtualNetworkArgs, _ *resources.Resources) error { + return NewVirtualNetwork(r, resourceId, args) } -func ExportVirtualNetwork(r *VirtualNetwork, _ *resources.Resources) (*resourcespb.VirtualNetworkArgs, bool, error) { +func (r *VirtualNetwork) Export(_ *resources.Resources) (*resourcespb.VirtualNetworkArgs, bool, error) { return r.Args, true, nil } -func VirtualNetworkFromState(r *VirtualNetwork, state *output.TfState) (*resourcespb.VirtualNetworkResource, error) { - if flags.DryRun { - return &resourcespb.VirtualNetworkResource{ - CommonParameters: &commonpb.CommonResourceParameters{ - ResourceId: r.ResourceId, - ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, - Location: r.Args.CommonParameters.Location, - CloudProvider: r.Args.CommonParameters.CloudProvider, - NeedsUpdate: false, - }, - Name: r.Args.Name, - CidrBlock: r.Args.CidrBlock, - }, nil - } - out := new(resourcespb.VirtualNetworkResource) - out.CommonParameters = &commonpb.CommonResourceParameters{ - ResourceId: r.ResourceId, - ResourceGroupId: r.Args.CommonParameters.ResourceGroupId, - Location: r.Args.CommonParameters.Location, - CloudProvider: r.GetCloud(), - NeedsUpdate: false, - } - - id, err := resources.GetMainOutputRef(r) - if err != nil { - return nil, err - } - - switch r.GetCloud() { - case common.AWS: - stateResource, err := output.GetParsed[virtual_network.AwsVpc](state, id) - if err != nil { - return nil, err - } - out.Name = stateResource.AwsResource.Tags["Name"] - out.CidrBlock = stateResource.CidrBlock - case common.AZURE: - stateResource, err := output.GetParsed[virtual_network.AzureVnet](state, id) - if err != nil { - return nil, err - } - out.Name = stateResource.Name - out.CidrBlock = stateResource.AddressSpace[0] - } - - return out, nil -} - -func (r *VirtualNetwork) GetMetadata() resources.ResourceMetadataInterface { - return &virtualNetworkMetadata -} - -func (r *VirtualNetwork) Translate(resources.MultyContext) ([]output.TfBlock, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - vpc := virtual_network.AwsVpc{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - CidrBlock: r.Args.CidrBlock, - EnableDnsHostnames: true, - } - // TODO make conditional on route_table_association with Internet Destination - igw := virtual_network.AwsInternetGateway{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - VpcId: r.GetVirtualNetworkId(), - } - allowAllSgRule := []network_security_group.AwsSecurityGroupRule{{ - Protocol: "-1", - FromPort: 0, - ToPort: 0, - Self: true, - }} - sg := network_security_group.AwsDefaultSecurityGroup{ - AwsResource: common.NewAwsResource(r.ResourceId, r.Args.Name), - VpcId: r.GetVirtualNetworkId(), - Ingress: allowAllSgRule, - Egress: allowAllSgRule, - } - return []output.TfBlock{ - vpc, - igw, - sg, - }, nil - } else if r.GetCloud() == commonpb.CloudProvider_AZURE { - return []output.TfBlock{virtual_network.AzureVnet{ - AzResource: common.NewAzResource( - r.ResourceId, r.Args.Name, GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId), - r.GetCloudSpecificLocation(), - ), - AddressSpace: []string{r.Args.CidrBlock}, - }, route_table.AzureRouteTable{ - AzResource: common.NewAzResource( - r.ResourceId, r.Args.Name, GetResourceGroupName(r.Args.CommonParameters.ResourceGroupId), - r.GetCloudSpecificLocation(), - ), - Routes: []route_table.AzureRouteTableRoute{{ - Name: "local", - AddressPrefix: "0.0.0.0/0", - NextHopType: "VnetLocal", - }}, - }}, nil - } - - return nil, fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -func (r *VirtualNetwork) GetVirtualNetworkId() string { - t, _ := r.GetMainResourceName() - return fmt.Sprintf("%s.%s.id", t, r.ResourceId) -} - -func (r *VirtualNetwork) GetVirtualNetworkName() string { - t, _ := r.GetMainResourceName() - return fmt.Sprintf("%s.%s.name", t, r.ResourceId) -} - -func (r *VirtualNetwork) GetAssociatedRouteTableId() (string, error) { - if r.GetCloud() == commonpb.CloudProvider_AZURE { - return fmt.Sprintf("%s.%s.id", route_table.AzureResourceName, r.ResourceId), nil - } - return "", fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) -} - -func (r *VirtualNetwork) GetAssociatedInternetGateway() (string, error) { - if r.GetCloud() == commonpb.CloudProvider_AWS { - return fmt.Sprintf("%s.%s.id", virtual_network.AwsInternetGatewayName, r.ResourceId), nil - } - return "", fmt.Errorf("cloud %s is not supported for this resource type ", r.GetCloud().String()) +func NewVirtualNetwork(r *VirtualNetwork, resourceId string, vn *resourcespb.VirtualNetworkArgs) error { + r.ResourceWithId = resources.NewResource(resourceId, vn) + return nil } // TODO validate commonparams @@ -210,17 +64,6 @@ func (r *VirtualNetwork) Validate(ctx resources.MultyContext) (errs []validate.V return errs } -func (r *VirtualNetwork) GetMainResourceName() (string, error) { - switch r.GetCloud() { - case commonpb.CloudProvider_AWS: - return virtual_network.AwsResourceName, nil - case commonpb.CloudProvider_AZURE: - return virtual_network.AzureResourceName, nil - default: - return "", fmt.Errorf("unknown cloud %s", r.GetCloud().String()) - } -} - /* Virtual Network is a private address space when resources can be placed. diff --git a/test/config_translate_test.go b/test/config_translate_test.go index 4739ab65..53211f8c 100644 --- a/test/config_translate_test.go +++ b/test/config_translate_test.go @@ -10,7 +10,7 @@ import ( "github.com/multycloud/multy/api/deploy" "github.com/multycloud/multy/api/proto/configpb" "github.com/multycloud/multy/resources" - "github.com/multycloud/multy/resources/types" + "github.com/multycloud/multy/resources/types/metadata" "github.com/multycloud/multy/validate" "github.com/zclconf/go-cty/cty" "google.golang.org/grpc/status" @@ -94,7 +94,7 @@ func testConfig(testFiles TestConfigFiles, t *testing.T) { t.Fatalf("unable to parse input file: %v", err) } - mconfig, err := resources.LoadConfig(&c, types.Metadatas) + mconfig, err := resources.LoadConfig(&c, metadata.Metadatas) if err != nil { t.Fatalf("error loading config: %v", err) } diff --git a/test/deploy/deploy_test.go b/test/deploy/deploy_test.go index 497613a4..3c7859b0 100644 --- a/test/deploy/deploy_test.go +++ b/test/deploy/deploy_test.go @@ -11,7 +11,7 @@ import ( "github.com/multycloud/multy/flags" "github.com/multycloud/multy/resources" "github.com/multycloud/multy/resources/output" - "github.com/multycloud/multy/resources/types" + metadata2 "github.com/multycloud/multy/resources/types/metadata" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "google.golang.org/grpc/metadata" @@ -64,7 +64,7 @@ func TestDeploy_rollbacksIfSomethingFails(t *testing.T) { UserId: "test", Resources: nil, ResourceCounter: 1, - }, types.Metadatas) + }, metadata2.Metadatas) if err != nil { t.Fatalf("can't load config, %s", err) } @@ -119,7 +119,7 @@ func TestDeploy_callsTfApply(t *testing.T) { UserId: "test", Resources: nil, ResourceCounter: 1, - }, types.Metadatas) + }, metadata2.Metadatas) if err != nil { t.Fatalf("can't load config, %s", err) } @@ -168,7 +168,7 @@ func TestDeploy_onlyAffectedResources(t *testing.T) { UserId: "test", Resources: nil, ResourceCounter: 1, - }, types.Metadatas) + }, metadata2.Metadatas) if err != nil { t.Fatalf("can't load config, %s", err) } @@ -235,7 +235,7 @@ func TestRefresh(t *testing.T) { UserId: "test", Resources: nil, ResourceCounter: 1, - }, types.Metadatas) + }, metadata2.Metadatas) if err != nil { t.Fatalf("can't load config, %s", err) } diff --git a/test/resources/resource_metadata_test.go b/test/resources/resource_metadata_test.go index ab9806c2..d6cb4b1e 100644 --- a/test/resources/resource_metadata_test.go +++ b/test/resources/resource_metadata_test.go @@ -6,6 +6,7 @@ import ( "github.com/multycloud/multy/api/proto/resourcespb" "github.com/multycloud/multy/resources" "github.com/multycloud/multy/resources/types" + "github.com/multycloud/multy/resources/types/metadata" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" @@ -29,7 +30,7 @@ func TestResourceMetadataImport(t *testing.T) { }, } - config, err := resources.LoadConfig(c, types.Metadatas) + config, err := resources.LoadConfig(c, metadata.Metadatas) if err != nil { t.Fatalf("unable to load config: %s", err) } @@ -69,7 +70,7 @@ func TestResourceMetadataImport_returnsAffectedResources(t *testing.T) { }, } - config, err := resources.LoadConfig(c, types.Metadatas) + config, err := resources.LoadConfig(c, metadata.Metadatas) if err != nil { t.Fatalf("unable to load config: %s", err) } @@ -85,7 +86,7 @@ func TestResourceMetadataCreate_createsResourceGroup(t *testing.T) { c := &configpb.Config{ UserId: "test-user", } - config, err := resources.LoadConfig(c, types.Metadatas) + config, err := resources.LoadConfig(c, metadata.Metadatas) if err != nil { t.Fatalf("unable to load config: %s", err) } @@ -122,16 +123,16 @@ func TestResourceMetadataCreate_createsResourceGroup(t *testing.T) { if !ok { t.Fatalf("resource should of type resource group, but is %t", config.Resources.ResourceMap[rgId]) } - assert.Equal(t, rgId, rg.Name) - assert.Equal(t, args.CommonParameters.Location, rg.Location) - assert.Equal(t, args.CommonParameters.CloudProvider, rg.Cloud) + assert.Equal(t, rgId, rg.Args.Name) + assert.Equal(t, args.CommonParameters.Location, rg.Args.CommonParameters.Location) + assert.Equal(t, args.CommonParameters.CloudProvider, rg.Args.CommonParameters.CloudProvider) } func TestResourceMetadataDelete_deletesResourceGroup(t *testing.T) { c := &configpb.Config{ UserId: "test-user", } - config, err := resources.LoadConfig(c, types.Metadatas) + config, err := resources.LoadConfig(c, metadata.Metadatas) if err != nil { t.Fatalf("unable to load config: %s", err) } @@ -170,7 +171,7 @@ func TestResourceMetadataExport(t *testing.T) { c := &configpb.Config{ UserId: "test-user", } - config, err := resources.LoadConfig(c, types.Metadatas) + config, err := resources.LoadConfig(c, metadata.Metadatas) if err != nil { t.Fatalf("unable to load config: %s", err) } @@ -204,7 +205,7 @@ func TestResourceMetadataUpdateMultyResourceGroups(t *testing.T) { c := &configpb.Config{ UserId: "test-user", } - config, err := resources.LoadConfig(c, types.Metadatas) + config, err := resources.LoadConfig(c, metadata.Metadatas) if err != nil { t.Fatalf("unable to load config: %s", err) } @@ -282,7 +283,7 @@ func TestResourceMetadataUpdate(t *testing.T) { }, } - config, err := resources.LoadConfig(c, types.Metadatas) + config, err := resources.LoadConfig(c, metadata.Metadatas) if err != nil { t.Fatalf("unable to load config: %s", err) } @@ -339,7 +340,7 @@ func TestResourceMetadataCreate_shouldNotCreateDuplicateRg(t *testing.T) { }, } - config, err := resources.LoadConfig(c, types.Metadatas) + config, err := resources.LoadConfig(c, metadata.Metadatas) if err != nil { t.Fatalf("unable to load config: %s", err) }