Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/app/piped/cloudprovider/ecs/BUILD.bazel
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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) {
Copy link
Member

Choose a reason for hiding this comment

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

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

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 {
Copy link
Member

Choose a reason for hiding this comment

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

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

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
Original file line number Diff line number Diff line change
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)
}