diff --git a/pkg/app/piped/cloudprovider/cloudrun/servicemanifest.go b/pkg/app/piped/cloudprovider/cloudrun/servicemanifest.go index 119ecb4e6c..92f1324100 100644 --- a/pkg/app/piped/cloudprovider/cloudrun/servicemanifest.go +++ b/pkg/app/piped/cloudprovider/cloudrun/servicemanifest.go @@ -23,6 +23,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/yaml" + + "github.com/pipe-cd/pipecd/pkg/model" ) type ServiceManifest struct { @@ -199,3 +201,36 @@ func parseContainerImage(image string) (name, tag string) { name = paths[len(paths)-1] return } + +func FindArtifactVersions(sm ServiceManifest) ([]*model.ArtifactVersion, error) { + containers, ok, err := unstructured.NestedSlice(sm.u.Object, "spec", "template", "spec", "containers") + if err != nil { + return nil, err + } + if !ok || len(containers) == 0 { + return nil, fmt.Errorf("spec.template.spec.containers was missing") + } + + container, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&containers[0]) + if err != nil { + return nil, fmt.Errorf("invalid container format") + } + + image, ok, err := unstructured.NestedString(container, "image") + if err != nil { + return nil, err + } + if !ok || image == "" { + return nil, fmt.Errorf("image was missing") + } + name, tag := parseContainerImage(image) + + return []*model.ArtifactVersion{ + { + Kind: model.ArtifactVersion_CONTAINER_IMAGE, + Version: tag, + Name: name, + Url: image, + }, + }, nil +} diff --git a/pkg/app/piped/cloudprovider/cloudrun/servicemanifest_test.go b/pkg/app/piped/cloudprovider/cloudrun/servicemanifest_test.go index 7818155f78..e5c655376a 100644 --- a/pkg/app/piped/cloudprovider/cloudrun/servicemanifest_test.go +++ b/pkg/app/piped/cloudprovider/cloudrun/servicemanifest_test.go @@ -19,6 +19,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/pipe-cd/pipecd/pkg/model" ) const serviceManifest = ` @@ -288,3 +290,130 @@ spec: }) } } + +func TestFindArtifactVersions(t *testing.T) { + testcases := []struct { + name string + manifest string + want []*model.ArtifactVersion + wantErr bool + }{ + { + name: "ok", + manifest: ` +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + name: helloworld + labels: + cloud.googleapis.com/location: asia-northeast1 + pipecd-dev-managed-by: piped + annotations: + run.googleapis.com/ingress: all + run.googleapis.com/ingress-status: all +spec: + template: + metadata: + name: helloworld-v010-1234567 + annotations: + autoscaling.knative.dev/maxScale: '1' + spec: + containerConcurrency: 80 + timeoutSeconds: 300 + containers: + - image: gcr.io/pipecd/helloworld:v0.1.0 + args: + - server + ports: + - name: http1 + containerPort: 9085 + resources: + limits: + cpu: 1000m + memory: 128Mi + traffic: + - revisionName: helloworld-v010-1234567 + percent: 100 +`, + want: []*model.ArtifactVersion{ + { + Kind: model.ArtifactVersion_CONTAINER_IMAGE, + Version: "v0.1.0", + Name: "helloworld", + Url: "gcr.io/pipecd/helloworld:v0.1.0", + }, + }, + wantErr: false, + }, + { + name: "err: containers missing", + manifest: ` +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + name: helloworld +spec: + template: + metadata: + name: helloworld-v010-1234567 + annotations: + autoscaling.knative.dev/maxScale: '1' + spec: + containerConcurrency: 80 + timeoutSeconds: 300 +`, + want: nil, + wantErr: true, + }, + { + name: "err: image missing", + manifest: ` +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + name: helloworld + labels: + cloud.googleapis.com/location: asia-northeast1 + pipecd-dev-managed-by: piped + annotations: + run.googleapis.com/ingress: all + run.googleapis.com/ingress-status: all +spec: + template: + metadata: + name: helloworld-v010-1234567 + annotations: + autoscaling.knative.dev/maxScale: '1' + spec: + containerConcurrency: 80 + timeoutSeconds: 300 + containers: + - args: + - server + ports: + - name: http1 + containerPort: 9085 + resources: + limits: + cpu: 1000m + memory: 128Mi + traffic: + - revisionName: helloworld-v010-1234567 + percent: 100 +`, + want: nil, + wantErr: true, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + data := []byte(tc.manifest) + sm, err := ParseServiceManifest(data) + require.NoError(t, err) + + got, err := FindArtifactVersions(sm) + require.Equal(t, tc.wantErr, err != nil) + require.Equal(t, tc.want, got) + }) + } +} diff --git a/pkg/app/piped/planner/cloudrun/cloudrun.go b/pkg/app/piped/planner/cloudrun/cloudrun.go index 438310afa2..3b1396de73 100644 --- a/pkg/app/piped/planner/cloudrun/cloudrun.go +++ b/pkg/app/piped/planner/cloudrun/cloudrun.go @@ -62,6 +62,17 @@ func (p *Planner) Plan(ctx context.Context, in planner.Input) (out planner.Outpu in.Logger.Warn("unable to determine target version", zap.Error(e)) } + out.Versions, err = p.determineVersions(ds.AppDir, cfg.Input.ServiceManifestFile) + if err != nil { + in.Logger.Warn("unable to determine target versions", zap.Error(err)) + out.Versions = []*model.ArtifactVersion{ + { + Kind: model.ArtifactVersion_UNKNOWN, + Version: "unknown", + }, + } + } + autoRollback := *cfg.Input.AutoRollback // In case the strategy has been decided by trigger. @@ -133,3 +144,12 @@ func (p *Planner) determineVersion(appDir, serviceManifestFile string) (string, return provider.FindImageTag(sm) } + +func (p *Planner) determineVersions(appDir, serviceManifestFile string) ([]*model.ArtifactVersion, error) { + sm, err := provider.LoadServiceManifest(appDir, serviceManifestFile) + if err != nil { + return nil, err + } + + return provider.FindArtifactVersions(sm) +}