mirror of
https://github.com/prometheus/prometheus.git
synced 2025-12-03 16:41:05 +01:00
954 lines
31 KiB
Go
954 lines
31 KiB
Go
// Copyright The Prometheus Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package aws
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/service/ecs"
|
|
ecsTypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
|
|
"github.com/prometheus/common/model"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/prometheus/prometheus/discovery/targetgroup"
|
|
)
|
|
|
|
// Struct for test data.
|
|
type ecsDataStore struct {
|
|
region string
|
|
|
|
clusters []ecsTypes.Cluster
|
|
services []ecsTypes.Service
|
|
tasks []ecsTypes.Task
|
|
}
|
|
|
|
func TestECSDiscoveryListClusterARNs(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// iterate through the test cases
|
|
for _, tt := range []struct {
|
|
name string
|
|
ecsData *ecsDataStore
|
|
expected []string
|
|
}{
|
|
{
|
|
name: "MultipleClusters",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-west-2",
|
|
clusters: []ecsTypes.Cluster{
|
|
{
|
|
ClusterName: strptr("test-cluster"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("ACTIVE"),
|
|
},
|
|
{
|
|
ClusterName: strptr("prod-cluster"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/prod-cluster"),
|
|
Status: strptr("ACTIVE"),
|
|
},
|
|
},
|
|
},
|
|
expected: []string{
|
|
"arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster",
|
|
"arn:aws:ecs:us-west-2:123456789012:cluster/prod-cluster",
|
|
},
|
|
},
|
|
{
|
|
name: "SingleCluster",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-east-1",
|
|
clusters: []ecsTypes.Cluster{
|
|
{
|
|
ClusterName: strptr("single-cluster"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/single-cluster"),
|
|
Status: strptr("ACTIVE"),
|
|
},
|
|
},
|
|
},
|
|
expected: []string{
|
|
"arn:aws:ecs:us-east-1:123456789012:cluster/single-cluster",
|
|
},
|
|
},
|
|
{
|
|
name: "NoClusters",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-east-1",
|
|
clusters: []ecsTypes.Cluster{},
|
|
},
|
|
expected: nil,
|
|
},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
client := newMockECSClient(tt.ecsData)
|
|
|
|
d := &ECSDiscovery{
|
|
ecs: client,
|
|
cfg: &ECSSDConfig{
|
|
Region: tt.ecsData.region,
|
|
RequestConcurrency: 10,
|
|
},
|
|
}
|
|
|
|
clusters, err := d.listClusterARNs(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.expected, clusters)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestECSDiscoveryDescribeClusters(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// iterate through the test cases
|
|
for _, tt := range []struct {
|
|
name string
|
|
ecsData *ecsDataStore
|
|
clusterARNs []string
|
|
expected map[string]ecsTypes.Cluster
|
|
}{
|
|
{
|
|
name: "SingleClusterWithTags",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-west-2",
|
|
clusters: []ecsTypes.Cluster{
|
|
{
|
|
ClusterName: strptr("test-cluster"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("ACTIVE"),
|
|
Tags: []ecsTypes.Tag{
|
|
{Key: strptr("Environment"), Value: strptr("test")},
|
|
{Key: strptr("Team"), Value: strptr("backend")},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
clusterARNs: []string{"arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"},
|
|
expected: map[string]ecsTypes.Cluster{
|
|
"arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster": {
|
|
ClusterName: strptr("test-cluster"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("ACTIVE"),
|
|
Tags: []ecsTypes.Tag{
|
|
{Key: strptr("Environment"), Value: strptr("test")},
|
|
{Key: strptr("Team"), Value: strptr("backend")},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "MultipleClusters",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-east-1",
|
|
clusters: []ecsTypes.Cluster{
|
|
{
|
|
ClusterName: strptr("cluster-1"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/cluster-1"),
|
|
Status: strptr("ACTIVE"),
|
|
},
|
|
{
|
|
ClusterName: strptr("cluster-2"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/cluster-2"),
|
|
Status: strptr("DRAINING"),
|
|
Tags: []ecsTypes.Tag{
|
|
{Key: strptr("Stage"), Value: strptr("prod")},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
clusterARNs: []string{
|
|
"arn:aws:ecs:us-east-1:123456789012:cluster/cluster-1",
|
|
"arn:aws:ecs:us-east-1:123456789012:cluster/cluster-2",
|
|
},
|
|
expected: map[string]ecsTypes.Cluster{
|
|
"arn:aws:ecs:us-east-1:123456789012:cluster/cluster-1": {
|
|
ClusterName: strptr("cluster-1"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/cluster-1"),
|
|
Status: strptr("ACTIVE"),
|
|
},
|
|
"arn:aws:ecs:us-east-1:123456789012:cluster/cluster-2": {
|
|
ClusterName: strptr("cluster-2"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/cluster-2"),
|
|
Status: strptr("DRAINING"),
|
|
Tags: []ecsTypes.Tag{
|
|
{Key: strptr("Stage"), Value: strptr("prod")},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
client := newMockECSClient(tt.ecsData)
|
|
|
|
d := &ECSDiscovery{
|
|
ecs: client,
|
|
cfg: &ECSSDConfig{
|
|
Region: tt.ecsData.region,
|
|
RequestConcurrency: 10,
|
|
},
|
|
}
|
|
|
|
clusterMap, err := d.describeClusters(ctx, tt.clusterARNs)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.expected, clusterMap)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestECSDiscoveryListServiceARNs(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// iterate through the test cases
|
|
for _, tt := range []struct {
|
|
name string
|
|
ecsData *ecsDataStore
|
|
clusterARNs []string
|
|
expected map[string][]string
|
|
}{
|
|
{
|
|
name: "SingleClusterWithServices",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-west-2",
|
|
clusters: []ecsTypes.Cluster{
|
|
{
|
|
ClusterName: strptr("test-cluster"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("ACTIVE"),
|
|
},
|
|
},
|
|
services: []ecsTypes.Service{
|
|
{
|
|
ServiceName: strptr("web-service"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-west-2:123456789012:service/test-cluster/web-service"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("RUNNING"),
|
|
},
|
|
{
|
|
ServiceName: strptr("api-service"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-west-2:123456789012:service/test-cluster/api-service"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("RUNNING"),
|
|
},
|
|
{
|
|
// this is to test the old arn format without the cluster name in the service arn
|
|
// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-arn-migration.html
|
|
ServiceName: strptr("old-api-service"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-west-2:123456789012:service/old-api-service"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("RUNNING"),
|
|
},
|
|
},
|
|
},
|
|
clusterARNs: []string{"arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"},
|
|
expected: map[string][]string{
|
|
"arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster": {
|
|
"arn:aws:ecs:us-west-2:123456789012:service/test-cluster/web-service",
|
|
"arn:aws:ecs:us-west-2:123456789012:service/test-cluster/api-service",
|
|
"arn:aws:ecs:us-west-2:123456789012:service/old-api-service",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "MultipleClustesWithServices",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-east-1",
|
|
clusters: []ecsTypes.Cluster{
|
|
{
|
|
ClusterName: strptr("cluster-1"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/cluster-1"),
|
|
Status: strptr("ACTIVE"),
|
|
},
|
|
{
|
|
ClusterName: strptr("cluster-2"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/cluster-2"),
|
|
Status: strptr("ACTIVE"),
|
|
},
|
|
},
|
|
services: []ecsTypes.Service{
|
|
{
|
|
ServiceName: strptr("service-1"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-east-1:123456789012:service/cluster-1/service-1"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/cluster-1"),
|
|
Status: strptr("RUNNING"),
|
|
},
|
|
{
|
|
ServiceName: strptr("service-2"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-east-1:123456789012:service/cluster-2/service-2"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/cluster-2"),
|
|
Status: strptr("RUNNING"),
|
|
},
|
|
},
|
|
},
|
|
clusterARNs: []string{
|
|
"arn:aws:ecs:us-east-1:123456789012:cluster/cluster-1",
|
|
"arn:aws:ecs:us-east-1:123456789012:cluster/cluster-2",
|
|
},
|
|
expected: map[string][]string{
|
|
"arn:aws:ecs:us-east-1:123456789012:cluster/cluster-1": {
|
|
"arn:aws:ecs:us-east-1:123456789012:service/cluster-1/service-1",
|
|
},
|
|
"arn:aws:ecs:us-east-1:123456789012:cluster/cluster-2": {
|
|
"arn:aws:ecs:us-east-1:123456789012:service/cluster-2/service-2",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "ClusterWithNoServices",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-west-2",
|
|
clusters: []ecsTypes.Cluster{
|
|
{
|
|
ClusterName: strptr("empty-cluster"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/empty-cluster"),
|
|
Status: strptr("ACTIVE"),
|
|
},
|
|
},
|
|
services: []ecsTypes.Service{},
|
|
},
|
|
clusterARNs: []string{"arn:aws:ecs:us-west-2:123456789012:cluster/empty-cluster"},
|
|
expected: map[string][]string{
|
|
"arn:aws:ecs:us-west-2:123456789012:cluster/empty-cluster": nil,
|
|
},
|
|
},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
client := newMockECSClient(tt.ecsData)
|
|
|
|
d := &ECSDiscovery{
|
|
ecs: client,
|
|
cfg: &ECSSDConfig{
|
|
Region: tt.ecsData.region,
|
|
RequestConcurrency: 1,
|
|
},
|
|
}
|
|
|
|
serviceMap, err := d.listServiceARNs(ctx, tt.clusterARNs)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.expected, serviceMap)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestECSDiscoveryDescribeServices(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// iterate through the test cases
|
|
for _, tt := range []struct {
|
|
name string
|
|
ecsData *ecsDataStore
|
|
clusterServiceARNsMap map[string][]string
|
|
expected map[string][]ecsTypes.Service
|
|
}{
|
|
{
|
|
name: "SingleClusterServices",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-west-2",
|
|
services: []ecsTypes.Service{
|
|
{
|
|
ServiceName: strptr("web-service"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-west-2:123456789012:service/test-cluster/web-service"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("RUNNING"),
|
|
TaskDefinition: strptr("arn:aws:ecs:us-west-2:123456789012:task-definition/web-task:1"),
|
|
Tags: []ecsTypes.Tag{
|
|
{Key: strptr("Environment"), Value: strptr("production")},
|
|
},
|
|
},
|
|
{
|
|
ServiceName: strptr("api-service"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-west-2:123456789012:service/test-cluster/api-service"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("RUNNING"),
|
|
TaskDefinition: strptr("arn:aws:ecs:us-west-2:123456789012:task-definition/api-task:2"),
|
|
},
|
|
},
|
|
},
|
|
clusterServiceARNsMap: map[string][]string{
|
|
"arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster": {
|
|
"arn:aws:ecs:us-west-2:123456789012:service/test-cluster/web-service",
|
|
"arn:aws:ecs:us-west-2:123456789012:service/test-cluster/api-service",
|
|
},
|
|
},
|
|
expected: map[string][]ecsTypes.Service{
|
|
"arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster": {
|
|
{
|
|
ServiceName: strptr("web-service"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-west-2:123456789012:service/test-cluster/web-service"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("RUNNING"),
|
|
TaskDefinition: strptr("arn:aws:ecs:us-west-2:123456789012:task-definition/web-task:1"),
|
|
Tags: []ecsTypes.Tag{
|
|
{Key: strptr("Environment"), Value: strptr("production")},
|
|
},
|
|
},
|
|
{
|
|
ServiceName: strptr("api-service"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-west-2:123456789012:service/test-cluster/api-service"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("RUNNING"),
|
|
TaskDefinition: strptr("arn:aws:ecs:us-west-2:123456789012:task-definition/api-task:2"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "MultipleClustersServices",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-east-1",
|
|
services: []ecsTypes.Service{
|
|
{
|
|
ServiceName: strptr("service-1"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-east-1:123456789012:service/cluster-1/service-1"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/cluster-1"),
|
|
Status: strptr("RUNNING"),
|
|
TaskDefinition: strptr("arn:aws:ecs:us-east-1:123456789012:task-definition/task-1:1"),
|
|
},
|
|
{
|
|
ServiceName: strptr("service-2"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-east-1:123456789012:service/cluster-2/service-2"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/cluster-2"),
|
|
Status: strptr("DRAINING"),
|
|
TaskDefinition: strptr("arn:aws:ecs:us-east-1:123456789012:task-definition/task-2:1"),
|
|
},
|
|
},
|
|
},
|
|
clusterServiceARNsMap: map[string][]string{
|
|
"arn:aws:ecs:us-east-1:123456789012:cluster/cluster-1": {
|
|
"arn:aws:ecs:us-east-1:123456789012:service/cluster-1/service-1",
|
|
},
|
|
"arn:aws:ecs:us-east-1:123456789012:cluster/cluster-2": {
|
|
"arn:aws:ecs:us-east-1:123456789012:service/cluster-2/service-2",
|
|
},
|
|
},
|
|
expected: map[string][]ecsTypes.Service{
|
|
"arn:aws:ecs:us-east-1:123456789012:cluster/cluster-1": {
|
|
{
|
|
ServiceName: strptr("service-1"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-east-1:123456789012:service/cluster-1/service-1"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/cluster-1"),
|
|
Status: strptr("RUNNING"),
|
|
TaskDefinition: strptr("arn:aws:ecs:us-east-1:123456789012:task-definition/task-1:1"),
|
|
},
|
|
},
|
|
"arn:aws:ecs:us-east-1:123456789012:cluster/cluster-2": {
|
|
{
|
|
ServiceName: strptr("service-2"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-east-1:123456789012:service/cluster-2/service-2"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/cluster-2"),
|
|
Status: strptr("DRAINING"),
|
|
TaskDefinition: strptr("arn:aws:ecs:us-east-1:123456789012:task-definition/task-2:1"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
client := newMockECSClient(tt.ecsData)
|
|
|
|
d := &ECSDiscovery{
|
|
ecs: client,
|
|
cfg: &ECSSDConfig{
|
|
Region: tt.ecsData.region,
|
|
RequestConcurrency: 1,
|
|
},
|
|
}
|
|
|
|
serviceMap, err := d.describeServices(ctx, tt.clusterServiceARNsMap)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.expected, serviceMap)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestECSDiscoveryListTaskARNs(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// iterate through the test cases
|
|
for _, tt := range []struct {
|
|
name string
|
|
ecsData *ecsDataStore
|
|
services []ecsTypes.Service
|
|
expected map[string][]string
|
|
}{
|
|
{
|
|
name: "ServicesWithTasks",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-west-2",
|
|
tasks: []ecsTypes.Task{
|
|
{
|
|
TaskArn: strptr("arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-1"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Group: strptr("service:web-service"),
|
|
TaskDefinitionArn: strptr("arn:aws:ecs:us-west-2:123456789012:task-definition/web-task:1"),
|
|
},
|
|
{
|
|
TaskArn: strptr("arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-2"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Group: strptr("service:web-service"),
|
|
TaskDefinitionArn: strptr("arn:aws:ecs:us-west-2:123456789012:task-definition/web-task:1"),
|
|
},
|
|
{
|
|
TaskArn: strptr("arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-3"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Group: strptr("service:api-service"),
|
|
TaskDefinitionArn: strptr("arn:aws:ecs:us-west-2:123456789012:task-definition/api-task:2"),
|
|
},
|
|
},
|
|
},
|
|
services: []ecsTypes.Service{
|
|
{
|
|
ServiceName: strptr("web-service"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-west-2:123456789012:service/test-cluster/web-service"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("RUNNING"),
|
|
},
|
|
{
|
|
ServiceName: strptr("api-service"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-west-2:123456789012:service/test-cluster/api-service"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("RUNNING"),
|
|
},
|
|
},
|
|
expected: map[string][]string{
|
|
"arn:aws:ecs:us-west-2:123456789012:service/test-cluster/web-service": {
|
|
"arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-1",
|
|
"arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-2",
|
|
},
|
|
"arn:aws:ecs:us-west-2:123456789012:service/test-cluster/api-service": {
|
|
"arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-3",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "ServiceWithNoTasks",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-west-2",
|
|
tasks: []ecsTypes.Task{},
|
|
},
|
|
services: []ecsTypes.Service{
|
|
{
|
|
ServiceName: strptr("empty-service"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-west-2:123456789012:service/test-cluster/empty-service"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("RUNNING"),
|
|
},
|
|
},
|
|
expected: map[string][]string{
|
|
"arn:aws:ecs:us-west-2:123456789012:service/test-cluster/empty-service": nil,
|
|
},
|
|
},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
client := newMockECSClient(tt.ecsData)
|
|
|
|
d := &ECSDiscovery{
|
|
ecs: client,
|
|
cfg: &ECSSDConfig{
|
|
Region: tt.ecsData.region,
|
|
RequestConcurrency: 1,
|
|
},
|
|
}
|
|
|
|
taskMap, err := d.listTaskARNs(ctx, tt.services)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.expected, taskMap)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestECSDiscoveryDescribeTasks(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// iterate through the test cases
|
|
for _, tt := range []struct {
|
|
name string
|
|
ecsData *ecsDataStore
|
|
clusterARN string
|
|
taskARNsMap map[string][]string
|
|
expected map[string][]ecsTypes.Task
|
|
}{
|
|
{
|
|
name: "TasksInCluster",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-west-2",
|
|
tasks: []ecsTypes.Task{
|
|
{
|
|
TaskArn: strptr("arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-1"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Group: strptr("service:web-service"),
|
|
TaskDefinitionArn: strptr("arn:aws:ecs:us-west-2:123456789012:task-definition/web-task:1"),
|
|
LastStatus: strptr("RUNNING"),
|
|
Tags: []ecsTypes.Tag{
|
|
{Key: strptr("Environment"), Value: strptr("production")},
|
|
},
|
|
},
|
|
{
|
|
TaskArn: strptr("arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-2"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Group: strptr("service:api-service"),
|
|
TaskDefinitionArn: strptr("arn:aws:ecs:us-west-2:123456789012:task-definition/api-task:2"),
|
|
LastStatus: strptr("RUNNING"),
|
|
},
|
|
},
|
|
},
|
|
clusterARN: "arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster",
|
|
taskARNsMap: map[string][]string{
|
|
"arn:aws:ecs:us-west-2:123456789012:service/test-cluster/web-service": {
|
|
"arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-1",
|
|
},
|
|
"arn:aws:ecs:us-west-2:123456789012:service/test-cluster/api-service": {
|
|
"arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-2",
|
|
},
|
|
},
|
|
expected: map[string][]ecsTypes.Task{
|
|
"arn:aws:ecs:us-west-2:123456789012:service/test-cluster/web-service": {
|
|
{
|
|
TaskArn: strptr("arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-1"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Group: strptr("service:web-service"),
|
|
TaskDefinitionArn: strptr("arn:aws:ecs:us-west-2:123456789012:task-definition/web-task:1"),
|
|
LastStatus: strptr("RUNNING"),
|
|
Tags: []ecsTypes.Tag{
|
|
{Key: strptr("Environment"), Value: strptr("production")},
|
|
},
|
|
},
|
|
},
|
|
"arn:aws:ecs:us-west-2:123456789012:service/test-cluster/api-service": {
|
|
{
|
|
TaskArn: strptr("arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-2"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Group: strptr("service:api-service"),
|
|
TaskDefinitionArn: strptr("arn:aws:ecs:us-west-2:123456789012:task-definition/api-task:2"),
|
|
LastStatus: strptr("RUNNING"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "EmptyTaskARNsMap",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-west-2",
|
|
tasks: []ecsTypes.Task{},
|
|
},
|
|
clusterARN: "arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster",
|
|
taskARNsMap: map[string][]string{},
|
|
expected: map[string][]ecsTypes.Task{},
|
|
},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
client := newMockECSClient(tt.ecsData)
|
|
|
|
d := &ECSDiscovery{
|
|
ecs: client,
|
|
cfg: &ECSSDConfig{
|
|
Region: tt.ecsData.region,
|
|
RequestConcurrency: 1,
|
|
},
|
|
}
|
|
|
|
taskMap, err := d.describeTasks(ctx, tt.clusterARN, tt.taskARNsMap)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.expected, taskMap)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestECSDiscoveryRefresh(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
tests := []struct {
|
|
name string
|
|
ecsData *ecsDataStore
|
|
expected []*targetgroup.Group
|
|
}{
|
|
{
|
|
name: "SingleClusterWithTasks",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-west-2",
|
|
clusters: []ecsTypes.Cluster{
|
|
{
|
|
ClusterName: strptr("test-cluster"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("ACTIVE"),
|
|
Tags: []ecsTypes.Tag{
|
|
{Key: strptr("Environment"), Value: strptr("test")},
|
|
},
|
|
},
|
|
},
|
|
services: []ecsTypes.Service{
|
|
{
|
|
ServiceName: strptr("web-service"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-west-2:123456789012:service/test-cluster/web-service"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("ACTIVE"),
|
|
Tags: []ecsTypes.Tag{
|
|
{Key: strptr("App"), Value: strptr("web")},
|
|
},
|
|
},
|
|
},
|
|
tasks: []ecsTypes.Task{
|
|
{
|
|
TaskArn: strptr("arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-1"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
TaskDefinitionArn: strptr("arn:aws:ecs:us-west-2:123456789012:task-definition/web-task:1"),
|
|
Group: strptr("service:web-service"),
|
|
LaunchType: ecsTypes.LaunchTypeFargate,
|
|
LastStatus: strptr("RUNNING"),
|
|
DesiredStatus: strptr("RUNNING"),
|
|
HealthStatus: ecsTypes.HealthStatusHealthy,
|
|
AvailabilityZone: strptr("us-west-2a"),
|
|
PlatformFamily: strptr("Linux"),
|
|
PlatformVersion: strptr("1.4.0"),
|
|
Attachments: []ecsTypes.Attachment{
|
|
{
|
|
Type: strptr("ElasticNetworkInterface"),
|
|
Details: []ecsTypes.KeyValuePair{
|
|
{Name: strptr("subnetId"), Value: strptr("subnet-12345")},
|
|
{Name: strptr("privateIPv4Address"), Value: strptr("10.0.1.100")},
|
|
},
|
|
},
|
|
},
|
|
Tags: []ecsTypes.Tag{
|
|
{Key: strptr("Version"), Value: strptr("v1.0")},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expected: []*targetgroup.Group{
|
|
{
|
|
Source: "us-west-2",
|
|
Targets: []model.LabelSet{
|
|
{
|
|
model.AddressLabel: model.LabelValue("10.0.1.100:80"),
|
|
"__meta_ecs_cluster": model.LabelValue("test-cluster"),
|
|
"__meta_ecs_cluster_arn": model.LabelValue("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
"__meta_ecs_service": model.LabelValue("web-service"),
|
|
"__meta_ecs_service_arn": model.LabelValue("arn:aws:ecs:us-west-2:123456789012:service/test-cluster/web-service"),
|
|
"__meta_ecs_service_status": model.LabelValue("ACTIVE"),
|
|
"__meta_ecs_task_group": model.LabelValue("service:web-service"),
|
|
"__meta_ecs_task_arn": model.LabelValue("arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-1"),
|
|
"__meta_ecs_task_definition": model.LabelValue("arn:aws:ecs:us-west-2:123456789012:task-definition/web-task:1"),
|
|
"__meta_ecs_region": model.LabelValue("us-west-2"),
|
|
"__meta_ecs_availability_zone": model.LabelValue("us-west-2a"),
|
|
"__meta_ecs_subnet_id": model.LabelValue("subnet-12345"),
|
|
"__meta_ecs_ip_address": model.LabelValue("10.0.1.100"),
|
|
"__meta_ecs_launch_type": model.LabelValue("FARGATE"),
|
|
"__meta_ecs_desired_status": model.LabelValue("RUNNING"),
|
|
"__meta_ecs_last_status": model.LabelValue("RUNNING"),
|
|
"__meta_ecs_health_status": model.LabelValue("HEALTHY"),
|
|
"__meta_ecs_platform_family": model.LabelValue("Linux"),
|
|
"__meta_ecs_platform_version": model.LabelValue("1.4.0"),
|
|
"__meta_ecs_tag_cluster_Environment": model.LabelValue("test"),
|
|
"__meta_ecs_tag_service_App": model.LabelValue("web"),
|
|
"__meta_ecs_tag_task_Version": model.LabelValue("v1.0"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "NoTasks",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-east-1",
|
|
clusters: []ecsTypes.Cluster{
|
|
{
|
|
ClusterName: strptr("empty-cluster"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/empty-cluster"),
|
|
Status: strptr("ACTIVE"),
|
|
},
|
|
},
|
|
services: []ecsTypes.Service{
|
|
{
|
|
ServiceName: strptr("empty-service"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-east-1:123456789012:service/empty-cluster/empty-service"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-east-1:123456789012:cluster/empty-cluster"),
|
|
Status: strptr("ACTIVE"),
|
|
},
|
|
},
|
|
tasks: []ecsTypes.Task{},
|
|
},
|
|
expected: []*targetgroup.Group{
|
|
{
|
|
Source: "us-east-1",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "TaskWithoutENI",
|
|
ecsData: &ecsDataStore{
|
|
region: "us-west-2",
|
|
clusters: []ecsTypes.Cluster{
|
|
{
|
|
ClusterName: strptr("test-cluster"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("ACTIVE"),
|
|
},
|
|
},
|
|
services: []ecsTypes.Service{
|
|
{
|
|
ServiceName: strptr("service-1"),
|
|
ServiceArn: strptr("arn:aws:ecs:us-west-2:123456789012:service/test-cluster/service-1"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
Status: strptr("ACTIVE"),
|
|
},
|
|
},
|
|
tasks: []ecsTypes.Task{
|
|
{
|
|
TaskArn: strptr("arn:aws:ecs:us-west-2:123456789012:task/test-cluster/task-1"),
|
|
ClusterArn: strptr("arn:aws:ecs:us-west-2:123456789012:cluster/test-cluster"),
|
|
TaskDefinitionArn: strptr("arn:aws:ecs:us-west-2:123456789012:task-definition/task-def:1"),
|
|
Group: strptr("service:service-1"),
|
|
LaunchType: ecsTypes.LaunchTypeEc2,
|
|
LastStatus: strptr("RUNNING"),
|
|
DesiredStatus: strptr("RUNNING"),
|
|
HealthStatus: ecsTypes.HealthStatusHealthy,
|
|
AvailabilityZone: strptr("us-west-2a"),
|
|
// No attachments - should be skipped
|
|
Attachments: []ecsTypes.Attachment{},
|
|
},
|
|
},
|
|
},
|
|
expected: []*targetgroup.Group{
|
|
{
|
|
Source: "us-west-2",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
client := newMockECSClient(tt.ecsData)
|
|
|
|
d := &ECSDiscovery{
|
|
ecs: client,
|
|
cfg: &ECSSDConfig{
|
|
Region: tt.ecsData.region,
|
|
Port: 80,
|
|
RequestConcurrency: 1,
|
|
},
|
|
}
|
|
|
|
groups, err := d.refresh(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.expected, groups)
|
|
})
|
|
}
|
|
}
|
|
|
|
// ECS client mock.
|
|
type mockECSClient struct {
|
|
ecsData ecsDataStore
|
|
}
|
|
|
|
func newMockECSClient(ecsData *ecsDataStore) *mockECSClient {
|
|
client := mockECSClient{
|
|
ecsData: *ecsData,
|
|
}
|
|
return &client
|
|
}
|
|
|
|
func (m *mockECSClient) ListClusters(_ context.Context, _ *ecs.ListClustersInput, _ ...func(*ecs.Options)) (*ecs.ListClustersOutput, error) {
|
|
clusterArns := make([]string, 0, len(m.ecsData.clusters))
|
|
for _, cluster := range m.ecsData.clusters {
|
|
clusterArns = append(clusterArns, *cluster.ClusterArn)
|
|
}
|
|
|
|
return &ecs.ListClustersOutput{
|
|
ClusterArns: clusterArns,
|
|
}, nil
|
|
}
|
|
|
|
func (m *mockECSClient) DescribeClusters(_ context.Context, input *ecs.DescribeClustersInput, _ ...func(*ecs.Options)) (*ecs.DescribeClustersOutput, error) {
|
|
var clusters []ecsTypes.Cluster
|
|
for _, clusterArn := range input.Clusters {
|
|
for _, cluster := range m.ecsData.clusters {
|
|
if *cluster.ClusterArn == clusterArn {
|
|
clusters = append(clusters, cluster)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return &ecs.DescribeClustersOutput{
|
|
Clusters: clusters,
|
|
}, nil
|
|
}
|
|
|
|
func (m *mockECSClient) ListServices(_ context.Context, input *ecs.ListServicesInput, _ ...func(*ecs.Options)) (*ecs.ListServicesOutput, error) {
|
|
var serviceArns []string
|
|
for _, service := range m.ecsData.services {
|
|
if *service.ClusterArn == *input.Cluster {
|
|
serviceArns = append(serviceArns, *service.ServiceArn)
|
|
}
|
|
}
|
|
|
|
return &ecs.ListServicesOutput{
|
|
ServiceArns: serviceArns,
|
|
}, nil
|
|
}
|
|
|
|
func (m *mockECSClient) DescribeServices(_ context.Context, input *ecs.DescribeServicesInput, _ ...func(*ecs.Options)) (*ecs.DescribeServicesOutput, error) {
|
|
var services []ecsTypes.Service
|
|
for _, serviceArn := range input.Services {
|
|
for _, service := range m.ecsData.services {
|
|
if *service.ServiceArn == serviceArn && *service.ClusterArn == *input.Cluster {
|
|
services = append(services, service)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return &ecs.DescribeServicesOutput{
|
|
Services: services,
|
|
}, nil
|
|
}
|
|
|
|
func (m *mockECSClient) ListTasks(_ context.Context, input *ecs.ListTasksInput, _ ...func(*ecs.Options)) (*ecs.ListTasksOutput, error) {
|
|
var taskArns []string
|
|
for _, task := range m.ecsData.tasks {
|
|
if *task.ClusterArn == *input.Cluster {
|
|
// If ServiceName is specified, filter by service
|
|
if input.ServiceName != nil {
|
|
expectedGroup := "service:" + *input.ServiceName
|
|
if task.Group != nil && *task.Group == expectedGroup {
|
|
taskArns = append(taskArns, *task.TaskArn)
|
|
}
|
|
} else {
|
|
taskArns = append(taskArns, *task.TaskArn)
|
|
}
|
|
}
|
|
}
|
|
|
|
return &ecs.ListTasksOutput{
|
|
TaskArns: taskArns,
|
|
}, nil
|
|
}
|
|
|
|
func (m *mockECSClient) DescribeTasks(_ context.Context, input *ecs.DescribeTasksInput, _ ...func(*ecs.Options)) (*ecs.DescribeTasksOutput, error) {
|
|
var tasks []ecsTypes.Task
|
|
for _, taskArn := range input.Tasks {
|
|
for _, task := range m.ecsData.tasks {
|
|
if *task.TaskArn == taskArn && *task.ClusterArn == *input.Cluster {
|
|
tasks = append(tasks, task)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return &ecs.DescribeTasksOutput{
|
|
Tasks: tasks,
|
|
}, nil
|
|
}
|