Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented new command to check if a set of container instances have…
… the attributes required to run a given task definition
- Loading branch information
1 parent
be660fb
commit 12483e0
Showing
8 changed files
with
244 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,4 +5,6 @@ bin/ | |
*.orig | ||
ecs-cli/vendor/pkg | ||
.vscode/* | ||
ecs-cli/.vscode/* | ||
ecs-cli/.idea | ||
.idea/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
172 changes: 172 additions & 0 deletions
172
ecs-cli/modules/cli/attributechecker/attribute_checker_app.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
// Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"). You may | ||
// not use this file except in compliance with the License. A copy of the | ||
// License is located at | ||
// | ||
// http://aws.amazon.com/apache2.0/ | ||
// | ||
// or in the "license" file accompanying this file. This file 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 attributechecker | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"strings" | ||
|
||
ecsclient "github.com/aws/amazon-ecs-cli/ecs-cli/modules/clients/aws/ecs" | ||
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/commands/flags" | ||
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/config" | ||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/docker/libcompose/project" | ||
"github.com/pkg/errors" | ||
"github.com/sirupsen/logrus" | ||
"github.com/urfave/cli" | ||
) | ||
|
||
const ( | ||
displayTitle = true | ||
containerInstanceHeader = "Container Instance" | ||
missingAttributesHeader = "Missing Attributes" | ||
) | ||
|
||
var infoColumns = []string{containerInstanceHeader, missingAttributesHeader} | ||
|
||
//AttributeChecker will compare task def and containers instances attributes and outputs missing attributes | ||
func AttributeChecker(c *cli.Context) { | ||
err := validateAttributeCheckerFlags(c) | ||
if err != nil { | ||
logrus.Fatal("Error executing 'Attribute Checker': ", err) | ||
} | ||
rdwr, err := config.NewReadWriter() | ||
if err != nil { | ||
logrus.Fatal("Error executing 'Attribute Checker': ", err) | ||
} | ||
commandConfig, err := config.NewCommandConfig(c, rdwr) | ||
if err != nil { | ||
logrus.Fatal("Error executing 'Attribute Checker': ", err) | ||
} | ||
|
||
ecsClient := ecsclient.NewECSClient(commandConfig) | ||
|
||
taskDefAttributeNames, err := taskdefattributesCheckRequest(c, ecsClient) | ||
if err != nil { | ||
logrus.Fatal("Error executing 'Attribute Checker': ", err) | ||
} | ||
if len(taskDefAttributeNames) == 0 { | ||
logrus.Info("The given task definition does not have any attributes") | ||
return | ||
} | ||
|
||
descrContainerInstancesResponse, err := describeContainerInstancesAttributeMap(c, ecsClient, commandConfig) | ||
if err != nil { | ||
logrus.Fatal("Error executing 'Attribute Checker': ", err) | ||
} | ||
|
||
compareOutput := compare(taskDefAttributeNames, descrContainerInstancesResponse) | ||
result := ConvertToInfoSet(compareOutput) | ||
os.Stdout.WriteString(result.String(infoColumns, displayTitle)) | ||
} | ||
|
||
func contains(containerInstanceAttributeNames []*string, tdAttrNames *string) bool { | ||
for _, containerInstAttrNames := range containerInstanceAttributeNames { | ||
if *containerInstAttrNames == *tdAttrNames { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
//compares between container instances and Task definition | ||
func compare(taskDefAttributeNames []*string, descrContainerInstancesResponse map[string][]*string) map[string]string { | ||
attributeCheckerResult := make(map[string]string) | ||
for containerInstanceARN, containerInstanceAttributeNames := range descrContainerInstancesResponse { | ||
var missingAttributes []string | ||
for _, tdAttrNames := range taskDefAttributeNames { | ||
if !contains(containerInstanceAttributeNames, tdAttrNames) { | ||
missingAttributes = append(missingAttributes, *tdAttrNames) | ||
} | ||
} | ||
missingAttributesNames := strings.Join(missingAttributes, ", ") | ||
if len(missingAttributesNames) == 0 { | ||
missingAttributesNames = "None" | ||
} | ||
containerInstance := strings.Split(containerInstanceARN, "/") | ||
attributeCheckerResult[containerInstance[1]] = missingAttributesNames | ||
} | ||
return attributeCheckerResult | ||
} | ||
|
||
// DescribeContainerInstancesAttributeMap and get a map with Container instance ARN and Container instances attribute Names | ||
func describeContainerInstancesAttributeMap(context *cli.Context, ecsClient ecsclient.ECSClient, commandConfig *config.CommandConfig) (map[string][]*string, error) { | ||
if err := validateCluster(commandConfig.Cluster, ecsClient); err != nil { | ||
return nil, err | ||
} | ||
var containerInstanceIdentifiers []*string | ||
containerInstanceIdentifier := context.String(flags.ContainerInstancesFlag) | ||
splitValues := strings.Split(containerInstanceIdentifier, ",") | ||
containerInstanceIdentifiers = aws.StringSlice(splitValues) | ||
|
||
descrContainerInstancesAttributes, err := ecsClient.GetAttributesFromDescribeContainerInstances(containerInstanceIdentifiers) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, fmt.Sprintf("Failed to Describe Container Instances, please check region/containerInstance values")) | ||
} | ||
return descrContainerInstancesAttributes, err | ||
} | ||
|
||
// validateCluster validates if the cluster exists in ECS and is in "ACTIVE" state. | ||
func validateCluster(clusterName string, ecsClient ecsclient.ECSClient) error { | ||
isClusterActive, err := ecsClient.IsActiveCluster(clusterName) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if !isClusterActive { | ||
return fmt.Errorf("Cluster '%s' is not active. Ensure that it exists", clusterName) | ||
} | ||
return nil | ||
} | ||
|
||
//taskdefattributesCheckRequest describes task def and gets all attribute Names from the task definition | ||
func taskdefattributesCheckRequest(context *cli.Context, ecsClient ecsclient.ECSClient) ([]*string, error) { | ||
|
||
taskDefIdentifier := context.String(flags.TaskDefinitionFlag) | ||
|
||
descrTaskDefinition, err := ecsClient.DescribeTaskDefinition(taskDefIdentifier) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, fmt.Sprintf("Failed to Describe TaskDefinition, please check the region/taskDefinition values")) | ||
} | ||
var taskattributeNames []*string | ||
for _, taskDefAttributesName := range descrTaskDefinition.RequiresAttributes { | ||
taskattributeNames = append(taskattributeNames, taskDefAttributesName.Name) | ||
} | ||
return taskattributeNames, err | ||
} | ||
|
||
//validates all required flags are passed to run the command | ||
func validateAttributeCheckerFlags(context *cli.Context) error { | ||
if taskDefIdentifier := context.String(flags.TaskDefinitionFlag); taskDefIdentifier == "" { | ||
return fmt.Errorf("TaskDefinition must be specified with the --%s flag", flags.TaskDefinitionFlag) | ||
} | ||
if containerInstanceIdentifier := context.String(flags.ContainerInstancesFlag); containerInstanceIdentifier == "" { | ||
return fmt.Errorf("ContainerInstance(s) must be specified with the --%s flag", flags.ContainerInstancesFlag) | ||
} | ||
return nil | ||
} | ||
|
||
//ConvertToInfoSet transforms the Map of containerARN and MissingAttributes into a formatted set of fields | ||
func ConvertToInfoSet(compareOutput map[string]string) project.InfoSet { | ||
result := project.InfoSet{} | ||
for key, element := range compareOutput { | ||
info := project.Info{ | ||
containerInstanceHeader: key, | ||
missingAttributesHeader: element, | ||
} | ||
result = append(result, info) | ||
} | ||
return result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
ecs-cli/modules/commands/attributechecker/attribute_checker_command.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package attributecheckercommand | ||
|
||
import ( | ||
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/cli/attributechecker" | ||
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/commands/flags" | ||
"github.com/urfave/cli" | ||
) | ||
|
||
// AttributecheckerCommand checks if all Capabilities/attributes are available to run the task on a specified Cluster or on a given Container Instance specified. | ||
func AttributecheckerCommand() cli.Command { | ||
return cli.Command{ | ||
Name: "check-attributes", | ||
Usage: "Checks if a given list of container instances can run a given task definition by checking their attributes. Outputs attributes that are required by the task definition but not present on the container instances.", | ||
Flags: append(flags.OptionalConfigFlags(), attributecheckerFlags()...), | ||
Action: attributechecker.AttributeChecker, | ||
OnUsageError: flags.UsageErrorFactory("attribute-checker"), | ||
} | ||
} | ||
|
||
func attributecheckerFlags() []cli.Flag { | ||
return []cli.Flag{ | ||
cli.StringFlag{ | ||
Name: flags.TaskDefinitionFlag, | ||
Usage: "Specifies the name or full Amazon Resource Name (ARN) of the ECS Task Definition. This is required to gather attributes of a Task Definition.", | ||
}, | ||
cli.StringFlag{ | ||
Name: flags.ContainerInstancesFlag, | ||
Usage: "A list of container instance IDs or full ARN entries to check if all required attributes are available for the Task Definition to RunTask.", | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters