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

Set router skip clean to false #1844

Merged
merged 1 commit into from
Feb 14, 2019
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"family": "task-server-endpoint-validator",
"taskRoleArn": "$$$TASK_ROLE$$$",
"networkMode": "host",
"containerDefinitions": [{
"image": "amazonlinux:2",
"name": "task_server_endpoint_validator_container",
"memory": 256,
"command": ["sh", "-c", "curl -L -o /dev/null -s -w \"%{http_code}\n\" http://169.254.170.2/$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI | grep \"200\" && exit 42 || exit 1"]
}]
}
42 changes: 42 additions & 0 deletions agent/functional_tests/tests/functionaltests_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1427,3 +1427,45 @@ func TestElasticInferenceValidator(t *testing.T) {
exitCode, _ := task.ContainerExitcode("container_1")
assert.Equal(t, 42, exitCode, fmt.Sprintf("Expected exit code of 42 for container; got %d", exitCode))
}

// TestServerEndpointValidator tests the workflow of task server endpoint
func TestServerEndpointValidator(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.

Is there a particular reason you hand-wrote this code instead of using the simpletests package? I would recommend using that instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe it is for setting the task iam roles configuration.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discussed with Sam offline, we might not need network mode setup for it to work.

But I'm thinking for now, let's keep test file as hand-written to release it today to solve our customers' issue. I will keep troubleshooting to see if I can make functional test work with simpletests file

Copy link
Contributor

Choose a reason for hiding this comment

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

Task IAM roles are supported with "host", "bridge", and "awsvpc" network modes, and we have existing functional tests that validate this. There should not be a dependency on using the "host" network mode.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Read some document, agree on this should not depend on host mode. I will dive deep to see if my host needs some config to make it functional test on other modes, and I will follow up with another pr to address it once i get it figured out.

// The test runs only when the environment TEST_IAM_ROLE was set
if os.Getenv("TEST_DISABLE_TASK_IAM_ROLE_NET_HOST") == "true" {
t.Skip("Skipping test TaskIamRole in host network mode, as TEST_DISABLE_TASK_IAM_ROLE_NET_HOST is set true")
}
agentOptions := &AgentOptions{
ExtraEnvironment: map[string]string{
"ECS_ENABLE_TASK_IAM_ROLE": "true",
"ECS_ENABLE_TASK_IAM_ROLE_NETWORK_HOST": "true",
},
PortBindings: map[nat.Port]map[string]string{
"51679/tcp": {
"HostIP": "0.0.0.0",
"HostPort": "51679",
},
},
}

agent := RunAgent(t, agentOptions)
defer agent.Cleanup()

roleArn := os.Getenv("TASK_IAM_ROLE_ARN")
if utils.ZeroOrNil(roleArn) {
t.Logf("TASK_IAM_ROLE_ARN not set, will try to use the role attached to instance profile")
role, err := GetInstanceIAMRole()
require.NoError(t, err, "Error getting IAM Roles from instance profile")
roleArn = *role.Arn
}
tdOverride := make(map[string]string)
tdOverride["$$$TASK_ROLE$$$"] = roleArn

task, err := agent.StartTaskWithTaskDefinitionOverrides(t, "task-server-endpoint-validator", tdOverride)
require.NoError(t, err, "Error start task-server-endpoint-validator task")

err = task.WaitStopped(10 * time.Minute)
assert.NoError(t, err)
code, ok := task.ContainerExitcode("task_server_endpoint_validator_container")
assert.True(t, ok, "Get exit code failed")
assert.Equal(t, 42, code, "Wrong exit code")
}
8 changes: 4 additions & 4 deletions agent/handlers/task_server_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ func taskServerSetup(credentialsManager credentials.Manager,
containerInstanceArn string) *http.Server {
muxRouter := mux.NewRouter()

// Set this so that for request like "/v3//metadata/task", the Agent will pass
// it to task metadata handler instead of returning a 301 error.
muxRouter.SkipClean(true)
// Set this to false so that for request like "//v3//metadata/task"
// to permanently redirect(301) to "/v3/metadata/task" handler
muxRouter.SkipClean(false)
Copy link
Member

@fierlion fierlion Feb 14, 2019

Choose a reason for hiding this comment

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

Since this defaults to false (from the docs: SkipClean defines the path cleaning behaviour for new routes. The initial value is false.) I initially wondered if we even need the explicit setting here.
But I like it here. It's deliberate.
I would make the comment clearer (like the one in the docs: When false, the path will be cleaned, so //v3//metadata/task will become /v3/metadata/task)

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 a 301 redirect is more clear as it is not "becoming" the new url. it is a specific 301 redirect to the new url. let me know how you think

Copy link
Member

Choose a reason for hiding this comment

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

I see w.WriteHeader(http.StatusMovedPermanently) aka 301. Cool.


muxRouter.HandleFunc(v1.CredentialsPath,
v1.CredentialsHandler(credentialsManager, auditLogger))
Expand All @@ -79,7 +79,7 @@ func taskServerSetup(credentialsManager credentials.Manager,
loggingMuxRouter.Handle(rootPath, tollbooth.LimitHandler(
limiter, NewLoggingHandler(muxRouter)))

loggingMuxRouter.SkipClean(true)
loggingMuxRouter.SkipClean(false)

server := http.Server{
Addr: ":" + strconv.Itoa(config.AgentCredentialsPort),
Expand Down
35 changes: 31 additions & 4 deletions agent/handlers/task_server_setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,34 @@ func TestV3ContainerAssociation(t *testing.T) {
assert.Equal(t, expectedAssociationResponse, string(res))
}

func TestTaskHTTPEndpoint301Redirect(t *testing.T) {
testPathsMap := map[string]string{
"http://127.0.0.1/v3///task/": "http://127.0.0.1/v3/task/",
"http://127.0.0.1//v2/credentials/test": "http://127.0.0.1/v2/credentials/test",
}

ctrl := gomock.NewController(t)
defer ctrl.Finish()

state := mock_dockerstate.NewMockTaskEngineState(ctrl)
auditLog := mock_audit.NewMockAuditLogger(ctrl)
statsEngine := mock_stats.NewMockEngine(ctrl)
ecsClient := mock_api.NewMockECSClient(ctrl)

server := taskServerSetup(credentials.NewManager(), auditLog, state, ecsClient, clusterName, statsEngine,
config.DefaultTaskMetadataSteadyStateRate, config.DefaultTaskMetadataBurstRate, "", containerInstanceArn)

for testPath, expectedPath := range testPathsMap {
t.Run(fmt.Sprintf("Test path: %s", testPath), func(t *testing.T) {
recorder := httptest.NewRecorder()
req, _ := http.NewRequest("GET", testPath, nil)
server.Handler.ServeHTTP(recorder, req)
assert.Equal(t, http.StatusMovedPermanently, recorder.Code)
assert.Equal(t, expectedPath, recorder.Header().Get("Location"))
})
}
}

func TestTaskHTTPEndpointErrorCode404(t *testing.T) {
testPaths := []string{
"/",
Expand All @@ -1061,7 +1089,6 @@ func TestTaskHTTPEndpointErrorCode404(t *testing.T) {
"/v3/v3-endpoint-id/",
"/v3/v3-endpoint-id/wrong-path",
"/v3/v3-endpoint-id/task/",
"/v3///task/",
"/v3/v3-endpoint-id/task/stats/",
"/v3/v3-endpoint-id/task/stats/wrong-path",
"/v3/v3-endpoint-id/associtions-with-typo/elastic-inference/dev1",
Expand Down Expand Up @@ -1101,11 +1128,11 @@ func TestTaskHTTPEndpointErrorCode400(t *testing.T) {
"/v3/wrong-v3-endpoint-id",
"/v3/",
"/v3/wrong-v3-endpoint-id/stats",
"/v3//stats",
"/v3/stats",
Copy link
Contributor

Choose a reason for hiding this comment

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

Why was this changed? Shouldn't "/v3//stats" work as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we handle double/muti slash with a redirect prior to 1.20.3 release. at 1.21.0 release we changed the router and added the behavior to not clean double/multi slash. This is the root cause of this issue #1839.

"/v3/wrong-v3-endpoint-id/task",
"/v3//task",
"/v3/task",
"/v3/wrong-v3-endpoint-id/task/stats",
"/v3//task/stats",
"/v3/task/stats",
"/v3/wrong-v3-endpoint-id/associations/elastic-inference",
"/v3/wrong-v3-endpoint-id/associations/elastic-inference/dev1",
}
Expand Down