Skip to content

Commit

Permalink
Output artifact versions for ecs deployment (#3385)
Browse files Browse the repository at this point in the history
**What this PR does / why we need it**:

This PR changes to output artifact versions for ecs development.

**Which issue(s) this PR fixes**:

A part of #3303

**Does this PR introduce a user-facing change?**:
<!--
If no, just write "NONE" in the release-note block below.
-->
```release-note
NONE
```

This PR was merged by Kapetanios.
  • Loading branch information
ffjlabo committed Mar 10, 2022
1 parent 0885899 commit f3b32ae
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 1 deletion.
2 changes: 2 additions & 0 deletions pkg/app/piped/cloudprovider/ecs/BUILD.bazel
Expand Up @@ -15,6 +15,7 @@ go_library(
deps = [
"//pkg/app/piped/cloudprovider:go_default_library",
"//pkg/config:go_default_library",
"//pkg/model:go_default_library",
"@com_github_aws_aws_sdk_go_v2//aws:go_default_library",
"@com_github_aws_aws_sdk_go_v2_config//:go_default_library",
"@com_github_aws_aws_sdk_go_v2_credentials//stscreds:go_default_library",
Expand All @@ -37,6 +38,7 @@ go_test(
],
embed = [":go_default_library"],
deps = [
"//pkg/model:go_default_library",
"@com_github_aws_aws_sdk_go_v2//aws:go_default_library",
"@com_github_aws_aws_sdk_go_v2_service_ecs//types:go_default_library",
"@com_github_stretchr_testify//assert:go_default_library",
Expand Down
33 changes: 32 additions & 1 deletion pkg/app/piped/cloudprovider/ecs/task.go
Expand Up @@ -19,9 +19,10 @@ import (
"os"
"strings"

"github.com/aws/aws-sdk-go-v2/service/ecs/types"
"sigs.k8s.io/yaml"

"github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/pipe-cd/pipecd/pkg/model"
)

func loadTaskDefinition(path string) (types.TaskDefinition, error) {
Expand Down Expand Up @@ -61,3 +62,33 @@ func parseContainerImage(image string) (name, tag string) {
name = paths[len(paths)-1]
return
}

// FindArtifactVersions parses artifact versions from ECS task definition.
func FindArtifactVersions(taskDefinition types.TaskDefinition) ([]*model.ArtifactVersion, error) {
if len(taskDefinition.ContainerDefinitions) == 0 {
return nil, fmt.Errorf("container definition could not be empty")
}

// Remove duplicate images.
imageMap := map[string]struct{}{}
for _, cd := range taskDefinition.ContainerDefinitions {
imageMap[*cd.Image] = struct{}{}
}

versions := make([]*model.ArtifactVersion, 0, len(imageMap))
for i := range imageMap {
name, tag := parseContainerImage(i)
if name == "" {
return nil, fmt.Errorf("image name could not be empty")
}

versions = append(versions, &model.ArtifactVersion{
Kind: model.ArtifactVersion_CONTAINER_IMAGE,
Version: tag,
Name: name,
Url: i,
})
}

return versions, nil
}
206 changes: 206 additions & 0 deletions pkg/app/piped/cloudprovider/ecs/task_test.go
Expand Up @@ -20,6 +20,8 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/stretchr/testify/assert"

"github.com/pipe-cd/pipecd/pkg/model"
)

func TestParseTaskDefinition(t *testing.T) {
Expand Down Expand Up @@ -78,3 +80,207 @@ cpu: 256
})
}
}

func TestFindArtifactVersions(t *testing.T) {
t.Parallel()

testcases := []struct {
name string
input []byte
expected []*model.ArtifactVersion
expectedErr bool
}{
{
name: "ok",
input: []byte(`
{
"family": "nginx-canary-fam-1",
"compatibilities": [
"FARGATE"
],
"networkMode": "awsvpc",
"memory": 512,
"cpu": 256,
"containerDefinitions" : [
{
"image": "gcr.io/pipecd/helloworld:v1.0.0",
"name": "helloworld",
"portMappings": [
{
"containerPort": 80,
"hostPort": 9085,
"protocol": "tcp"
}
]
}
]
}
`),
expected: []*model.ArtifactVersion{
{
Kind: model.ArtifactVersion_CONTAINER_IMAGE,
Version: "v1.0.0",
Name: "helloworld",
Url: "gcr.io/pipecd/helloworld:v1.0.0",
},
},
expectedErr: false,
},
{
name: "missing containerDefinitions",
input: []byte(`
{
"family": "nginx-canary-fam-1",
"compatibilities": [
"FARGATE"
],
"networkMode": "awsvpc",
"memory": 512,
"cpu": 256,
}
`),
expected: nil,
expectedErr: true,
},
{
name: "missing image name",
input: []byte(`
{
"family": "nginx-canary-fam-1",
"compatibilities": [
"FARGATE"
],
"networkMode": "awsvpc",
"memory": 512,
"cpu": 256,
"containerDefinitions" : [
{
"image": "gcr.io/pipecd/:v1.0.0",
"name": "helloworld",
"portMappings": [
{
"containerPort": 80,
"hostPort": 9085,
"protocol": "tcp"
}
]
}
]
}
`),
expected: nil,
expectedErr: true,
},
{
name: "multiple containers",
input: []byte(`
{
"family": "nginx-canary-fam-1",
"compatibilities": [
"FARGATE"
],
"networkMode": "awsvpc",
"memory": 512,
"cpu": 256,
"containerDefinitions" : [
{
"image": "gcr.io/pipecd/helloworld:v1.0.0",
"name": "helloworld",
"portMappings": [
{
"containerPort": 80,
"hostPort": 9085,
"protocol": "tcp"
}
]
},
{
"image": "gcr.io/pipecd/my-service:v1.0.0",
"name": "my-service",
"portMappings": [
{
"containerPort": 80,
"hostPort": 9090,
"protocol": "tcp"
}
]
}
]
}
`),
expected: []*model.ArtifactVersion{
{
Kind: model.ArtifactVersion_CONTAINER_IMAGE,
Version: "v1.0.0",
Name: "helloworld",
Url: "gcr.io/pipecd/helloworld:v1.0.0",
},
{
Kind: model.ArtifactVersion_CONTAINER_IMAGE,
Version: "v1.0.0",
Name: "my-service",
Url: "gcr.io/pipecd/my-service:v1.0.0",
},
},
expectedErr: false,
},
{
name: "multiple containers with the same image",
input: []byte(`
{
"family": "nginx-canary-fam-1",
"compatibilities": [
"FARGATE"
],
"networkMode": "awsvpc",
"memory": 512,
"cpu": 256,
"containerDefinitions" : [
{
"image": "gcr.io/pipecd/helloworld:v1.0.0",
"name": "helloworld",
"portMappings": [
{
"containerPort": 80,
"hostPort": 9085,
"protocol": "tcp"
}
]
},
{
"image": "gcr.io/pipecd/helloworld:v1.0.0",
"name": "helloworld-02",
"portMappings": [
{
"containerPort": 80,
"hostPort": 9091,
"protocol": "tcp"
}
]
}
]
}
`),
expected: []*model.ArtifactVersion{
{
Kind: model.ArtifactVersion_CONTAINER_IMAGE,
Version: "v1.0.0",
Name: "helloworld",
Url: "gcr.io/pipecd/helloworld:v1.0.0",
},
},
expectedErr: false,
},
}

for _, tc := range testcases {
tc := tc

t.Run(tc.name, func(t *testing.T) {
td, _ := parseTaskDefinition(tc.input)
versions, err := FindArtifactVersions(td)
assert.Equal(t, tc.expectedErr, err != nil)
assert.ElementsMatch(t, tc.expected, versions)
})
}
}
20 changes: 20 additions & 0 deletions pkg/app/piped/planner/ecs/ecs.go
Expand Up @@ -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(err))
}

out.Versions, err = determineVersions(ds.AppDir, cfg.Input.TaskDefinitionFile)
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.
Expand Down Expand Up @@ -133,3 +144,12 @@ func determineVersion(appDir, taskDefinitonFile string) (string, error) {

return provider.FindImageTag(taskDefinition)
}

func determineVersions(appDir, taskDefinitonFile string) ([]*model.ArtifactVersion, error) {
taskDefinition, err := provider.LoadTaskDefinition(appDir, taskDefinitonFile)
if err != nil {
return nil, err
}

return provider.FindArtifactVersions(taskDefinition)
}

0 comments on commit f3b32ae

Please sign in to comment.