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

ECS Agent tagging support #1618

Merged
merged 2 commits into from
Oct 25, 2018
Merged

ECS Agent tagging support #1618

merged 2 commits into from
Oct 25, 2018

Conversation

haikuoliu
Copy link
Contributor

@haikuoliu haikuoliu commented Oct 12, 2018

Summary

ECS local tags support

Implementation details

  1. Introduce ECS_CONTAINER_INSTANCE_TAGS env var to allow customer to configure tags on the instance.
  2. Introduce ECS_CONTAINER_INSTANCE_PROPAGATE_TAGS to get the tags of the ec2 instance that Agent is running on using DescribeTags API. We can specify either none or ec2_instance for this env var, the reason that we don't use a boolean is because we may introduce other ways to propagate the tags (like ask backend to propagate tags instead of doing it on Agent side).
  3. ECS model changes for tags, introduce a new API ListTagsForResourceInput for testing purpose.
  4. ECS client API changes to allow tags passed from RCI call.
  5. Introduce EC2 client API, DescribeECSTagsForInstance is used for RCI call, and CreateTags for func test.
  6. Add InstanceID API to EC2 metadata client.
  7. Functional test and manual test against Gamma endpoint.
  8. Vendor packages update.
  9. Joel reviewed my README.

TODO:
Make func test pass once the ListTagsForResource API is in prod.

Testing

  • Builds on Linux (make release)
  • Builds on Windows (go build -out amazon-ecs-agent.exe ./agent)
  • Unit tests on Linux (make test) pass
  • Unit tests on Windows (go test -timeout=25s ./agent/...) pass
  • Integration tests on Linux (make run-integ-tests) pass
  • Integration tests on Windows (.\scripts\run-integ-tests.ps1) pass
  • Functional tests on Linux (make run-functional-tests) pass
  • Functional tests on Windows (.\scripts\run-functional-tests.ps1) pass

Description for the changelog

Licensing

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@haikuoliu haikuoliu added this to the 1.22.0 milestone Oct 12, 2018
@haikuoliu haikuoliu changed the title ECS local tags support ECS Agent tagging support Oct 15, 2018
@haikuoliu haikuoliu requested review from brycecarman and a team and removed request for brycecarman October 15, 2018 03:28
@haikuoliu
Copy link
Contributor Author

Func tests are expected to fail as the ListTagsForResource API is not in prod yet.

Copy link
Contributor

@petderek petderek left a comment

Choose a reason for hiding this comment

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

Neat!


// When ContainerInstancePropagateTagsEC2InstanceType is specified, agent will
// make DescribeTags API call to get tags remotely.
ContainerInstancePropagateTagsEC2InstanceType
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this get stored in the state file? if so, we should test that the values of these don't change. (IE, we don't accidentally reorder them in the future)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This will not be stored in the state file, every time Agent starts it will read the config file and populate this value again.

