Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add v4 metadata feature and tests #2396

Merged
merged 6 commits into from
Mar 23, 2020
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
22 changes: 22 additions & 0 deletions agent/api/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ const (
// MetadataURIFormat defines the URI format for v3 metadata endpoint
MetadataURIFormat = "http://169.254.170.2/v3/%s"

// MetadataURIEnvVarNameV4 defines the name of the environment
// variable in containers' config, which can be used by the containers to access the
// v4 metadata endpoint
MetadataURIEnvVarNameV4 = "ECS_CONTAINER_METADATA_URI_V4"

// MetadataURIFormat defines the URI format for v4 metadata endpoint
MetadataURIFormatV4 = "http://169.254.170.2/v4/%s"

// SecretProviderSSM is to show secret provider being SSM
SecretProviderSSM = "ssm"

Expand Down Expand Up @@ -857,6 +865,20 @@ func (c *Container) InjectV3MetadataEndpoint() {
fmt.Sprintf(MetadataURIFormat, c.V3EndpointID)
}

// InjectV4MetadataEndpoint injects the v4 metadata endpoint as an environment variable for a container
func (c *Container) InjectV4MetadataEndpoint() {
c.lock.Lock()
defer c.lock.Unlock()

// don't assume that the environment variable map has been initialized by others
if c.Environment == nil {
c.Environment = make(map[string]string)
}

c.Environment[MetadataURIEnvVarNameV4] =
fmt.Sprintf(MetadataURIFormatV4, c.V3EndpointID)
}

// ShouldCreateWithSSMSecret returns true if this container needs to get secret
// value from SSM Parameter Store
func (c *Container) ShouldCreateWithSSMSecret() bool {
Expand Down
11 changes: 11 additions & 0 deletions agent/api/container/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,17 @@ func TestInjectV3MetadataEndpoint(t *testing.T) {
fmt.Sprintf(MetadataURIFormat, "myV3EndpointID"))
}

func TestInjectV4MetadataEndpoint(t *testing.T) {
container := Container{
V3EndpointID: "EndpointID",
}
container.InjectV4MetadataEndpoint()

assert.NotNil(t, container.Environment)
assert.Equal(t, container.Environment[MetadataURIEnvVarNameV4],
fmt.Sprintf(MetadataURIFormatV4, "EndpointID"))
}

func TestShouldCreateWithSSMSecret(t *testing.T) {
cases := []struct {
in Container
Expand Down
15 changes: 15 additions & 0 deletions agent/api/task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ func (task *Task) PostUnmarshalTask(cfg *config.Config,

task.initializeCredentialsEndpoint(credentialsManager)
task.initializeContainersV3MetadataEndpoint(utils.NewDynamicUUIDProvider())
task.initializeContainersV4MetadataEndpoint(utils.NewDynamicUUIDProvider())
if err := task.addNetworkResourceProvisioningDependency(cfg); err != nil {
seelog.Errorf("Task [%s]: could not provision network resource: %v", task.Arn, err)
return apierrors.NewResourceInitError(task.Arn, err)
Expand Down Expand Up @@ -719,6 +720,20 @@ func (task *Task) initializeContainersV3MetadataEndpoint(uuidProvider utils.UUID
}
}

// initializeContainersV4MetadataEndpoint generates an v4 endpoint id which we reuse the v3 container id
// (they are the same) for each container, constructs the v4 metadata endpoint,
// and injects it as an environment variable
func (task *Task) initializeContainersV4MetadataEndpoint(uuidProvider utils.UUIDProvider) {
for _, container := range task.Containers {
v3EndpointID := container.GetV3EndpointID()
if v3EndpointID == "" { // if container's v3 endpoint has not been set
container.SetV3EndpointID(uuidProvider.New())
}

container.InjectV4MetadataEndpoint()
}
}

// requiresASMDockerAuthData returns true if atleast one container in the task
// needs to retrieve private registry authentication data from ASM
func (task *Task) requiresASMDockerAuthData() bool {
Expand Down
19 changes: 19 additions & 0 deletions agent/api/task/task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,25 @@ func TestInitializeContainersV3MetadataEndpoint(t *testing.T) {
fmt.Sprintf(apicontainer.MetadataURIFormat, "new-uuid"))
}

func TestInitializeContainersV4MetadataEndpoint(t *testing.T) {
task := Task{
Containers: []*apicontainer.Container{
{
Name: "c1",
Environment: make(map[string]string),
},
},
}
container := task.Containers[0]

task.initializeContainersV4MetadataEndpoint(utils.NewStaticUUIDProvider("new-uuid"))

// Test if the v3 endpoint id is set and the endpoint is injected to env
assert.Equal(t, container.GetV3EndpointID(), "new-uuid")
assert.Equal(t, container.Environment[apicontainer.MetadataURIEnvVarNameV4],
fmt.Sprintf(apicontainer.MetadataURIFormatV4, "new-uuid"))
}

func TestPostUnmarshalTaskWithLocalVolumes(t *testing.T) {
// Constants used here are defined in task_unix_test.go and task_windows_test.go
taskFromACS := ecsacs.Task{
Expand Down
5 changes: 3 additions & 2 deletions agent/api/task/task_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,18 +113,19 @@ func TestPostUnmarshalWindowsCanonicalPaths(t *testing.T) {
task.PostUnmarshalTask(&cfg, nil, nil, nil, nil)

for _, container := range task.Containers { // remove v3 endpoint from each container because it's randomly generated
removeV3EndpointConfig(container)
removeV3andV4EndpointConfig(container)
}
assert.Equal(t, expectedTask.Containers, task.Containers, "Containers should be equal")
assert.Equal(t, expectedTask.Volumes, task.Volumes, "Volumes should be equal")
}

// removeV3EndpointConfig removes the v3 endpoint id and the injected env for a container
// so that checking all other fields can be easier
func removeV3EndpointConfig(container *apicontainer.Container) {
func removeV3andV4EndpointConfig(container *apicontainer.Container) {
container.SetV3EndpointID("")
if container.Environment != nil {
delete(container.Environment, apicontainer.MetadataURIEnvironmentVariableName)
delete(container.Environment, apicontainer.MetadataURIEnvVarNameV4)
}
if len(container.Environment) == 0 {
container.Environment = nil
Expand Down
2 changes: 2 additions & 0 deletions agent/engine/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ func validateContainerRunWorkflow(t *testing.T,
container.SetV3EndpointID(v3EndpointID)
metadataEndpointEnvValue := fmt.Sprintf(apicontainer.MetadataURIFormat, v3EndpointID)
dockerConfig.Env = append(dockerConfig.Env, "ECS_CONTAINER_METADATA_URI="+metadataEndpointEnvValue)
metadataEndpointEnvValueV4 := fmt.Sprintf(apicontainer.MetadataURIFormatV4, v3EndpointID)
dockerConfig.Env = append(dockerConfig.Env, "ECS_CONTAINER_METADATA_URI_V4="+metadataEndpointEnvValueV4)
}
// Container config should get updated with this during CreateContainer
dockerConfig.Labels["com.amazonaws.ecs.task-arn"] = task.Arn
Expand Down
21 changes: 21 additions & 0 deletions agent/handlers/task_server_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
v1 "github.com/aws/amazon-ecs-agent/agent/handlers/v1"
v2 "github.com/aws/amazon-ecs-agent/agent/handlers/v2"
v3 "github.com/aws/amazon-ecs-agent/agent/handlers/v3"
v4 "github.com/aws/amazon-ecs-agent/agent/handlers/v4"
"github.com/aws/amazon-ecs-agent/agent/logger/audit"
"github.com/aws/amazon-ecs-agent/agent/stats"
"github.com/aws/amazon-ecs-agent/agent/utils/retry"
Expand Down Expand Up @@ -67,6 +68,8 @@ func taskServerSetup(credentialsManager credentials.Manager,

v3HandlersSetup(muxRouter, state, ecsClient, statsEngine, cluster, availabilityZone, containerInstanceArn)

v4HandlersSetup(muxRouter, state, ecsClient, statsEngine, cluster, availabilityZone, containerInstanceArn)

limiter := tollbooth.NewLimiter(int64(steadyStateRate), nil)
limiter.SetOnLimitReached(handlersutils.LimitReachedHandler(auditLogger))
limiter.SetBurst(burstRate)
Expand Down Expand Up @@ -130,6 +133,24 @@ func v3HandlersSetup(muxRouter *mux.Router,
muxRouter.HandleFunc(v3.ContainerAssociationPath, v3.ContainerAssociationHandler(state))
}

// v4HandlerSetup adda all handlers in v4 package to the mux router
func v4HandlersSetup(muxRouter *mux.Router,
state dockerstate.TaskEngineState,
ecsClient api.ECSClient,
statsEngine stats.Engine,
cluster string,
availabilityZone string,
containerInstanceArn string) {
muxRouter.HandleFunc(v4.ContainerMetadataPath, v4.ContainerMetadataHandler(state))
muxRouter.HandleFunc(v4.TaskMetadataPath, v4.TaskMetadataHandler(state, ecsClient, cluster, availabilityZone, containerInstanceArn, false))
muxRouter.HandleFunc(v4.TaskWithTagsMetadataPath, v4.TaskMetadataHandler(state, ecsClient, cluster, availabilityZone, containerInstanceArn, true))
muxRouter.HandleFunc(v4.ContainerStatsPath, v4.ContainerStatsHandler(state, statsEngine))
muxRouter.HandleFunc(v4.TaskStatsPath, v4.TaskStatsHandler(state, statsEngine))
muxRouter.HandleFunc(v4.ContainerAssociationsPath, v4.ContainerAssociationsHandler(state))
muxRouter.HandleFunc(v4.ContainerAssociationPathWithSlash, v4.ContainerAssociationHandler(state))
muxRouter.HandleFunc(v4.ContainerAssociationPath, v4.ContainerAssociationHandler(state))
}

// ServeTaskHTTPEndpoint serves task/container metadata, task/container stats, and IAM Role Credentials
// for tasks being managed by the agent.
func ServeTaskHTTPEndpoint(credentialsManager credentials.Manager,
Expand Down
Loading