From cc94d4a8e0e4db76e3d3bd11468353d7a87da1b3 Mon Sep 17 00:00:00 2001 From: sivchari Date: Thu, 24 Apr 2025 02:54:09 +0900 Subject: [PATCH] handle plugins Signed-off-by: sivchari --- .github/workflows/lint-test.yml | 2 +- .golangci.yml | 118 ++++++------- docs/data-sources/application.md | 12 +- docs/data-sources/piped.md | 12 +- docs/resources/application.md | 14 +- docs/resources/piped.md | 2 +- go.mod | 20 +-- go.sum | 49 ++++-- internal/provider/data_source_application.go | 77 +++++++-- .../provider/data_source_application_test.go | 75 +++++++- internal/provider/data_source_piped.go | 87 +++++++--- internal/provider/data_source_piped_test.go | 78 ++++++++- internal/provider/mock/mock.go | 160 ++++++++++-------- internal/provider/resource_application.go | 147 +++++++++++++--- .../provider/resource_application_test.go | 125 +++++++++++++- internal/provider/resource_piped_test.go | 2 +- 16 files changed, 751 insertions(+), 229 deletions(-) diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml index 2a54caf..497d85b 100644 --- a/.github/workflows/lint-test.yml +++ b/.github/workflows/lint-test.yml @@ -20,7 +20,7 @@ jobs: go-version: 'stable' cache: true - name: Install golangci-lint - run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + run: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest - name: Run golangci-lint run: golangci-lint run ./... test: diff --git a/.golangci.yml b/.golangci.yml index 5b03cf7..7e68fa6 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,80 +1,68 @@ -run: - timeout: 5m - -linters-settings: - govet: - enable-all: true - disable: - - shadow - misspell: - locale: US - lll: - line-length: 160 - revive: - rules: - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-naming - - name: var-naming - forbidigo: - forbid: - - t\.Fatal.* - - fmt\.Print.* - +version: "2" linters: - disable-all: true + default: none enable: - # Errcheck is a program for checking for unchecked errors in go programs. - # These unchecked errors can be critical bugs in some cases - - errcheck - # Linter for Go source code that specializes in simplifying a code - - gosimple - # Vet examines Go source code and reports suspicious constructs, - # such as Printf calls whose arguments do not align with the format string - - govet - # Forbids identifiers - - forbidigo - # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - - staticcheck - # Like the front-end of a Go compiler, parses and type-checks Go code - - typecheck - # Checks Go code for unused constants, variables, functions and types - - unused - # checks whether HTTP response body is closed successfully - bodyclose - # containedctx is a linter that detects struct contained context.Context field - containedctx - # Checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error. - - errname - # A linter detects places where loop variables are copied. - copyloopvar - # Gofumpt checks whether code was gofumpt-ed. - - gofumpt - # Reports long lines + - errcheck + - errname + - forbidigo + - govet - lll - # Finds commonly misspelled English words in comments - misspell - # Finds naked returns in functions greater than a specified function length - nakedret - # paralleltest detects missing usage of t.Parallel() method in your Go test - paralleltest - # Finds slice declarations that could potentially be pre-allocated - prealloc - # find code that shadows one of Go's predeclared identifiers - predeclared - # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint - revive - # Stylecheck is a replacement for golint - - stylecheck - # Reports uses of functions with replacement inside the testing package. + - staticcheck + - unused - usetesting - + settings: + forbidigo: + forbid: + - pattern: t\.Fatal.* + - pattern: fmt\.Print.* + govet: + disable: + - shadow + enable-all: true + lll: + line-length: 165 + misspell: + locale: US + revive: + rules: + - name: var-naming + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - govet + path: \.go + text: 'fieldalignment:' + - linters: + - staticcheck + path: \.go + text: 'SA1019' + paths: + - third_party$ + - builtin$ + - examples$ issues: - exclude-use-default: true - max-per-linter: 0 max-same-issues: 0 - exclude: [] - exclude-rules: - # All - - path: \.go - text: "fieldalignment:" - linters: - - govet +formatters: + enable: + - gofumpt + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/docs/data-sources/application.md b/docs/data-sources/application.md index 7fe3572..6f7abcd 100644 --- a/docs/data-sources/application.md +++ b/docs/data-sources/application.md @@ -26,7 +26,8 @@ PipeCD application resource. - `kind` (String) The kind of application. - `name` (String) The application name. - `piped_id` (String) The ID of piped that should handle this application. -- `platform_provider` (String) The platform provider name. One of the registered providers in the piped configuration. The previous name of this field is cloud-provider. +- `platform_provider` (String, Deprecated) The platform provider name. One of the registered providers in the piped configuration. The previous name of this field is cloud-provider. +- `plugins` (Attributes List) The list of plugins that this application uses. (see [below for nested schema](#nestedatt--plugins)) - `project_id` (String) @@ -39,3 +40,12 @@ Read-Only: - `path` (String) The relative path from the root of repository to the application directory. - `remote` (String) - `repository_id` (String) The repository ID. One the registered repositories in the piped configuration. + + + +### Nested Schema for `plugins` + +Read-Only: + +- `deploy_targets` (List of String) The list of deploy targets that this plugin uses. +- `name` (String) The name of the plugin. diff --git a/docs/data-sources/piped.md b/docs/data-sources/piped.md index 5d5ca37..5b7ec6a 100644 --- a/docs/data-sources/piped.md +++ b/docs/data-sources/piped.md @@ -20,7 +20,8 @@ PipeCD pied data source. - `description` (String) - `id` (String) The ID of this resource. - `name` (String) -- `platform_providers` (Attributes List) (see [below for nested schema](#nestedatt--platform_providers)) +- `platform_providers` (Attributes List, Deprecated) (see [below for nested schema](#nestedatt--platform_providers)) +- `plugins` (Attributes List) The list of plugins that this piped uses. (see [below for nested schema](#nestedatt--plugins)) - `project_id` (String) - `repositories` (Attributes List) (see [below for nested schema](#nestedatt--repositories)) @@ -33,6 +34,15 @@ Read-Only: - `type` (String) + +### Nested Schema for `plugins` + +Read-Only: + +- `deploy_targets` (List of String) The list of deploy targets that this plugin uses. +- `name` (String) The name of the plugin. + + ### Nested Schema for `repositories` diff --git a/docs/resources/application.md b/docs/resources/application.md index d150248..640280c 100644 --- a/docs/resources/application.md +++ b/docs/resources/application.md @@ -21,11 +21,12 @@ PipeCD application resource. - `kind` (String) The kind of application. - `name` (String) The application name. - `piped_id` (String) The ID of piped that should handle this application. -- `platform_provider` (String) The platform provider name. One of the registered providers in the piped configuration. The previous name of this field is cloud-provider. ### Optional - `description` (String) The description of the application. +- `platform_provider` (String, Deprecated) The platform provider name. One of the registered providers in the piped configuration. The previous name of this field is cloud-provider. +- `plugins` (Attributes List) The list of plugins that this application uses. (see [below for nested schema](#nestedatt--plugins)) ### Read-Only @@ -41,6 +42,13 @@ Required: Optional: -- `branch` (String) - `filename` (String) The configuration file name. (default "app.pipecd.yaml") -- `remote` (String) + + + +### Nested Schema for `plugins` + +Required: + +- `deploy_targets` (List of String) The list of deploy targets that this plugin uses. +- `name` (String) The name of the plugin. diff --git a/docs/resources/piped.md b/docs/resources/piped.md index 762a108..9218e2d 100644 --- a/docs/resources/piped.md +++ b/docs/resources/piped.md @@ -25,5 +25,5 @@ PipeCD piped resource. ### Read-Only -- `api_key` (String) The API key of the piped. +- `api_key` (String, Sensitive) The API key of the piped. - `id` (String) The ID of piped that should handle this application. diff --git a/go.mod b/go.mod index 0c362fe..380d3a5 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,16 @@ module github.com/pipe-cd/terraform-provider-pipecd -go 1.23.0 - -toolchain go1.24.1 +go 1.24.1 require ( - github.com/golang/mock v1.6.0 github.com/hashicorp/terraform-plugin-docs v0.21.0 github.com/hashicorp/terraform-plugin-framework v1.14.1 github.com/hashicorp/terraform-plugin-framework-validators v0.17.0 github.com/hashicorp/terraform-plugin-go v0.26.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-testing v1.12.0 - github.com/pipe-cd/pipecd v0.50.2 + github.com/pipe-cd/pipecd v0.51.2 + go.uber.org/mock v0.5.0 google.golang.org/grpc v1.72.0 ) @@ -32,8 +30,8 @@ require ( github.com/coreos/go-oidc/v3 v3.11.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/fatih/color v1.16.0 // indirect - github.com/go-jose/go-jose/v4 v4.0.4 // indirect - github.com/golang-jwt/jwt v3.2.1+incompatible // indirect + github.com/go-jose/go-jose/v4 v4.0.5 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect @@ -78,13 +76,13 @@ require ( github.com/yuin/goldmark-meta v1.1.0 // indirect github.com/zclconf/go-cty v1.16.2 // indirect go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect - go.uber.org/atomic v1.7.0 // indirect - go.uber.org/multierr v1.2.0 // indirect - go.uber.org/zap v1.10.1-0.20190709142728-9a9fa7d4b5f0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.19.1 // indirect golang.org/x/crypto v0.36.0 // indirect golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 // indirect golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.37.0 // indirect + golang.org/x/net v0.38.0 // indirect golang.org/x/oauth2 v0.26.0 // indirect golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.31.0 // indirect diff --git a/go.sum b/go.sum index 6d77fc6..bc8a9d4 100644 --- a/go.sum +++ b/go.sum @@ -21,6 +21,8 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38= @@ -51,20 +53,18 @@ github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cj github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E= github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= -github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= -github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= +github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= +github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= -github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -175,10 +175,11 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/pipe-cd/pipecd v0.50.2 h1:9ziRBxj+bCIUTHKmlvsuVwEy/NaH6ac2s0/c2ks5fC8= -github.com/pipe-cd/pipecd v0.50.2/go.mod h1:EAqfprLFSVoW+1+mgXzi+Ba99yabu8U6UF1d2vT3OJA= +github.com/pipe-cd/pipecd v0.51.2 h1:eYDZeswhEoHPanTdp034fZBreJeNRdi4j9n4kQ9liVU= +github.com/pipe-cd/pipecd v0.51.2/go.mod h1:jfAQDzbKkepCaPDMqo2daESHlMbEk9EraSCIftWjNAY= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -202,9 +203,10 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -238,12 +240,17 @@ go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.2.0 h1:6I+W7f5VwC5SV9dNrZ3qXrDB9mD0dyGOi/ZJmYw03T4= -go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.1-0.20190709142728-9a9fa7d4b5f0 h1:fRtzhL15Tocngn2WZQ91iA2apveqArjCX5b9Lp9O+eA= -go.uber.org/zap v1.10.1-0.20190709142728-9a9fa7d4b5f0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -252,18 +259,20 @@ golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U= golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -300,8 +309,9 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= @@ -326,8 +336,11 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/provider/data_source_application.go b/internal/provider/data_source_application.go index 7396bbe..2cb3ec4 100644 --- a/internal/provider/data_source_application.go +++ b/internal/provider/data_source_application.go @@ -16,6 +16,7 @@ package provider import ( "context" + "sort" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" @@ -39,14 +40,15 @@ type applicationDataSource struct { type ( applicationDataSourceModel struct { - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - PipedID types.String `tfsdk:"piped_id"` - ProjectID types.String `tfsdk:"project_id"` - Kind types.String `tfsdk:"kind"` - PlatformProvider types.String `tfsdk:"platform_provider"` - Description types.String `tfsdk:"description"` - Git *applicationDataSourceGitModel `tfsdk:"git"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + PipedID types.String `tfsdk:"piped_id"` + ProjectID types.String `tfsdk:"project_id"` + Kind types.String `tfsdk:"kind"` + PlatformProvider types.String `tfsdk:"platform_provider"` + Plugins []applicationDataSourcePluginModel `tfsdk:"plugins"` + Description types.String `tfsdk:"description"` + Git *applicationDataSourceGitModel `tfsdk:"git"` } applicationDataSourceGitModel struct { @@ -56,6 +58,11 @@ type ( Path types.String `tfsdk:"path"` Filename types.String `tfsdk:"filename"` } + + applicationDataSourcePluginModel struct { + Name types.String `tfsdk:"name"` + DeployTargets []types.String `tfsdk:"deploy_targets"` + } ) func (a *applicationDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { @@ -87,8 +94,26 @@ func (a *applicationDataSource) Schema(_ context.Context, _ datasource.SchemaReq Computed: true, }, "platform_provider": schema.StringAttribute{ - Description: "The platform provider name. One of the registered providers in the piped configuration. The previous name of this field is cloud-provider.", + Description: "The platform provider name. One of the registered providers in the piped configuration. The previous name of this field is cloud-provider.", + Computed: true, + DeprecationMessage: "Use `plugins` instead. This field will be removed in the next major version.", + }, + "plugins": schema.ListNestedAttribute{ + Description: "The list of plugins that this application uses.", Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Description: "The name of the plugin.", + Computed: true, + }, + "deploy_targets": schema.ListAttribute{ + Description: "The list of deploy targets that this plugin uses.", + Computed: true, + ElementType: types.StringType, + }, + }, + }, }, "description": schema.StringAttribute{ Description: "The description of the application.", @@ -151,13 +176,12 @@ func (a *applicationDataSource) Read(ctx context.Context, req datasource.ReadReq } state = applicationDataSourceModel{ - ID: types.StringValue(getResp.Application.Id), - Name: types.StringValue(getResp.Application.Name), - PipedID: types.StringValue(getResp.Application.PipedId), - ProjectID: types.StringValue(getResp.Application.ProjectId), - Kind: types.StringValue(getResp.Application.Kind.String()), - PlatformProvider: types.StringValue(getResp.Application.PlatformProvider), - Description: types.StringValue(getResp.Application.Description), + ID: types.StringValue(getResp.Application.Id), + Name: types.StringValue(getResp.Application.Name), + PipedID: types.StringValue(getResp.Application.PipedId), + ProjectID: types.StringValue(getResp.Application.ProjectId), + Kind: types.StringValue(getResp.Application.Kind.String()), + Description: types.StringValue(getResp.Application.Description), Git: &applicationDataSourceGitModel{ RepositoryID: types.StringValue(getResp.Application.GitPath.Repo.Id), Remote: types.StringValue(getResp.Application.GitPath.Repo.Remote), @@ -167,6 +191,27 @@ func (a *applicationDataSource) Read(ctx context.Context, req datasource.ReadReq }, } + if getResp.Application.PlatformProvider != "" { + state.PlatformProvider = types.StringValue(getResp.Application.PlatformProvider) + } + + if len(getResp.Application.DeployTargetsByPlugin) != 0 { + state.Plugins = make([]applicationDataSourcePluginModel, 0, len(getResp.Application.DeployTargetsByPlugin)) + for k, v := range getResp.Application.DeployTargetsByPlugin { + deployTargets := make([]types.String, 0, len(v.DeployTargets)) + for _, dt := range v.DeployTargets { + deployTargets = append(deployTargets, types.StringValue(dt)) + } + state.Plugins = append(state.Plugins, applicationDataSourcePluginModel{ + Name: types.StringValue(k), + DeployTargets: deployTargets, + }) + } + sort.Slice(state.Plugins, func(i, j int) bool { + return state.Plugins[i].Name.ValueString() < state.Plugins[j].Name.ValueString() + }) + } + diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) } diff --git a/internal/provider/data_source_application_test.go b/internal/provider/data_source_application_test.go index 867d2e9..441c51a 100644 --- a/internal/provider/data_source_application_test.go +++ b/internal/provider/data_source_application_test.go @@ -18,19 +18,19 @@ import ( "fmt" "testing" - "github.com/golang/mock/gomock" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "go.uber.org/mock/gomock" "github.com/pipe-cd/pipecd/pkg/app/server/service/apiservice" "github.com/pipe-cd/pipecd/pkg/model" "github.com/pipe-cd/terraform-provider-pipecd/internal/provider/mock" ) +const appID = "test_application_id" + func TestAccDataSourceApplication(t *testing.T) { t.Parallel() - const appID = "test_application_id" - getReq := &apiservice.GetApplicationRequest{ApplicationId: appID} getResp := &apiservice.GetApplicationResponse{ Application: &model.Application{ @@ -82,6 +82,75 @@ func TestAccDataSourceApplication(t *testing.T) { }) } +func TestAccDataSourceApplicationWithBoth(t *testing.T) { + t.Parallel() + + getReq := &apiservice.GetApplicationRequest{ApplicationId: appID} + getResp := &apiservice.GetApplicationResponse{ + Application: &model.Application{ + Id: appID, + Name: "test_name", + ProjectId: "test_project", + PipedId: "test_piped_id", + Kind: model.ApplicationKind_CLOUDRUN, + PlatformProvider: "test_provider", + DeployTargetsByPlugin: map[string]*model.DeployTargets{ + "test_plugin": { + DeployTargets: []string{"test_target"}, + }, + "test_plugin_2": { + DeployTargets: []string{"test_target_2"}, + }, + }, + Description: "test_desc", + GitPath: &model.ApplicationGitPath{ + Repo: &model.ApplicationGitRepository{ + Id: "test_repo_id", + Remote: "test_repo_remote", + Branch: "test_repo_branch", + }, + Path: "test_git_path", + ConfigFilename: "test_git_config_filename", + Url: "test_git_url", + }, + }, + } + + ctrl := gomock.NewController(t) + client := mock.NewMockAPIClient(ctrl) + client.EXPECT().GetApplication(gomock.Any(), getReq).Return(getResp, nil).AnyTimes() + + resource.UnitTest(t, resource.TestCase{ + ProtoV6ProviderFactories: protoV6ProviderFactories(client), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceApplication(appID), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.pipecd_application.test", "id", appID), + resource.TestCheckResourceAttr("data.pipecd_application.test", "name", "test_name"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "project_id", "test_project"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "piped_id", "test_piped_id"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "kind", "CLOUDRUN"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "platform_provider", "test_provider"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "plugins.#", "2"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "plugins.0.name", "test_plugin"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "plugins.0.deploy_targets.#", "1"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "plugins.0.deploy_targets.0", "test_target"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "plugins.1.name", "test_plugin_2"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "plugins.1.deploy_targets.#", "1"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "plugins.1.deploy_targets.0", "test_target_2"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "description", "test_desc"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "git.repository_id", "test_repo_id"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "git.remote", "test_repo_remote"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "git.branch", "test_repo_branch"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "git.path", "test_git_path"), + resource.TestCheckResourceAttr("data.pipecd_application.test", "git.filename", "test_git_config_filename"), + ), + }, + }, + }) +} + func testAccDataSourceApplication(appID string) string { return providerConfig + fmt.Sprintf(` data "pipecd_application" "test" { diff --git a/internal/provider/data_source_piped.go b/internal/provider/data_source_piped.go index 82ec981..9a90463 100644 --- a/internal/provider/data_source_piped.go +++ b/internal/provider/data_source_piped.go @@ -16,6 +16,7 @@ package provider import ( "context" + "sort" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" @@ -45,6 +46,7 @@ type ( ProjectID types.String `tfsdk:"project_id"` Repositories []pipedDataSourceRepositoryModel `tfsdk:"repositories"` PlatformProviders []pipedDataSourcePlatformProviderModel `tfsdk:"platform_providers"` + Plugins []pipedDataSourcePluginModel `tfsdk:"plugins"` } pipedDataSourceRepositoryModel struct { @@ -57,6 +59,11 @@ type ( Name types.String `tfsdk:"name"` Type types.String `tfsdk:"type"` } + + pipedDataSourcePluginModel struct { + Name types.String `tfsdk:"name"` + DeployTargets []types.String `tfsdk:"deploy_targets"` + } ) func (p *pipedDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { @@ -108,6 +115,24 @@ func (p *pipedDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, }, }, }, + DeprecationMessage: "Use `plugins` instead. This field will be removed in the next major version.", + }, + "plugins": schema.ListNestedAttribute{ + Description: "The list of plugins that this piped uses.", + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Computed: true, + Description: "The name of the plugin.", + }, + "deploy_targets": schema.ListAttribute{ + Computed: true, + ElementType: types.StringType, + Description: "The list of deploy targets that this plugin uses.", + }, + }, + }, }, }, } @@ -141,30 +166,52 @@ func (p *pipedDataSource) Read(ctx context.Context, req datasource.ReadRequest, return } - repos := make([]pipedDataSourceRepositoryModel, 0, len(getResp.Piped.Repositories)) - for _, r := range getResp.Piped.Repositories { - repos = append(repos, pipedDataSourceRepositoryModel{ - ID: types.StringValue(r.Id), - Remote: types.StringValue(r.Remote), - Branch: types.StringValue(r.Branch), - }) + state = pipedDataSourceModel{ + ID: types.StringValue(getResp.Piped.Id), + Name: types.StringValue(getResp.Piped.Name), + ProjectID: types.StringValue(getResp.Piped.ProjectId), + Description: types.StringValue(getResp.Piped.Desc), } - providers := make([]pipedDataSourcePlatformProviderModel, 0, len(getResp.Piped.PlatformProviders)) - for _, p := range getResp.Piped.PlatformProviders { - providers = append(providers, pipedDataSourcePlatformProviderModel{ - Name: types.StringValue(p.Name), - Type: types.StringValue(p.Type), - }) + if len(getResp.Piped.Repositories) != 0 { + repos := make([]pipedDataSourceRepositoryModel, 0, len(getResp.Piped.Repositories)) + for _, r := range getResp.Piped.Repositories { + repos = append(repos, pipedDataSourceRepositoryModel{ + ID: types.StringValue(r.Id), + Remote: types.StringValue(r.Remote), + Branch: types.StringValue(r.Branch), + }) + } + state.Repositories = repos } - state = pipedDataSourceModel{ - ID: types.StringValue(getResp.Piped.Id), - Name: types.StringValue(getResp.Piped.Name), - ProjectID: types.StringValue(getResp.Piped.ProjectId), - Description: types.StringValue(getResp.Piped.Desc), - Repositories: repos, - PlatformProviders: providers, + if len(getResp.Piped.PlatformProviders) != 0 { + providers := make([]pipedDataSourcePlatformProviderModel, 0, len(getResp.Piped.PlatformProviders)) + for _, p := range getResp.Piped.PlatformProviders { + providers = append(providers, pipedDataSourcePlatformProviderModel{ + Name: types.StringValue(p.Name), + Type: types.StringValue(p.Type), + }) + } + state.PlatformProviders = providers + } + + if len(getResp.Piped.Plugins) != 0 { + plugins := make([]pipedDataSourcePluginModel, 0, len(getResp.Piped.Plugins)) + for _, p := range getResp.Piped.Plugins { + deployTargets := make([]types.String, 0, len(p.DeployTargets)) + for _, dt := range p.DeployTargets { + deployTargets = append(deployTargets, types.StringValue(dt)) + } + plugins = append(plugins, pipedDataSourcePluginModel{ + Name: types.StringValue(p.Name), + DeployTargets: deployTargets, + }) + } + sort.Slice(plugins, func(i, j int) bool { + return plugins[i].Name.ValueString() < plugins[j].Name.ValueString() + }) + state.Plugins = plugins } diags = resp.State.Set(ctx, &state) diff --git a/internal/provider/data_source_piped_test.go b/internal/provider/data_source_piped_test.go index 5ec5bd5..601e178 100644 --- a/internal/provider/data_source_piped_test.go +++ b/internal/provider/data_source_piped_test.go @@ -18,8 +18,8 @@ import ( "fmt" "testing" - "github.com/golang/mock/gomock" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "go.uber.org/mock/gomock" "github.com/pipe-cd/pipecd/pkg/app/server/service/apiservice" "github.com/pipe-cd/pipecd/pkg/model" @@ -81,6 +81,82 @@ func TestAccDataSourcePiped(t *testing.T) { }) } +func TestAccDataSourcePipedWithBoth(t *testing.T) { + t.Parallel() + + const pipedID = "test_piped_id" + + getReq := &apiservice.GetPipedRequest{PipedId: pipedID} + getResp := &apiservice.GetPipedResponse{ + Piped: &model.Piped{ + Id: pipedID, + Name: "test_name", + Desc: "test_desc", + ProjectId: "test_project", + Repositories: []*model.ApplicationGitRepository{ + { + Id: "test_repo_id", + Remote: "test_repo_remote", + Branch: "test_repo_branch", + }, + }, + PlatformProviders: []*model.Piped_PlatformProvider{ + { + Name: "test_provider_name", + Type: "test_provider_type", + }, + }, + Plugins: []*model.Piped_Plugin{ + { + Name: "test_plugin", + DeployTargets: []string{ + "test_target", + }, + }, + { + Name: "test_plugin_2", + DeployTargets: []string{ + "test_target_2", + }, + }, + }, + }, + } + + ctrl := gomock.NewController(t) + client := mock.NewMockAPIClient(ctrl) + client.EXPECT().GetPiped(gomock.Any(), getReq).Return(getResp, nil).AnyTimes() + + resource.UnitTest(t, resource.TestCase{ + ProtoV6ProviderFactories: protoV6ProviderFactories(client), + Steps: []resource.TestStep{ + { + Config: testAccDataSourcePiped(pipedID), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.pipecd_piped.test", "id", pipedID), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "name", "test_name"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "description", "test_desc"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "project_id", "test_project"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "repositories.#", "1"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "repositories.0.id", "test_repo_id"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "repositories.0.remote", "test_repo_remote"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "repositories.0.branch", "test_repo_branch"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "platform_providers.#", "1"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "platform_providers.0.name", "test_provider_name"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "platform_providers.0.type", "test_provider_type"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "plugins.#", "2"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "plugins.0.name", "test_plugin"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "plugins.0.deploy_targets.#", "1"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "plugins.0.deploy_targets.0", "test_target"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "plugins.1.name", "test_plugin_2"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "plugins.1.deploy_targets.#", "1"), + resource.TestCheckResourceAttr("data.pipecd_piped.test", "plugins.1.deploy_targets.0", "test_target_2"), + ), + }, + }, + }) +} + func testAccDataSourcePiped(pipedID string) string { return providerConfig + fmt.Sprintf(` data "pipecd_piped" "test" { diff --git a/internal/provider/mock/mock.go b/internal/provider/mock/mock.go index 6b57acf..88fa98f 100644 --- a/internal/provider/mock/mock.go +++ b/internal/provider/mock/mock.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: provider.go +// +// Generated by this command: +// +// mockgen -source=provider.go -package=mock -destination=./mock/mock.go +// // Package mock is a generated GoMock package. package mock @@ -8,8 +13,8 @@ import ( context "context" reflect "reflect" - gomock "github.com/golang/mock/gomock" apiservice "github.com/pipe-cd/pipecd/pkg/app/server/service/apiservice" + gomock "go.uber.org/mock/gomock" grpc "google.golang.org/grpc" ) @@ -17,6 +22,7 @@ import ( type MockAPIClient struct { ctrl *gomock.Controller recorder *MockAPIClientMockRecorder + isgomock struct{} } // MockAPIClientMockRecorder is the mock recorder for MockAPIClient. @@ -39,7 +45,7 @@ func (m *MockAPIClient) EXPECT() *MockAPIClientMockRecorder { // AddApplication mocks base method. func (m *MockAPIClient) AddApplication(ctx context.Context, in *apiservice.AddApplicationRequest, opts ...grpc.CallOption) (*apiservice.AddApplicationResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -50,16 +56,16 @@ func (m *MockAPIClient) AddApplication(ctx context.Context, in *apiservice.AddAp } // AddApplication indicates an expected call of AddApplication. -func (mr *MockAPIClientMockRecorder) AddApplication(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) AddApplication(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddApplication", reflect.TypeOf((*MockAPIClient)(nil).AddApplication), varargs...) } // DeleteApplication mocks base method. func (m *MockAPIClient) DeleteApplication(ctx context.Context, in *apiservice.DeleteApplicationRequest, opts ...grpc.CallOption) (*apiservice.DeleteApplicationResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -70,16 +76,16 @@ func (m *MockAPIClient) DeleteApplication(ctx context.Context, in *apiservice.De } // DeleteApplication indicates an expected call of DeleteApplication. -func (mr *MockAPIClientMockRecorder) DeleteApplication(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) DeleteApplication(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteApplication", reflect.TypeOf((*MockAPIClient)(nil).DeleteApplication), varargs...) } // DisableApplication mocks base method. func (m *MockAPIClient) DisableApplication(ctx context.Context, in *apiservice.DisableApplicationRequest, opts ...grpc.CallOption) (*apiservice.DisableApplicationResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -90,16 +96,16 @@ func (m *MockAPIClient) DisableApplication(ctx context.Context, in *apiservice.D } // DisableApplication indicates an expected call of DisableApplication. -func (mr *MockAPIClientMockRecorder) DisableApplication(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) DisableApplication(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisableApplication", reflect.TypeOf((*MockAPIClient)(nil).DisableApplication), varargs...) } // DisablePiped mocks base method. func (m *MockAPIClient) DisablePiped(ctx context.Context, in *apiservice.DisablePipedRequest, opts ...grpc.CallOption) (*apiservice.DisablePipedResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -110,16 +116,16 @@ func (m *MockAPIClient) DisablePiped(ctx context.Context, in *apiservice.Disable } // DisablePiped indicates an expected call of DisablePiped. -func (mr *MockAPIClientMockRecorder) DisablePiped(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) DisablePiped(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisablePiped", reflect.TypeOf((*MockAPIClient)(nil).DisablePiped), varargs...) } // EnableApplication mocks base method. func (m *MockAPIClient) EnableApplication(ctx context.Context, in *apiservice.EnableApplicationRequest, opts ...grpc.CallOption) (*apiservice.EnableApplicationResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -130,16 +136,16 @@ func (m *MockAPIClient) EnableApplication(ctx context.Context, in *apiservice.En } // EnableApplication indicates an expected call of EnableApplication. -func (mr *MockAPIClientMockRecorder) EnableApplication(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) EnableApplication(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnableApplication", reflect.TypeOf((*MockAPIClient)(nil).EnableApplication), varargs...) } // EnablePiped mocks base method. func (m *MockAPIClient) EnablePiped(ctx context.Context, in *apiservice.EnablePipedRequest, opts ...grpc.CallOption) (*apiservice.EnablePipedResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -150,16 +156,16 @@ func (m *MockAPIClient) EnablePiped(ctx context.Context, in *apiservice.EnablePi } // EnablePiped indicates an expected call of EnablePiped. -func (mr *MockAPIClientMockRecorder) EnablePiped(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) EnablePiped(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnablePiped", reflect.TypeOf((*MockAPIClient)(nil).EnablePiped), varargs...) } // Encrypt mocks base method. func (m *MockAPIClient) Encrypt(ctx context.Context, in *apiservice.EncryptRequest, opts ...grpc.CallOption) (*apiservice.EncryptResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -170,16 +176,16 @@ func (m *MockAPIClient) Encrypt(ctx context.Context, in *apiservice.EncryptReque } // Encrypt indicates an expected call of Encrypt. -func (mr *MockAPIClientMockRecorder) Encrypt(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) Encrypt(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Encrypt", reflect.TypeOf((*MockAPIClient)(nil).Encrypt), varargs...) } // GetApplication mocks base method. func (m *MockAPIClient) GetApplication(ctx context.Context, in *apiservice.GetApplicationRequest, opts ...grpc.CallOption) (*apiservice.GetApplicationResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -190,16 +196,16 @@ func (m *MockAPIClient) GetApplication(ctx context.Context, in *apiservice.GetAp } // GetApplication indicates an expected call of GetApplication. -func (mr *MockAPIClientMockRecorder) GetApplication(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) GetApplication(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetApplication", reflect.TypeOf((*MockAPIClient)(nil).GetApplication), varargs...) } // GetCommand mocks base method. func (m *MockAPIClient) GetCommand(ctx context.Context, in *apiservice.GetCommandRequest, opts ...grpc.CallOption) (*apiservice.GetCommandResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -210,16 +216,16 @@ func (m *MockAPIClient) GetCommand(ctx context.Context, in *apiservice.GetComman } // GetCommand indicates an expected call of GetCommand. -func (mr *MockAPIClientMockRecorder) GetCommand(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) GetCommand(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCommand", reflect.TypeOf((*MockAPIClient)(nil).GetCommand), varargs...) } // GetDeployment mocks base method. func (m *MockAPIClient) GetDeployment(ctx context.Context, in *apiservice.GetDeploymentRequest, opts ...grpc.CallOption) (*apiservice.GetDeploymentResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -230,16 +236,16 @@ func (m *MockAPIClient) GetDeployment(ctx context.Context, in *apiservice.GetDep } // GetDeployment indicates an expected call of GetDeployment. -func (mr *MockAPIClientMockRecorder) GetDeployment(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) GetDeployment(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeployment", reflect.TypeOf((*MockAPIClient)(nil).GetDeployment), varargs...) } // GetPiped mocks base method. func (m *MockAPIClient) GetPiped(ctx context.Context, in *apiservice.GetPipedRequest, opts ...grpc.CallOption) (*apiservice.GetPipedResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -250,16 +256,16 @@ func (m *MockAPIClient) GetPiped(ctx context.Context, in *apiservice.GetPipedReq } // GetPiped indicates an expected call of GetPiped. -func (mr *MockAPIClientMockRecorder) GetPiped(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) GetPiped(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPiped", reflect.TypeOf((*MockAPIClient)(nil).GetPiped), varargs...) } // GetPlanPreviewResults mocks base method. func (m *MockAPIClient) GetPlanPreviewResults(ctx context.Context, in *apiservice.GetPlanPreviewResultsRequest, opts ...grpc.CallOption) (*apiservice.GetPlanPreviewResultsResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -270,16 +276,16 @@ func (m *MockAPIClient) GetPlanPreviewResults(ctx context.Context, in *apiservic } // GetPlanPreviewResults indicates an expected call of GetPlanPreviewResults. -func (mr *MockAPIClientMockRecorder) GetPlanPreviewResults(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) GetPlanPreviewResults(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPlanPreviewResults", reflect.TypeOf((*MockAPIClient)(nil).GetPlanPreviewResults), varargs...) } // ListApplications mocks base method. func (m *MockAPIClient) ListApplications(ctx context.Context, in *apiservice.ListApplicationsRequest, opts ...grpc.CallOption) (*apiservice.ListApplicationsResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -290,16 +296,16 @@ func (m *MockAPIClient) ListApplications(ctx context.Context, in *apiservice.Lis } // ListApplications indicates an expected call of ListApplications. -func (mr *MockAPIClientMockRecorder) ListApplications(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) ListApplications(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListApplications", reflect.TypeOf((*MockAPIClient)(nil).ListApplications), varargs...) } // ListDeployments mocks base method. func (m *MockAPIClient) ListDeployments(ctx context.Context, in *apiservice.ListDeploymentsRequest, opts ...grpc.CallOption) (*apiservice.ListDeploymentsResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -310,16 +316,16 @@ func (m *MockAPIClient) ListDeployments(ctx context.Context, in *apiservice.List } // ListDeployments indicates an expected call of ListDeployments. -func (mr *MockAPIClientMockRecorder) ListDeployments(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) ListDeployments(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDeployments", reflect.TypeOf((*MockAPIClient)(nil).ListDeployments), varargs...) } // ListStageLogs mocks base method. func (m *MockAPIClient) ListStageLogs(ctx context.Context, in *apiservice.ListStageLogsRequest, opts ...grpc.CallOption) (*apiservice.ListStageLogsResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -330,16 +336,16 @@ func (m *MockAPIClient) ListStageLogs(ctx context.Context, in *apiservice.ListSt } // ListStageLogs indicates an expected call of ListStageLogs. -func (mr *MockAPIClientMockRecorder) ListStageLogs(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) ListStageLogs(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListStageLogs", reflect.TypeOf((*MockAPIClient)(nil).ListStageLogs), varargs...) } // RegisterEvent mocks base method. func (m *MockAPIClient) RegisterEvent(ctx context.Context, in *apiservice.RegisterEventRequest, opts ...grpc.CallOption) (*apiservice.RegisterEventResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -350,16 +356,16 @@ func (m *MockAPIClient) RegisterEvent(ctx context.Context, in *apiservice.Regist } // RegisterEvent indicates an expected call of RegisterEvent. -func (mr *MockAPIClientMockRecorder) RegisterEvent(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) RegisterEvent(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterEvent", reflect.TypeOf((*MockAPIClient)(nil).RegisterEvent), varargs...) } // RegisterPiped mocks base method. func (m *MockAPIClient) RegisterPiped(ctx context.Context, in *apiservice.RegisterPipedRequest, opts ...grpc.CallOption) (*apiservice.RegisterPipedResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -370,16 +376,16 @@ func (m *MockAPIClient) RegisterPiped(ctx context.Context, in *apiservice.Regist } // RegisterPiped indicates an expected call of RegisterPiped. -func (mr *MockAPIClientMockRecorder) RegisterPiped(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) RegisterPiped(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterPiped", reflect.TypeOf((*MockAPIClient)(nil).RegisterPiped), varargs...) } // RenameApplicationConfigFile mocks base method. func (m *MockAPIClient) RenameApplicationConfigFile(ctx context.Context, in *apiservice.RenameApplicationConfigFileRequest, opts ...grpc.CallOption) (*apiservice.RenameApplicationConfigFileResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -390,16 +396,16 @@ func (m *MockAPIClient) RenameApplicationConfigFile(ctx context.Context, in *api } // RenameApplicationConfigFile indicates an expected call of RenameApplicationConfigFile. -func (mr *MockAPIClientMockRecorder) RenameApplicationConfigFile(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) RenameApplicationConfigFile(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RenameApplicationConfigFile", reflect.TypeOf((*MockAPIClient)(nil).RenameApplicationConfigFile), varargs...) } // RequestPlanPreview mocks base method. func (m *MockAPIClient) RequestPlanPreview(ctx context.Context, in *apiservice.RequestPlanPreviewRequest, opts ...grpc.CallOption) (*apiservice.RequestPlanPreviewResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -410,16 +416,16 @@ func (m *MockAPIClient) RequestPlanPreview(ctx context.Context, in *apiservice.R } // RequestPlanPreview indicates an expected call of RequestPlanPreview. -func (mr *MockAPIClientMockRecorder) RequestPlanPreview(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) RequestPlanPreview(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestPlanPreview", reflect.TypeOf((*MockAPIClient)(nil).RequestPlanPreview), varargs...) } // SyncApplication mocks base method. func (m *MockAPIClient) SyncApplication(ctx context.Context, in *apiservice.SyncApplicationRequest, opts ...grpc.CallOption) (*apiservice.SyncApplicationResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -430,16 +436,16 @@ func (m *MockAPIClient) SyncApplication(ctx context.Context, in *apiservice.Sync } // SyncApplication indicates an expected call of SyncApplication. -func (mr *MockAPIClientMockRecorder) SyncApplication(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) SyncApplication(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncApplication", reflect.TypeOf((*MockAPIClient)(nil).SyncApplication), varargs...) } // UpdateApplication mocks base method. func (m *MockAPIClient) UpdateApplication(ctx context.Context, in *apiservice.UpdateApplicationRequest, opts ...grpc.CallOption) (*apiservice.UpdateApplicationResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -450,16 +456,36 @@ func (m *MockAPIClient) UpdateApplication(ctx context.Context, in *apiservice.Up } // UpdateApplication indicates an expected call of UpdateApplication. -func (mr *MockAPIClientMockRecorder) UpdateApplication(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) UpdateApplication(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateApplication", reflect.TypeOf((*MockAPIClient)(nil).UpdateApplication), varargs...) } +// UpdateApplicationDeployTargets mocks base method. +func (m *MockAPIClient) UpdateApplicationDeployTargets(ctx context.Context, in *apiservice.UpdateApplicationDeployTargetsRequest, opts ...grpc.CallOption) (*apiservice.UpdateApplicationDeployTargetsResponse, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UpdateApplicationDeployTargets", varargs...) + ret0, _ := ret[0].(*apiservice.UpdateApplicationDeployTargetsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateApplicationDeployTargets indicates an expected call of UpdateApplicationDeployTargets. +func (mr *MockAPIClientMockRecorder) UpdateApplicationDeployTargets(ctx, in any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateApplicationDeployTargets", reflect.TypeOf((*MockAPIClient)(nil).UpdateApplicationDeployTargets), varargs...) +} + // UpdatePiped mocks base method. func (m *MockAPIClient) UpdatePiped(ctx context.Context, in *apiservice.UpdatePipedRequest, opts ...grpc.CallOption) (*apiservice.UpdatePipedResponse, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, in} + varargs := []any{ctx, in} for _, a := range opts { varargs = append(varargs, a) } @@ -470,8 +496,8 @@ func (m *MockAPIClient) UpdatePiped(ctx context.Context, in *apiservice.UpdatePi } // UpdatePiped indicates an expected call of UpdatePiped. -func (mr *MockAPIClientMockRecorder) UpdatePiped(ctx, in interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockAPIClientMockRecorder) UpdatePiped(ctx, in any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, in}, opts...) + varargs := append([]any{ctx, in}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePiped", reflect.TypeOf((*MockAPIClient)(nil).UpdatePiped), varargs...) } diff --git a/internal/provider/resource_application.go b/internal/provider/resource_application.go index d1c9399..c6bec91 100644 --- a/internal/provider/resource_application.go +++ b/internal/provider/resource_application.go @@ -16,6 +16,7 @@ package provider import ( "context" + "sort" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -25,7 +26,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-log/tflog" api "github.com/pipe-cd/pipecd/pkg/app/server/service/apiservice" "github.com/pipe-cd/pipecd/pkg/model" @@ -46,13 +46,14 @@ type ApplicationResource struct { type ( applicationResourceModel struct { - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - PipedID types.String `tfsdk:"piped_id"` - Kind types.String `tfsdk:"kind"` - PlatformProvider types.String `tfsdk:"platform_provider"` - Description types.String `tfsdk:"description"` - Git applicationResourceGitModel `tfsdk:"git"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + PipedID types.String `tfsdk:"piped_id"` + Kind types.String `tfsdk:"kind"` + PlatformProvider types.String `tfsdk:"platform_provider"` + Plugins []applicationReSourcePluginModel `tfsdk:"plugins"` + Description types.String `tfsdk:"description"` + Git applicationResourceGitModel `tfsdk:"git"` } applicationResourceGitModel struct { @@ -60,6 +61,11 @@ type ( Path types.String `tfsdk:"path"` Filename types.String `tfsdk:"filename"` } + + applicationReSourcePluginModel struct { + Name types.String `tfsdk:"name"` + DeployTargets []types.String `tfsdk:"deploy_targets"` + } ) func (a *ApplicationResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { @@ -88,6 +94,22 @@ func (a *ApplicationResource) ImportState(ctx context.Context, req resource.Impo Filename: types.StringValue(getResp.Application.GitPath.ConfigFilename), }, } + + if len(getResp.Application.DeployTargetsByPlugin) != 0 { + plugins := make([]applicationReSourcePluginModel, 0, len(getResp.Application.DeployTargetsByPlugin)) + for k, v := range getResp.Application.DeployTargetsByPlugin { + deployTargets := make([]types.String, 0, len(v.DeployTargets)) + for _, dt := range v.DeployTargets { + deployTargets = append(deployTargets, types.StringValue(dt)) + } + plugins = append(plugins, applicationReSourcePluginModel{ + Name: types.StringValue(k), + DeployTargets: deployTargets, + }) + } + state.Plugins = plugins + } + diags := resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) } @@ -136,8 +158,26 @@ func (a *ApplicationResource) Schema(_ context.Context, _ resource.SchemaRequest }, }, "platform_provider": schema.StringAttribute{ - Description: "The platform provider name. One of the registered providers in the piped configuration. The previous name of this field is cloud-provider.", - Required: true, + Description: "The platform provider name. One of the registered providers in the piped configuration. The previous name of this field is cloud-provider.", + Optional: true, + DeprecationMessage: "Use `plugins` instead. This field will be removed in the next major version.", + }, + "plugins": schema.ListNestedAttribute{ + Description: "The list of plugins that this application uses.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Description: "The name of the plugin.", + Required: true, + }, + "deploy_targets": schema.ListAttribute{ + Description: "The list of deploy targets that this plugin uses.", + Required: true, + ElementType: types.StringType, + }, + }, + }, }, "description": schema.StringAttribute{ Description: "The description of the application.", @@ -186,18 +226,50 @@ func (a *applicationResourceModel) application() *model.Application { ConfigFilename: a.Git.Filename.ValueString(), } kind := model.ApplicationKind_value[a.Kind.ValueString()] + deployTargetsByPlugin := make(map[string]*model.DeployTargets) + for _, plugin := range a.Plugins { + deployTargets := make([]string, 0, len(plugin.DeployTargets)) + for _, dt := range plugin.DeployTargets { + deployTargets = append(deployTargets, dt.ValueString()) + } + deployTargetsByPlugin[plugin.Name.ValueString()] = &model.DeployTargets{ + DeployTargets: deployTargets, + } + } app := &model.Application{ - Id: a.ID.ValueString(), - Name: a.Name.ValueString(), - PipedId: a.PipedID.ValueString(), - GitPath: git, - Kind: model.ApplicationKind(kind), - PlatformProvider: a.PlatformProvider.ValueString(), - Description: a.Description.ValueString(), + Id: a.ID.ValueString(), + Name: a.Name.ValueString(), + PipedId: a.PipedID.ValueString(), + GitPath: git, + Kind: model.ApplicationKind(kind), + PlatformProvider: a.PlatformProvider.ValueString(), + DeployTargetsByPlugin: deployTargetsByPlugin, + Description: a.Description.ValueString(), } return app } +func plugins(plugins map[string]*model.DeployTargets) []applicationReSourcePluginModel { + if len(plugins) == 0 { + return nil + } + pluginsList := make([]applicationReSourcePluginModel, 0, len(plugins)) + for k, v := range plugins { + deployTargets := make([]types.String, 0, len(v.DeployTargets)) + for _, dt := range v.DeployTargets { + deployTargets = append(deployTargets, types.StringValue(dt)) + } + pluginsList = append(pluginsList, applicationReSourcePluginModel{ + Name: types.StringValue(k), + DeployTargets: deployTargets, + }) + } + sort.Slice(pluginsList, func(i, j int) bool { + return pluginsList[i].Name.ValueString() < pluginsList[j].Name.ValueString() + }) + return pluginsList +} + func (a *ApplicationResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { var plan applicationResourceModel diags := req.Plan.Get(ctx, &plan) @@ -225,6 +297,20 @@ func (a *ApplicationResource) Create(ctx context.Context, req resource.CreateReq return } + if len(app.DeployTargetsByPlugin) != 0 { + updateReq := &api.UpdateApplicationDeployTargetsRequest{ + ApplicationId: addResp.ApplicationId, + DeployTargetsByPlugin: app.DeployTargetsByPlugin, + } + if _, err := a.c.UpdateApplicationDeployTargets(ctx, updateReq); err != nil { + resp.Diagnostics.AddError( + "Error updating application deploy targets", + "Could not update application deploy targets, unexpected error: "+err.Error(), + ) + return + } + } + getReq := &api.GetApplicationRequest{ ApplicationId: addResp.ApplicationId, } @@ -237,14 +323,13 @@ func (a *ApplicationResource) Create(ctx context.Context, req resource.CreateReq return } - tflog.Debug(ctx, "AddApplication response", map[string]interface{}{"response_fields": getResp}) - state := applicationResourceModel{ ID: types.StringValue(addResp.ApplicationId), Name: types.StringValue(getResp.Application.Name), PipedID: types.StringValue(getResp.Application.PipedId), Kind: types.StringValue(getResp.Application.Kind.String()), PlatformProvider: types.StringValue(getResp.Application.PlatformProvider), + Plugins: plugins(getResp.Application.DeployTargetsByPlugin), Description: types.StringValue(getResp.Application.Description), Git: applicationResourceGitModel{ RepositoryID: types.StringValue(getResp.Application.GitPath.Repo.Id), @@ -252,6 +337,7 @@ func (a *ApplicationResource) Create(ctx context.Context, req resource.CreateReq Filename: types.StringValue(getResp.Application.GitPath.ConfigFilename), }, } + diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) } @@ -275,6 +361,14 @@ func (a *ApplicationResource) Update(ctx context.Context, req resource.UpdateReq if resp.Diagnostics.HasError() { return } + + if plan.application().PlatformProvider == "" && len(plan.Plugins) == 0 { + resp.Diagnostics.AddError( + "Error updating application", + "The application must have at least one plugin or platform provider.", + ) + } + updateReq := &api.UpdateApplicationRequest{ ApplicationId: plan.application().Id, PipedId: plan.application().PipedId, @@ -288,6 +382,21 @@ func (a *ApplicationResource) Update(ctx context.Context, req resource.UpdateReq ) return } + + if len(plan.application().DeployTargetsByPlugin) != 0 { + updateReq := &api.UpdateApplicationDeployTargetsRequest{ + ApplicationId: plan.application().Id, + DeployTargetsByPlugin: plan.application().DeployTargetsByPlugin, + } + if _, err := a.c.UpdateApplicationDeployTargets(ctx, updateReq); err != nil { + resp.Diagnostics.AddError( + "Error updating application deploy targets", + "Could not update application deploy targets, unexpected error: "+err.Error(), + ) + return + } + } + diags = resp.State.Set(ctx, &plan) resp.Diagnostics.Append(diags...) } diff --git a/internal/provider/resource_application_test.go b/internal/provider/resource_application_test.go index 0d50ff9..10bb9a9 100644 --- a/internal/provider/resource_application_test.go +++ b/internal/provider/resource_application_test.go @@ -17,8 +17,8 @@ package provider import ( "testing" - "github.com/golang/mock/gomock" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "go.uber.org/mock/gomock" "github.com/pipe-cd/pipecd/pkg/app/server/service/apiservice" "github.com/pipe-cd/pipecd/pkg/model" @@ -93,6 +93,103 @@ func TestAccResourceApplication(t *testing.T) { }) } +func TestAccResourceApplicationWithBoth(t *testing.T) { + t.Parallel() + + const appID = "test_application_id" + + appGit := &model.ApplicationGitPath{ + Repo: &model.ApplicationGitRepository{ + Id: "repo_id", + }, + Path: "path/to/config", + ConfigFilename: "testapp.pipecd.yaml", + } + + app := &model.Application{ + Id: appID, + Name: "test_application", + PipedId: "test_piped_id", + GitPath: appGit, + Kind: model.ApplicationKind_CLOUDRUN, + PlatformProvider: "test_provider", + Description: "test description", + DeployTargetsByPlugin: map[string]*model.DeployTargets{ + "test_plugin": { + DeployTargets: []string{"target1", "target2"}, + }, + "test_plugin_2": { + DeployTargets: []string{"target3", "target4"}, + }, + }, + } + + addReq := &apiservice.AddApplicationRequest{ + Name: app.Name, + PipedId: app.PipedId, + GitPath: app.GitPath, + Kind: app.Kind, + PlatformProvider: app.PlatformProvider, + Description: app.Description, + } + addResp := &apiservice.AddApplicationResponse{ApplicationId: appID} + + getReq := &apiservice.GetApplicationRequest{ApplicationId: appID} + getResp := &apiservice.GetApplicationResponse{Application: app} + + updateReq := &apiservice.UpdateApplicationRequest{ApplicationId: appID} + updateResp := &apiservice.UpdateApplicationResponse{ApplicationId: appID} + + updatePluginsReq := &apiservice.UpdateApplicationDeployTargetsRequest{ + ApplicationId: appID, + DeployTargetsByPlugin: map[string]*model.DeployTargets{ + "test_plugin": { + DeployTargets: []string{"target1", "target2"}, + }, + "test_plugin_2": { + DeployTargets: []string{"target3", "target4"}, + }, + }, + } + + deleteReq := &apiservice.DeleteApplicationRequest{ApplicationId: appID} + deleteResp := &apiservice.DeleteApplicationResponse{ApplicationId: appID} + + ctrl := gomock.NewController(t) + client := mock.NewMockAPIClient(ctrl) + client.EXPECT().AddApplication(gomock.Any(), addReq).Return(addResp, nil).AnyTimes() + client.EXPECT().GetApplication(gomock.Any(), getReq).Return(getResp, nil).AnyTimes() + client.EXPECT().UpdateApplication(gomock.Any(), updateReq).Return(updateResp, nil).AnyTimes() + client.EXPECT().UpdateApplicationDeployTargets(gomock.Any(), updatePluginsReq).Return(nil, nil).AnyTimes() + client.EXPECT().DeleteApplication(gomock.Any(), deleteReq).Return(deleteResp, nil).AnyTimes() + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: protoV6ProviderFactories(client), + Steps: []resource.TestStep{ + { + Config: testAccResourceApplicationWithPlugins(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("pipecd_application.test", "name", "test_application"), + resource.TestCheckResourceAttr("pipecd_application.test", "piped_id", "test_piped_id"), + resource.TestCheckResourceAttr("pipecd_application.test", "platform_provider", "test_provider"), + resource.TestCheckResourceAttr("pipecd_application.test", "description", "test description"), + resource.TestCheckResourceAttr("pipecd_application.test", "git.repository_id", "repo_id"), + resource.TestCheckResourceAttr("pipecd_application.test", "git.path", "path/to/config"), + resource.TestCheckResourceAttr("pipecd_application.test", "git.filename", "testapp.pipecd.yaml"), + resource.TestCheckResourceAttr("pipecd_application.test", "plugins.0.name", "test_plugin"), + resource.TestCheckResourceAttr("pipecd_application.test", "plugins.0.deploy_targets.#", "2"), + resource.TestCheckResourceAttr("pipecd_application.test", "plugins.0.deploy_targets.0", "target1"), + resource.TestCheckResourceAttr("pipecd_application.test", "plugins.0.deploy_targets.1", "target2"), + resource.TestCheckResourceAttr("pipecd_application.test", "plugins.1.name", "test_plugin_2"), + resource.TestCheckResourceAttr("pipecd_application.test", "plugins.1.deploy_targets.#", "2"), + resource.TestCheckResourceAttr("pipecd_application.test", "plugins.1.deploy_targets.0", "target3"), + resource.TestCheckResourceAttr("pipecd_application.test", "plugins.1.deploy_targets.1", "target4"), + ), + }, + }, + }) +} + func testAccResourceApplication() string { return providerConfig + ` resource "pipecd_application" "test" { @@ -108,3 +205,29 @@ resource "pipecd_application" "test" { } }` } + +func testAccResourceApplicationWithPlugins() string { + return providerConfig + ` +resource "pipecd_application" "test" { + name = "test_application" + piped_id = "test_piped_id" + kind = "CLOUDRUN" + platform_provider = "test_provider" + description = "test description" + git = { + repository_id = "repo_id" + path = "path/to/config" + filename = "testapp.pipecd.yaml" + } + plugins = [ + { + name = "test_plugin" + deploy_targets = ["target1", "target2"] + }, + { + name = "test_plugin_2" + deploy_targets = ["target3", "target4"] + } + ] +}` +} diff --git a/internal/provider/resource_piped_test.go b/internal/provider/resource_piped_test.go index 882dcb7..572a001 100644 --- a/internal/provider/resource_piped_test.go +++ b/internal/provider/resource_piped_test.go @@ -18,8 +18,8 @@ import ( "fmt" "testing" - "github.com/golang/mock/gomock" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "go.uber.org/mock/gomock" "github.com/pipe-cd/pipecd/pkg/app/server/service/apiservice" "github.com/pipe-cd/pipecd/pkg/model"