@@ -406,6 +416,9 @@ func environmentConfig() (Config, error) {
var errs []error
instanceAttributes, errs = parseInstanceAttributes(errs)

var containerInstanceTags map[string]string
containerInstanceTags, errs = parseContainerInstanceTags(errs)
Copy link
Contributor

Choose a reason for hiding this comment

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

This might be a silly question, but why doesn't := work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah that will work, changed to the simpler format.

agent/ec2/ec2_client.go Show resolved Hide resolved
@@ -592,6 +601,49 @@ func TestUserDataConfig(t *testing.T) {
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Add test cases for the key:value pairs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I've already added here. LMK if you want me to include more test cases.


for _, tc := range testcases {
// Test the parse function only.
t.Run(tc.name, func(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd probably merge these two t.Run functions together and just make sure the "message" includes a note that the parsing didn't work as expected.

@@ -208,3 +210,20 @@ func IsAWSErrorCodeEqual(err error, code string) bool {
awsErr, ok := err.(awserr.Error)
return ok && awsErr.Code() == code
}

// MapToTags converts a map to a slice of tags.
func MapToTags(tagsMap map[string]string) []*ecs.Tag {
Copy link
Contributor

Choose a reason for hiding this comment

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

FFTI: I'd consider moving this and the other tagging-specific functions (mergeTags) into a tagging-specific file / package.

func extractTagsMapFromRegisterContainerInstanceInput(req *ecs.RegisterContainerInstanceInput) map[string]string {
tagsMap := make(map[string]string, len(req.Tags))
for i := range req.Tags {
tagsMap[*req.Tags[i].Key] = aws.StringValue(req.Tags[i].Value)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why key is using pointer dereference while value is using aws.StringValue here?

ec2metadata "github.com/aws/aws-sdk-go/aws/ec2metadata"
ec20 "github.com/aws/aws-sdk-go/service/ec2"
Copy link
Contributor

Choose a reason for hiding this comment

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

This ec20 is a little bit weird as a name. Maybe change '/agent/ec2' to agent_ec2 and keep '/service/ec2' as ec2.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know, this is something auto-generated...

Copy link
Contributor

Choose a reason for hiding this comment

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

its the worst!

README.md Outdated
@@ -131,6 +131,8 @@ additional details on each available environment variable.
| `ECS_ENABLE_CPU_UNBOUNDED_WINDOWS_WORKAROUND` | `true` | When `true`, ECS will allow CPU unbounded(CPU=`0`) tasks to run along with CPU bounded tasks in Windows. | Not applicable | `false` |
| `ECS_TASK_METADATA_RPS_LIMIT` | `100,150` | Comma separated integer values for steady state and burst throttle limits for task metadata endpoint | `40,60` | `40,60` |
| `ECS_SHARED_VOLUME_MATCH_FULL_CONFIG` | `true` | When `true`, ECS Agent will compare name, driver options, and labels to make sure volumes are identical. When `false`, Agent will short circuit shared volume comparison if the names match. This is the default Docker behavior. If a volume is shared across instances, this should be set to `false`. | `false` | `false`|
| `ECS_CONTAINER_INSTANCE_PROPAGATE_TAGS` | `"ec2_instance"` | If `"ec2_instance"` is specified, tags of the ec2 instance will be registered to ECS, make sure you allow `ec2:DescribeTags` action in your instance profile. | `"none"` | `"none"` |
Copy link
Contributor

Choose a reason for hiding this comment

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

Name ECS_CONTAINER_INSTANCE_PROPAGATE_TAGS can be a bit confusing and it sounds like a bool var.

how about something like -
ECS_INSTANCE_REGISTER_TAG_TYPES or ECS_INSTANCE_OBTAIN_TAG_TYPES?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Confirmed with Akram, decided to go with ECS_CONTAINER_INSTANCE_PROPAGATE_TAGS_FROM.

}

func parseContainerInstancePropagateTags() ContainerInstancePropagateTagsType {
containerInstancePropagateTagsString := os.Getenv("ECS_CONTAINER_INSTANCE_PROPAGATE_TAGS")
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like ECS_CONTAINER_INSTANCE_PROPAGATE_TAGS should be a list of strings rather than one, if we want to support multiple tag types in the future and not break backwards compatibility.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think customer only has one option - either ask Agent to propagate the tags or ask backend to propagate the tags, customer will get the same tags either way, so supporting both of them seems to do duplicate work.

Copy link
Contributor

Choose a reason for hiding this comment

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

The reason why I brought this up is because we have ContainerInstancePropagateTagsType as an enum -- implying there can be other values too?
Do we know what they could be or if multiple values can be used?

If ec2-instance is going to be the only value, we could just have a string as opposed to an enum.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it can be other values, but even if there are other possible values, customer can only use one of them. For example, if we have backend as one of the values in the future, customer only needs ECS_CONTAINER_INSTANCE_PROPAGATE_TAGS to be either ec2_instance or backend, because we will get the same set of tags when we ask backend to propagate or we let Agent propagate tags.

@haikuoliu
Copy link
Contributor Author

@yumex93 @sharanyad I've refactored accordingly.

Also introduced PutAccountSetting API used in functional test, this is required for tags related API.

haikuoliu added a commit to haikuoliu/amazon-ecs-agent that referenced this pull request Oct 23, 2018
@haikuoliu
Copy link
Contributor Author

Joel reviewed my README. Rebase the dev branch, and added changelog.

CHANGELOG.md Outdated
@@ -1,8 +1,11 @@
# Changelog

## 1.22.0-dev
* Feature - Introduce two environment variables to support ECS tagging [#1618](https://github.com/aws/amazon-ecs-agent/pull/1618)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we also specify what they are?

@@ -310,6 +325,20 @@
{"shape":"ServiceNotFoundException"}
]
},
"PutAccountSetting":{
Copy link
Contributor

Choose a reason for hiding this comment

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

is this part of tagging?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, used in func test.

assert.Equal(t, localValue, aws.StringValue(mergedTags[2].Value))
}

func TestGetContainerInstanceTagsFromEC2API(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add negative test case as well

localKey, localValue, commonKey, commonKeyLocalValue),
},
}
agent := RunAgent(t, agentOptions)
Copy link
Contributor

Choose a reason for hiding this comment

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

this test will require specific agent version.

func TestContainerInstanceTags(t *testing.T) {
// We need long container instance ARN for tagging APIs, PutAccountSettingInput
// will enable long container instance ARN.
putAccountSettingInput := ecsapi.PutAccountSettingInput{
Copy link
Contributor

Choose a reason for hiding this comment

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

Will customers have to manually enable this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, it will be described in doc

agent/ec2/ec2_client.go Show resolved Hide resolved
Copy link
Contributor

@sharanyad sharanyad left a comment

Choose a reason for hiding this comment

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

please add a comment in the functional test to change the required agent version during staging

@@ -88,6 +88,7 @@ func TestEnvironmentConfig(t *testing.T) {
defer setTestEnv("ECS_NUM_IMAGES_DELETE_PER_CYCLE", "2")()
defer setTestEnv("ECS_IMAGE_PULL_BEHAVIOR", "always")()
defer setTestEnv("ECS_INSTANCE_ATTRIBUTES", "{\"my_attribute\": \"testing\"}")()
defer setTestEnv("ECS_CONTAINER_INSTANCE_TAGS", "{\"my_tag\": \"testing\"}")()
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Use string literals instead of escaping:

defer setTestEnv("ECS_CONTAINER_INSTANCE_TAGS", `{"my_tag": "testing"}`)() 

agent/ec2/ec2_client.go Show resolved Hide resolved
ec2metadata "github.com/aws/aws-sdk-go/aws/ec2metadata"
ec20 "github.com/aws/aws-sdk-go/service/ec2"
Copy link
Contributor

Choose a reason for hiding this comment

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

its the worst!

Introduce ECS_CONTAINER_INSTANCE_TAGS environment variable and ECS_CONTAINER_INSTANCE_PROPAGATE_TAGS
to allow Agent retrieve tags locally and remotely.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants