diff --git a/.changelog/2098.txt b/.changelog/2098.txt new file mode 100644 index 00000000000..35721b8b5ff --- /dev/null +++ b/.changelog/2098.txt @@ -0,0 +1,23 @@ +```release-note:improvement +plugin/aws-ecs: More complete list of resources displayed in `waypoint deploy` logs +``` + +```release-note:improvement +plugin/aws-ecs: Support for status reports, enabling `waypoint status` for ecs deployments +``` + +```release-note:improvement +plugin/aws-ecs: Deployments delete their resources on failure. +``` + +```release-note:improvement +plugin/aws-ecs: Improve security of ecs tasks by restricting ingress to the ALB +``` + +```release-note:bug +plugin/aws-ecs: Fix panic when specifying a sidecar without a healthcheck +``` + +```release-note:improvement +plugin/aws-ecs: Error messages contain additional context +``` diff --git a/builtin/aws/ecs/main.go b/builtin/aws/ecs/main.go index 575183e2502..30dc31f99ff 100644 --- a/builtin/aws/ecs/main.go +++ b/builtin/aws/ecs/main.go @@ -1,11 +1,13 @@ package ecs import ( - "github.com/hashicorp/waypoint-plugin-sdk" + sdk "github.com/hashicorp/waypoint-plugin-sdk" ) //go:generate protoc -I ../../../.. --go_opt=plugins=grpc --go_out=../../../.. waypoint/builtin/aws/ecs/plugin.proto +const platformName = "aws-ecs" + // Options are the SDK options to use for instantiation. var Options = []sdk.Option{ sdk.WithComponents(&Platform{}), diff --git a/builtin/aws/ecs/platform.go b/builtin/aws/ecs/platform.go index 53b4848fea4..afa1a7cc447 100644 --- a/builtin/aws/ecs/platform.go +++ b/builtin/aws/ecs/platform.go @@ -2,6 +2,7 @@ package ecs import ( "context" + "encoding/json" "fmt" "sort" "strconv" @@ -19,13 +20,26 @@ import ( "github.com/aws/aws-sdk-go/service/route53" validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/hashicorp/go-hclog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/timestamppb" + "github.com/hashicorp/waypoint-plugin-sdk/component" "github.com/hashicorp/waypoint-plugin-sdk/docs" + "github.com/hashicorp/waypoint-plugin-sdk/framework/resource" + sdk "github.com/hashicorp/waypoint-plugin-sdk/proto/gen" "github.com/hashicorp/waypoint-plugin-sdk/terminal" "github.com/hashicorp/waypoint/builtin/aws/utils" "github.com/hashicorp/waypoint/builtin/docker" ) +const ( + executionRolePolicyArn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" + awsCreateRetries = 30 + awsCreateRetryIntervalSeconds = 2 + defaultServicePort = 3000 +) + type Platform struct { config Config } @@ -41,7 +55,7 @@ func (p *Platform) ConfigSet(config interface{}) error { c, ok := config.(*Config) if !ok { // this should never happen - return fmt.Errorf("Invalid configuration, expected *cloudrun.Config, got %T", config) + return status.Errorf(codes.FailedPrecondition, "invalid configuration, expected *ecs.Config, got %T", config) } if c.ALB != nil { @@ -119,718 +133,848 @@ func (p *Platform) ValidateAuth() error { return nil } +// StatusFunc implements component.Status +func (p *Platform) StatusFunc() interface{} { + return p.Status +} + // DefaultReleaserFunc implements component.PlatformReleaser func (p *Platform) DefaultReleaserFunc() interface{} { return func() *Releaser { return &Releaser{p: p} } } -type Lifecycle struct { - Init func(LifecycleStatus) error - Run func(LifecycleStatus) error - Cleanup func(LifecycleStatus) error +func (p *Platform) resourceManager(log hclog.Logger, dcr *component.DeclaredResourcesResp) *resource.Manager { + return resource.NewManager( + resource.WithLogger(log.Named("resource_manager")), + resource.WithValueProvider(p.getSession), + resource.WithDeclaredResourcesResp(dcr), + resource.WithResource(resource.NewResource( + resource.WithName("cluster"), + resource.WithPlatform(platformName), + resource.WithState(&Resource_Cluster{}), + resource.WithCreate(p.resourceClusterCreate), + // TODO: implement destroy when we have better support for app-scoped resources + resource.WithStatus(p.resourceClusterStatus), + resource.WithCategoryDisplayHint(sdk.ResourceCategoryDisplayHint_OTHER), + )), + resource.WithResource(resource.NewResource( + resource.WithName("execution role"), + resource.WithType("IAM role"), + resource.WithPlatform(platformName), + resource.WithState(&Resource_ExecutionRole{}), + resource.WithCreate(p.resourceExecutionRoleCreate), + // TODO: implement destroy when we have better support for app-scoped resources + // TODO: implement status when we have a plan to not hit rate limits + resource.WithCategoryDisplayHint(sdk.ResourceCategoryDisplayHint_POLICY), + )), + resource.WithResource(resource.NewResource( + resource.WithName("task role"), + resource.WithType("IAM role"), + resource.WithPlatform(platformName), + resource.WithState(&Resource_TaskRole{}), + resource.WithCreate(p.resourceTaskRoleCreate), + // TODO: implement destroy when we have better support for app-scoped resources + // TODO: implement status when we have a plan to not hit rate limits + resource.WithCategoryDisplayHint(sdk.ResourceCategoryDisplayHint_POLICY), + )), + resource.WithResource(resource.NewResource( + resource.WithName("internal security groups"), + resource.WithPlatform(platformName), + resource.WithType("security groups"), + resource.WithState(&Resource_InternalSecurityGroups{}), + resource.WithCreate(p.resourceInternalSecurityGroupsCreate), + // TODO: implement destroy when we have better support for app-scoped resources + // TODO: implement status when we have a plan to not hit rate limits + resource.WithCategoryDisplayHint(sdk.ResourceCategoryDisplayHint_POLICY), + )), + resource.WithResource(resource.NewResource( + resource.WithName("external security groups"), + resource.WithPlatform(platformName), + resource.WithType("security groups"), + resource.WithState(&Resource_ExternalSecurityGroups{}), + resource.WithCreate(p.resourceExternalSecurityGroupsCreate), + // TODO: implement destroy when we have better support for app-scoped resources + // TODO: implement status when we have a plan to not hit rate limits + resource.WithCategoryDisplayHint(sdk.ResourceCategoryDisplayHint_POLICY), + )), + resource.WithResource(resource.NewResource( + resource.WithName("log group"), + resource.WithPlatform(platformName), + resource.WithState(&Resource_LogGroup{}), + resource.WithCreate(p.resourceLogGroupCreate), + // TODO: implement destroy when we have better support for waypoint global resources + // TODO: implement status when we have a plan to not hit rate limits + resource.WithCategoryDisplayHint(sdk.ResourceCategoryDisplayHint_OTHER), + )), + resource.WithResource(resource.NewResource( + resource.WithName("subnets"), + resource.WithPlatform(platformName), + resource.WithState(&Resource_Subnets{}), + resource.WithCreate(p.resourceSubnetsDiscover), + // We never create subnets, and therefore should never destroy them + // TODO: implement status when we have a plan to not hit rate limits + resource.WithCategoryDisplayHint(sdk.ResourceCategoryDisplayHint_OTHER), + )), + resource.WithResource(resource.NewResource( + resource.WithName("target group"), + resource.WithPlatform(platformName), + resource.WithState(&Resource_TargetGroup{}), + resource.WithCreate(p.resourceTargetGroupCreate), + resource.WithDestroy(p.resourceTargetGroupDestroy), + // TODO: implement status when we have a plan to not hit rate limits + resource.WithCategoryDisplayHint(sdk.ResourceCategoryDisplayHint_OTHER), + )), + resource.WithResource(resource.NewResource( + resource.WithName("application load balancer"), + resource.WithPlatform(platformName), + resource.WithState(&Resource_Alb{}), + resource.WithCreate(p.resourceAlbCreate), + // TODO: implement destroy when we have better support for app-scoped resources + // TODO: implement status when we have a plan to not hit rate limits + resource.WithCategoryDisplayHint(sdk.ResourceCategoryDisplayHint_ROUTER), + )), + resource.WithResource(resource.NewResource( + resource.WithName("alb listener"), + resource.WithPlatform(platformName), + resource.WithState(&Resource_Alb_Listener{}), + resource.WithCreate(p.resourceAlbListenerCreate), + resource.WithDestroy(p.resourceAlbListenerDestroy), + // TODO: implement status when we have a plan to not hit rate limits + resource.WithCategoryDisplayHint(sdk.ResourceCategoryDisplayHint_ROUTER), + )), + resource.WithResource(resource.NewResource( + resource.WithName("route53 record"), + resource.WithPlatform(platformName), + resource.WithState(&Resource_Route53Record{}), + resource.WithCreate(p.resourceRoute53RecordCreate), + // TODO: implement destroy when we have better support for app-scoped resources + // TODO: implement status when we have a plan to not hit rate limits + resource.WithCategoryDisplayHint(sdk.ResourceCategoryDisplayHint_ROUTER), + )), + resource.WithResource(resource.NewResource( + resource.WithName("task definition"), + resource.WithPlatform(platformName), + resource.WithState(&Resource_TaskDefinition{}), + resource.WithCreate(p.resourceTaskDefinitionCreate), + // TODO: implement destroy when we have better support for app-scoped resources. + // TODO: implement status when we have a plan to not hit rate limits + resource.WithCategoryDisplayHint(sdk.ResourceCategoryDisplayHint_INSTANCE_MANAGER), + )), + resource.WithResource(resource.NewResource( + resource.WithName("service"), + resource.WithPlatform(platformName), + resource.WithState(&Resource_Service{}), + resource.WithCreate(p.resourceServiceCreate), + resource.WithDestroy(p.resourceServiceDestroy), + resource.WithStatus(p.resourceServiceStatus), + resource.WithCategoryDisplayHint(sdk.ResourceCategoryDisplayHint_INSTANCE_MANAGER), + )), + ) } -type lStatus struct { - ui terminal.UI - sg terminal.StepGroup - step terminal.Step -} +// DeploymentId is a unique ID to be consistently used throughout our deployment +type DeploymentId string -func (l *lStatus) Status(str string, args ...interface{}) { - if l.sg == nil { - l.sg = l.ui.StepGroup() - } +// ExternalIngressPort is the port that the ALB will listen for traffic on +type ExternalIngressPort int64 - if l.step != nil { - l.step.Done() - l.step = nil - } +func (p *Platform) Deploy( + ctx context.Context, + log hclog.Logger, + src *component.Source, + img *docker.Image, + deployConfig *component.DeploymentConfig, + ui terminal.UI, + dcr *component.DeclaredResourcesResp, +) (*Deployment, error) { + var result Deployment - l.step = l.sg.Add(str, args...) -} + // We'll update the user in real time + sg := ui.StepGroup() + defer sg.Wait() -func (l *lStatus) Update(str string, args ...interface{}) { - if l.sg == nil { - l.sg = l.ui.StepGroup() - } + s := sg.Add("Initializing deployment...") + defer s.Abort() - if l.step != nil { - l.step.Update(str, args...) - } else { - l.step = l.sg.Add(str, args) + // Generate a common deployment ID to use in the resources we create. + // TODO: should include the sequence ID + ulid, err := component.Id() + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to generate a ULID: %s", err) } -} + deploymentId := DeploymentId(fmt.Sprintf("%s-%s", src.App, ulid)) -func (l *lStatus) Error(str string, args ...interface{}) { - if l.sg == nil { - l.sg = l.ui.StepGroup() + // Set default port - it's used for multiple resources + if p.config.ServicePort != 0 { + log.Debug("Using configured service port %d", p.config.ServicePort) + } else { + log.Debug("Using the default service port %d", defaultServicePort) + p.config.ServicePort = int64(defaultServicePort) + } + + // Set ALB ingress port - used for multiple resources + var externalIngressPort ExternalIngressPort + if p.config.ALB != nil && p.config.ALB.IngressPort != 0 { + log.Debug("Using configured ingress port %d", p.config.ServicePort) + externalIngressPort = ExternalIngressPort(p.config.ALB.IngressPort) + } else if p.config.ALB != nil && p.config.ALB.CertificateId != "" { + log.Debug("ALB config defined and cert configured, using ingress port 443") + externalIngressPort = ExternalIngressPort(443) + } else { + log.Debug("Defaulting external ingress port to 80") + externalIngressPort = ExternalIngressPort(80) } - if l.step != nil { - l.step.Update(str, args...) - l.step.Abort() - } else { - l.step = l.sg.Add(str, args) - l.step.Abort() + // Create our resource manager and create + rm := p.resourceManager(log, dcr) + if err := rm.CreateAll( + ctx, log, sg, ui, deploymentId, externalIngressPort, + src, img, deployConfig, &result, + ); err != nil { + return nil, status.Errorf(status.Convert(err).Code(), "failed to create deployment resources: %s", err) } - l.step = nil -} + // Store our resource state + result.ResourceState = rm.State() -func (l *lStatus) Abort() error { - if l.step != nil { - l.step.Abort() - l.step = nil - } + // Get other state required for older versions to destroy this deployment + srState := rm.Resource("service").State().(*Resource_Service) + result.ServiceArn = srState.Arn - if l.sg != nil { - l.sg.Wait() - l.sg = nil - } + tgState := rm.Resource("target group").State().(*Resource_TargetGroup) + result.TargetGroupArn = tgState.Arn - return nil -} + albState := rm.Resource("application load balancer").State().(*Resource_Alb) + result.LoadBalancerArn = albState.Arn -func (l *lStatus) Close() error { - if l.step != nil { - l.step.Done() - l.step = nil - } + cState := rm.Resource("cluster").State().(*Resource_Cluster) + result.Cluster = cState.Name - if l.sg != nil { - l.sg.Wait() - l.sg = nil - } + tdState := rm.Resource("task definition").State().(*Resource_TaskDefinition) + result.TaskArn = tdState.Arn - return nil + s.Update("Deployment resources created") + s.Done() + return &result, nil } -func (lf *Lifecycle) Execute(L hclog.Logger, ui terminal.UI) error { - var l lStatus - l.ui = ui +func (p *Platform) Status( + ctx context.Context, + log hclog.Logger, + deployment *Deployment, + ui terminal.UI, +) (*sdk.StatusReport, error) { + sg := ui.StepGroup() + defer sg.Wait() - defer l.Close() + s := sg.Add("Gathering health report for ecs deployment...") + defer s.Abort() - if lf.Init != nil { - L.Debug("lifecycle init") + rm := p.resourceManager(log, nil) - err := lf.Init(&l) - if err != nil { - l.Abort() - return err + // If we don't have resource state, this state is from an older version + // and we need to manually recreate it. + if deployment.ResourceState == nil { + if err := p.loadResourceManagerState(ctx, rm, deployment, log, sg); err != nil { + return nil, status.Errorf(codes.Internal, "failed recovering old state into resource manager: %s", err) + } + } else { + // Load our set state + if err := rm.LoadState(deployment.ResourceState); err != nil { + return nil, status.Errorf(codes.Internal, "failed loading state into resource manager: %s", err) } - } - L.Debug("lifecycle run") - err := lf.Run(&l) + result, err := rm.StatusReport(ctx, log, sg) if err != nil { - l.Abort() - return err + return nil, status.Errorf(status.Convert(err).Code(), "resource manager failed to generate a status report: %s", err) } - if lf.Cleanup != nil { - L.Debug("lifecycle cleanup") + s.Update("Finished building report for ecs deployment") + s.Done() - err = lf.Cleanup(&l) - if err != nil { - l.Abort() - return err + // NOTE(briancain): Replace ui.Status with StepGroups once this bug + // has been fixed: https://github.com/hashicorp/waypoint/issues/1536 + st := ui.Status() + defer st.Close() + + st.Update("Determining overall health for ecs deployment...") + if result.Health == sdk.StatusReport_READY { + st.Step(terminal.StatusOK, result.HealthMessage) + } else { + if result.Health == sdk.StatusReport_PARTIAL { + st.Step(terminal.StatusWarn, result.HealthMessage) + } else { + st.Step(terminal.StatusError, result.HealthMessage) } - } - return nil -} + // Extra advisory wording to let user know that the deployment could be still starting up + // if the report was generated immediately after it was deployed or released. + st.Step(terminal.StatusWarn, mixedHealthWarn) + } -type LifecycleStatus interface { - Status(str string, args ...interface{}) - Update(str string, args ...interface{}) - Error(str string, args ...interface{}) + return result, nil } -func (p *Platform) Deploy( +func (p *Platform) Destroy( ctx context.Context, log hclog.Logger, - src *component.Source, - img *docker.Image, - deployConfig *component.DeploymentConfig, + deployment *Deployment, ui terminal.UI, -) (*Deployment, error) { - var ( - sess *session.Session - dep *Deployment +) error { - executionRole, taskRole, cluster, logGroup string + sg := ui.StepGroup() + defer sg.Wait() - err error - ) + s := sg.Add("Destroying ecs deployment...") + defer s.Abort() - if p.config.ALB != nil { - if p.config.ALB.ListenerARN != "" { - if p.config.ALB.ZoneId != "" || p.config.ALB.FQDN != "" { - return nil, fmt.Errorf("When using an existing listener, Route53 setup is not available") - } + rm := p.resourceManager(log, nil) - if p.config.ALB.CertificateId != "" { - return nil, fmt.Errorf("When using an existing listener, certification configuration is not available") - } + // If we don't have resource state, this state is from an older version + // and we need to manually recreate it. + if deployment.ResourceState == nil { + if err := p.loadResourceManagerState(ctx, rm, deployment, log, sg); err != nil { + return status.Errorf(codes.Internal, "failed recovering old state into resource manager: %s", err) + } + } else { + // Load our set state + if err := rm.LoadState(deployment.ResourceState); err != nil { + return status.Errorf(codes.Internal, "failed loading state into resource manager: %s", err) } } - if p.config.ServicePort == 0 { - p.config.ServicePort = 3000 - } - - lf := &Lifecycle{ - Init: func(s LifecycleStatus) error { - sess, err = utils.GetSession(&utils.SessionConfig{ - Region: p.config.Region, - Logger: log, - }) - if err != nil { - return err - } - cluster, err = p.SetupCluster(ctx, s, sess) - if err != nil { - return err - } - - executionRole, err = p.SetupExecutionRole(ctx, s, log, sess, src) - if err != nil { - return err - } - - taskRole, err = p.SetupTaskRole(ctx, s, log, sess, src) - if err != nil { - return err - } - - logGroup, err = p.SetupLogs(ctx, s, log, sess) - if err != nil { - return err - } - - return nil - }, - - Run: func(s LifecycleStatus) error { - dep, err = p.Launch(ctx, s, log, ui, sess, src, img, deployConfig, executionRole, taskRole, cluster, logGroup) - return err - }, - - Cleanup: func(s LifecycleStatus) error { return nil }, - } - - if err := lf.Execute(log, ui); err != nil { - return nil, err - } - - return dep, nil -} - -func defaultSubnets(ctx context.Context, sess *session.Session) ([]*string, error) { - svc := ec2.New(sess) - - desc, err := svc.DescribeSubnets(&ec2.DescribeSubnetsInput{ - Filters: []*ec2.Filter{ - { - Name: aws.String("default-for-az"), - Values: []*string{aws.String("true")}, - }, - }, - }) + // Destroy + err := rm.DestroyAll(ctx, log, sg, ui) if err != nil { - return nil, err - } - - var subnets []*string - - for _, subnet := range desc.Subnets { - subnets = append(subnets, subnet.SubnetId) + return status.Errorf(status.Convert(err).Code(), "failed to destroy all resources for deployment: %s", err) } - return subnets, nil + s.Update("Finished destroying ECS deployment") + s.Done() + return nil } -func (p *Platform) SetupCluster(ctx context.Context, s LifecycleStatus, sess *session.Session) (string, error) { - ecsSvc := ecs.New(sess) +func (p *Platform) resourceClusterCreate( + ctx context.Context, + sg terminal.StepGroup, + log hclog.Logger, + sess *session.Session, + state *Resource_Cluster, +) error { + s := sg.Add("Initiating cluster creation...") + defer s.Abort() cluster := p.config.Cluster if cluster == "" { cluster = "waypoint" } + state.Name = cluster - desc, err := ecsSvc.DescribeClusters(&ecs.DescribeClustersInput{ + s.Update("Attempting to find existing cluster named %q", cluster) + + ecsSvc := ecs.New(sess) + desc, err := ecsSvc.DescribeClustersWithContext(ctx, &ecs.DescribeClustersInput{ Clusters: []*string{aws.String(cluster)}, }) if err != nil { - return "", err + return err } for _, c := range desc.Clusters { - if *c.ClusterName == cluster && strings.ToLower(*c.Status) == "active" { - s.Status("Found existing ECS cluster: %s", cluster) - return cluster, nil + if *c.ClusterName == cluster { + if *c.Status == "PROVISIONING" { + s.Update("Existing ecs cluster %q is still provisioning - try again later.", cluster) + } else if *c.Status == "ACTIVE" { + s.Update("Using existing ECS cluster %s", cluster) + if c.ClusterArn != nil { + state.Arn = *c.ClusterArn + } + s.Done() + return nil + } else { + // Warn if we encounter waypoint clusters in other odd states (i.e. DEPROVISIONING, FAILED, etc.) + // I think it's ok to try to create a new cluster if one exists in a non-active non-provisioning state + log.Warn("Ignoring cluster named %q in state %q", cluster, *c.Status) + } } } if p.config.EC2Cluster { - return "", fmt.Errorf("EC2 clusters can not be automatically created") + return status.Errorf(codes.FailedPrecondition, "EC2 clusters can not be automatically created") } - s.Status("Creating new ECS cluster: %s", cluster) + s.Update("No existing cluster found - creating new ECS cluster: %s", cluster) - _, err = ecsSvc.CreateCluster(&ecs.CreateClusterInput{ + c, err := ecsSvc.CreateClusterWithContext(ctx, &ecs.CreateClusterInput{ ClusterName: aws.String(cluster), }) if err != nil { - return "", err + return err } - s.Update("Created new ECS cluster: %s", cluster) - return cluster, nil -} - -const rolePolicy = `{ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "", - "Effect": "Allow", - "Principal": { - "Service": "ecs-tasks.amazonaws.com" - }, - "Action": "sts:AssumeRole" - } - ] -}` - -var fargateResources = map[int][]int{ - 512: {256}, - 1024: {256, 512}, - 2048: {256, 512, 1024}, - 3072: {512, 1024}, - 4096: {512, 1024}, - 5120: {1024}, - 6144: {1024}, - 7168: {1024}, - 8192: {1024}, -} - -func init() { - for i := 4096; i < 16384; i += 1024 { - fargateResources[i] = append(fargateResources[i], 2048) + if c.Cluster != nil && c.Cluster.ClusterArn != nil { + state.Arn = *c.Cluster.ClusterArn } - for i := 8192; i <= 30720; i += 1024 { - fargateResources[i] = append(fargateResources[i], 4096) - } + s.Update("Created ECS cluster: %s", cluster) + s.Done() + return nil } -func (p *Platform) SetupTaskRole(ctx context.Context, s LifecycleStatus, L hclog.Logger, sess *session.Session, app *component.Source) (string, error) { - svc := iam.New(sess) - - roleName := p.config.TaskRoleName - - if roleName == "" && p.config.TaskRolePolicyArns == nil { - return "", nil - } - - if roleName == "" { - roleName = app.App + "-task-role" - } +func (p *Platform) resourceClusterStatus( + ctx context.Context, + sg terminal.StepGroup, + sess *session.Session, + state *Resource_Cluster, + sr *resource.StatusResponse, +) error { + s := sg.Add("Checking status of the ecs cluster %q...", state.Name) + defer s.Abort() - // role names have to be 64 characters or less, and the client side doesn't validate this. - if len(roleName) > 64 { - roleName = roleName[:64] - L.Debug("using a shortened value for role name due to AWS's length limits", "roleName", roleName) + ecsSvc := ecs.New(sess) + desc, err := ecsSvc.DescribeClustersWithContext(ctx, &ecs.DescribeClustersInput{ + Clusters: []*string{aws.String(state.Name)}, + }) + if err != nil { + return status.Errorf(codes.Internal, "failed to describe cluster named %q (ARN: %q): %s", state.Name, state.Arn, err) } - L.Debug("attempting to retrieve existing role", "role-name", roleName) - - queryInput := &iam.GetRoleInput{ - RoleName: aws.String(roleName), + clusterResource := sdk.StatusReport_Resource{ + Name: state.Name, } - var roleArn string - - getOut, err := svc.GetRoleWithContext(ctx, queryInput) - if err != nil { - if aerr, ok := err.(awserr.Error); ok { - switch aerr.Code() { - case "NoSuchEntity": - L.Debug("creating new role") - s.Status("Creating IAM role: %s", roleName) - - input := &iam.CreateRoleInput{ - AssumeRolePolicyDocument: aws.String(rolePolicy), - Path: aws.String("/"), - RoleName: aws.String(roleName), - } - - result, err := svc.CreateRoleWithContext(ctx, input) - if err != nil { - return "", err - } + sr.Resources = append(sr.Resources, &clusterResource) - roleArn = *result.Role.Arn + for _, c := range desc.Clusters { + if *c.ClusterName == state.Name { + s.Update("Found existing ECS cluster: %s", state.Name) + clusterResource.Id = *c.ClusterArn + switch *c.Status { + case "ACTIVE": + clusterResource.Health = sdk.StatusReport_READY + case "PROVISIONING": + clusterResource.Health = sdk.StatusReport_ALIVE + case "DEPROVISIONING", "FAILED", "INACTIVE": + clusterResource.Health = sdk.StatusReport_DOWN default: - return "", err - } - } else { - return "", err - } - } else { - roleArn = *getOut.Role.Arn - } - - listOut, err := svc.ListAttachedRolePoliciesWithContext(ctx, &iam.ListAttachedRolePoliciesInput{ - RoleName: &roleName, - }) - - if err != nil { - return "", err - } - - for _, aPolicy := range p.config.TaskRolePolicyArns { - found := false - for _, policy := range listOut.AttachedPolicies { - if aPolicy == *policy.PolicyArn { - found = true - break + clusterResource.Health = sdk.StatusReport_UNKNOWN } - } + clusterResource.HealthMessage = *c.Status - if !found { - _, err = svc.AttachRolePolicyWithContext(ctx, &iam.AttachRolePolicyInput{ - PolicyArn: &aPolicy, - RoleName: &roleName, - }) + stateJson, err := json.Marshal(c) if err != nil { - return "", err + return status.Errorf(codes.Internal, "failed to marshal ecs cluster state json: %s", err) } + clusterResource.StateJson = string(stateJson) + + s.Done() + return nil } } - L.Debug("attached task role policy") + // Failed to find ECS cluster + clusterResource.Health = sdk.StatusReport_MISSING + clusterResource.HealthMessage = fmt.Sprintf("No cluster named %q found (expected arn %q)", state.Name, state.Arn) - s.Update("Created IAM role: %s", roleName) - return roleArn, nil + s.Update("Done checking ecs cluster status") + s.Done() + return nil } -func (p *Platform) SetupExecutionRole(ctx context.Context, s LifecycleStatus, L hclog.Logger, sess *session.Session, app *component.Source) (string, error) { - svc := iam.New(sess) - - roleName := p.config.ExecutionRoleName +func (p *Platform) resourceServiceCreate( + ctx context.Context, + sg terminal.StepGroup, + log hclog.Logger, + sess *session.Session, + src *component.Source, + deploymentId DeploymentId, + state *Resource_Service, - if roleName == "" { - roleName = "ecr-" + app.App - } + // Outputs of other resource creation processes + taskDefinition *Resource_TaskDefinition, + cluster *Resource_Cluster, + targetGroup *Resource_TargetGroup, + subnets *Resource_Subnets, + securityGroups *Resource_InternalSecurityGroups, - // role names have to be 64 characters or less, and the client side doesn't validate this. - if len(roleName) > 64 { - roleName = roleName[:64] - L.Debug("using a shortened value for role name due to AWS's length limits", "roleName", roleName) - } + _ *Resource_Alb_Listener, // Necessary dependency. service creation will fail unless this exists and the target group has been added. +) error { + s := sg.Add("Initiating ecs service creation") + defer s.Abort() - // p.updateStatus("setting up IAM role") - L.Debug("attempting to retrieve existing role", "role-name", roleName) + // Use the common deployment ID as our service name + serviceName := string(deploymentId) - queryInput := &iam.GetRoleInput{ - RoleName: aws.String(roleName), + // We have to clamp at a length of 32 because the Name field + // requires that the name is 32 characters or less. + if len(serviceName) > 32 { + serviceName = serviceName[:32] + log.Debug("using a shortened value for service name due to AWS's length limits", "serviceName", serviceName) } - getOut, err := svc.GetRole(queryInput) - if err == nil { - s.Status("Found existing IAM role to use: %s", roleName) - return *getOut.Role.Arn, nil + count := int64(p.config.Count) + if count == 0 { + count = 1 } - L.Debug("creating new role") - s.Status("Creating IAM role: %s", roleName) - - input := &iam.CreateRoleInput{ - AssumeRolePolicyDocument: aws.String(rolePolicy), - Path: aws.String("/"), - RoleName: aws.String(roleName), + securityGroupIds := make([]*string, len(securityGroups.SecurityGroups)) + for i, securityGroup := range securityGroups.SecurityGroups { + securityGroupIds[i] = &securityGroup.Id } - result, err := svc.CreateRole(input) - if err != nil { - return "", err + subnetIds := make([]*string, len(subnets.Subnets)) + for i, subnet := range subnets.Subnets { + subnetIds[i] = &subnet.Id } - roleArn := *result.Role.Arn - - L.Debug("created new role", "arn", roleArn) - - aInput := &iam.AttachRolePolicyInput{ - RoleName: aws.String(roleName), - PolicyArn: aws.String("arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"), + netCfg := &ecs.AwsVpcConfiguration{ + Subnets: subnetIds, + SecurityGroups: securityGroupIds, } - _, err = svc.AttachRolePolicy(aInput) - if err != nil { - return "", err + if !p.config.EC2Cluster { + netCfg.AssignPublicIp = aws.String("ENABLED") } - L.Debug("attached execution role policy") - - s.Update("Created IAM role: %s", roleName) - return roleArn, nil -} - -func (p *Platform) SetupLogs(ctx context.Context, s LifecycleStatus, L hclog.Logger, sess *session.Session) (string, error) { - // e.updateStatus("setting up CloudWatchLogs") + state.Cluster = cluster.Name - logGroup := p.config.LogGroup - if logGroup == "" { - logGroup = "waypoint-logs" + createServiceInput := &ecs.CreateServiceInput{ + Cluster: &cluster.Name, + DesiredCount: aws.Int64(count), + LaunchType: &taskDefinition.Runtime, + ServiceName: aws.String(serviceName), + TaskDefinition: aws.String(taskDefinition.Arn), + NetworkConfiguration: &ecs.NetworkConfiguration{ + AwsvpcConfiguration: netCfg, + }, } - cwl := cloudwatchlogs.New(sess) - groups, err := cwl.DescribeLogGroups(&cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int64(1), - LogGroupNamePrefix: aws.String(logGroup), - }) - if err != nil { - return "", err + if targetGroup.Arn != "" { + log.Debug("Creating ECS service with a load balancer") + createServiceInput.SetLoadBalancers([]*ecs.LoadBalancer{{ + ContainerName: aws.String(src.App), + ContainerPort: aws.Int64(targetGroup.Port), + TargetGroupArn: &targetGroup.Arn, + }}) + } else { + log.Debug("No target group specified - skipping load balancer config for ECS service") } - if len(groups.LogGroups) == 0 { - s.Status("Creating CloudWatchLogs group to store logs in: %s", logGroup) + s.Update("Creating ECS Service %s", serviceName) - L.Debug("creating log group", "group", logGroup) - _, err = cwl.CreateLogGroup(&cloudwatchlogs.CreateLogGroupInput{ - LogGroupName: aws.String(logGroup), - }) - if err != nil { - return "", err + ecsSvc := ecs.New(sess) + // AWS is eventually consistent so even though we probably created the resources that + // are referenced by the task definition, it can error out if we try to reference those resources + // too quickly. So we're forced to guard actions which reference other AWS services + // with loops like this. + var servOut *ecs.CreateServiceOutput + var err error +OUTER: + for i := 0; i <= awsCreateRetries; i++ { + servOut, err = ecsSvc.CreateServiceWithContext(ctx, createServiceInput) + if err == nil { + break } - s.Update("Created CloudWatchLogs group to store logs in: %s", logGroup) + // if we encounter an unrecoverable error, exit and skip this loop + if aerr, ok := err.(awserr.Error); ok { + switch aerr.Code() { + case "AccessDeniedException", "UnsupportedFeatureException", + "PlatformUnknownException", + "PlatformTaskDefinitionIncompatibilityException": + break OUTER + } + } + + s.Update("Failed to register ecs service. Will retry in %d seconds (up to %d more times)\nError: %s", awsCreateRetryIntervalSeconds, awsCreateRetries-i, err) + + // otherwise sleep and try again + time.Sleep(awsCreateRetryIntervalSeconds * time.Second) + } + if err != nil { + return status.Errorf(codes.Internal, "failed registering ecs service: %s", err) } - return logGroup, nil + state.Name = *servOut.Service.ServiceName + state.Arn = *servOut.Service.ServiceArn + + s.Update("Created ECS Service %s", serviceName) + s.Done() + return nil } -func createSG( +func (p *Platform) resourceServiceStatus( ctx context.Context, - s LifecycleStatus, + sg terminal.StepGroup, sess *session.Session, - name string, - vpcId *string, - ports ...int, -) (*string, error) { - ec2srv := ec2.New(sess) + log hclog.Logger, + state *Resource_Service, + sr *resource.StatusResponse, +) error { + s := sg.Add("Determining status of ecs service %s", state.Name) + defer s.Abort() - dsg, err := ec2srv.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{ - Filters: []*ec2.Filter{ - { - Name: aws.String("group-name"), - Values: []*string{aws.String(name)}, - }, - }, + ecsSvc := ecs.New(sess) + + servicesResp, err := ecsSvc.DescribeServicesWithContext(ctx, &ecs.DescribeServicesInput{ + Services: []*string{&state.Name}, + Cluster: &state.Cluster, }) + if _, ok := err.(*ecs.ClusterNotFoundException); ok { + sr.Resources = append(sr.Resources, &sdk.StatusReport_Resource{ + Name: state.Name, + Id: state.Arn, + Health: sdk.StatusReport_MISSING, + HealthMessage: fmt.Sprintf("Cluster named %q is missing", state.Cluster), + }) + s.Done() + return nil + } if err != nil { - return nil, err + return status.Errorf(codes.Internal, "failed to describe service (ARN %q): %s", state.Arn, err) + } + if len(servicesResp.Services) == 0 { + sr.Resources = append(sr.Resources, &sdk.StatusReport_Resource{ + Name: state.Name, + Id: state.Arn, + Health: sdk.StatusReport_MISSING, + HealthMessage: fmt.Sprintf("service %q is missing", state.Name), + }) + s.Done() + return nil } - var groupId *string + service := servicesResp.Services[0] - if len(dsg.SecurityGroups) != 0 { - groupId = dsg.SecurityGroups[0].GroupId - s.Status("Using existing security group: %s", name) + serviceResource := sdk.StatusReport_Resource{ + Name: *service.ServiceName, + Id: *service.ServiceArn, + CreatedTime: timestamppb.New(*service.CreatedAt), + PlatformUrl: fmt.Sprintf("https://console.aws.amazon.com/ecs/home?region=%s#/clusters/waypoint/services/%s", p.config.Region, state.Name), + Type: "service", + CategoryDisplayHint: sdk.ResourceCategoryDisplayHint_INSTANCE_MANAGER, + HealthMessage: fmt.Sprintf("service is %q", *service.Status), + } + sr.Resources = append(sr.Resources, &serviceResource) + + if *service.Status == "ACTIVE" { + serviceResource.Health = sdk.StatusReport_READY } else { - s.Status("Creating security group: %s", name) - out, err := ec2srv.CreateSecurityGroup(&ec2.CreateSecurityGroupInput{ - Description: aws.String("created by waypoint"), - GroupName: aws.String(name), - VpcId: vpcId, - }) - if err != nil { - return nil, err - } + serviceResource.Health = sdk.StatusReport_DOWN + serviceResource.HealthMessage = fmt.Sprintf("service is %q", *service.Status) + } - groupId = out.GroupId - s.Update("Created security group: %s", name) + serviceJson, err := json.Marshal(map[string]interface{}{"service": service}) + if err != nil { + return status.Errorf(codes.Internal, "failed to marshal service %q (ARN %q) state to json: %s", *service.ServiceName, *service.ServiceArn, err) } + serviceResource.StateJson = string(serviceJson) - s.Update("Authorizing ports to security group") - for _, port := range ports { - _, err = ec2srv.AuthorizeSecurityGroupIngress(&ec2.AuthorizeSecurityGroupIngressInput{ - CidrIp: aws.String("0.0.0.0/0"), - FromPort: aws.Int64(int64(port)), - ToPort: aws.Int64(int64(port)), - GroupId: groupId, - IpProtocol: aws.String("tcp"), + taskArns, err := ecsSvc.ListTasksWithContext(ctx, &ecs.ListTasksInput{ + ServiceName: &state.Name, + Cluster: &state.Cluster, + }) + if err != nil { + return status.Errorf(codes.Internal, "failed to list tasks for service %q in cluster %q: %s", state.Name, state.Cluster, err) + } + + // Insert missing tasks if necessary + missingCount := int(*service.DesiredCount) - len(taskArns.TaskArns) + log.Debug("There are missing tasks. The service may be just starting up.", "missing count", missingCount, "service name", state.Name, "cluster", state.Cluster) + for i := 0; i < missingCount; i++ { + sr.Resources = append(sr.Resources, &sdk.StatusReport_Resource{ + Type: "task", + Name: "missing", + ParentResourceId: *service.ServiceArn, + Health: sdk.StatusReport_MISSING, + HealthMessage: fmt.Sprintf("task is missing. The parent service %q may be just starting up at the time of this status check", state.Name), + CategoryDisplayHint: sdk.ResourceCategoryDisplayHint_INSTANCE, }) } - if err != nil { - if aerr, ok := err.(awserr.Error); ok { - switch aerr.Code() { - case "InvalidPermission.Duplicate": - // fine, means we already added it. + if len(taskArns.TaskArns) > 0 { + tasks, err := ecsSvc.DescribeTasksWithContext(ctx, &ecs.DescribeTasksInput{ + Tasks: taskArns.TaskArns, + Cluster: &state.Cluster, + }) + if err != nil { + return status.Errorf(codes.Internal, "failed to describe tasks for service %q in cluster %q: %s", state.Name, state.Cluster, err) + } + + for _, task := range tasks.Tasks { + // Determine short task ID + splitArn := strings.Split(*task.TaskArn, "/") + taskId := splitArn[len(splitArn)-1] + + taskResource := &sdk.StatusReport_Resource{ + Type: "task", + Name: taskId, + ParentResourceId: *service.ServiceArn, + Id: *task.TaskArn, + CategoryDisplayHint: sdk.ResourceCategoryDisplayHint_INSTANCE, + CreatedTime: timestamppb.New(*task.CreatedAt), + PlatformUrl: fmt.Sprintf("https://console.aws.amazon.com/ecs/home?region=%s#/clusters/waypoint/tasks/%s", p.config.Region, taskId), + } + sr.Resources = append(sr.Resources, taskResource) + + switch strings.ToLower(*task.LastStatus) { + case "running": + taskResource.Health = sdk.StatusReport_READY + case "provisioning", "pending", "activating": + taskResource.Health = sdk.StatusReport_ALIVE default: - return nil, err + taskResource.Health = sdk.StatusReport_DOWN } - } else { - return nil, err - } - } - s.Update("Configured security group: %s", name) + taskResource.HealthMessage = fmt.Sprintf("task is %q", *task.LastStatus) - return groupId, nil + // Find IP address if possible + + var ipAddress string + for _, attachment := range task.Attachments { + for _, detail := range attachment.Details { + if *detail.Name == "privateIPv4Address" { + ipAddress = *detail.Value + } + } + } + + stateJson, err := json.Marshal(map[string]interface{}{ + "ipAddress": ipAddress, + "task": task, + }) + if err != nil { + return status.Errorf(codes.Internal, "failed to marshal task (arn %q) state to json: %s", *task.TaskArn, err) + } + taskResource.StateJson = string(stateJson) + } + } + s.Done() + return nil } -func createALB( +func (p *Platform) resourceServiceDestroy( ctx context.Context, - s LifecycleStatus, - L hclog.Logger, + sg terminal.StepGroup, sess *session.Session, - app *component.Source, - albConfig *ALBConfig, - vpcId *string, - serviceName *string, - sgWebId *string, - servicePort *int64, - subnets []*string, -) (lbArn *string, tgArn *string, err error) { - s.Update("Creating ALB target group") - L.Debug("creating target group", "name", serviceName) + log hclog.Logger, + state *Resource_Service, +) error { + log.Debug("deleting ecs service", "arn", state.Arn) + if state.Arn == "" { + log.Debug("Missing ECS Service ARN - it must not have been created successfully. Skipping delete.") + return nil + } - elbsrv := elbv2.New(sess) - ctg, err := elbsrv.CreateTargetGroup(&elbv2.CreateTargetGroupInput{ - HealthCheckEnabled: aws.Bool(true), - Name: serviceName, - Port: servicePort, - Protocol: aws.String("HTTP"), - TargetType: aws.String("ip"), - VpcId: vpcId, + s := sg.Add("Deleting service %s", state.Name) + defer s.Abort() + + _, err := ecs.New(sess).DeleteServiceWithContext(ctx, &ecs.DeleteServiceInput{ + Cluster: &state.Cluster, + Force: aws.Bool(true), + Service: &state.Arn, }) if err != nil { - return nil, nil, err + if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "ServiceNotFoundException" { + s.Update("Service does not exist - it must have already been deleted. (ARN: %q)", state.Arn) + s.Done() + return nil + } + return status.Errorf(codes.Internal, "failed to delete ECS cluster %s (ARN: %q): %s", state.Name, state.Arn, err) } - tgArn = ctg.TargetGroups[0].TargetGroupArn + s.Update("Deleted service %s", state.Name) + s.Done() + return nil +} + +func (p *Platform) resourceAlbListenerCreate( + ctx context.Context, + sg terminal.StepGroup, + sess *session.Session, + log hclog.Logger, + alb *Resource_Alb, + targetGroup *Resource_TargetGroup, + externalIngressPort ExternalIngressPort, + + state *Resource_Alb_Listener, +) error { + s := sg.Add("Initiating ALB creation") + defer s.Abort() - s.Update("Created ALB target group") + state.TargetGroup = targetGroup - // Create the load balancer OR modify the existing one to have this new target - // group but with a weight of 0 + albConfig := p.config.ALB tgs := []*elbv2.TargetGroupTuple{ { - TargetGroupArn: tgArn, + TargetGroupArn: &targetGroup.Arn, Weight: aws.Int64(0), }, } var ( - certs []*elbv2.Certificate - protocol string = "HTTP" - port int64 = 80 + certs []*elbv2.Certificate + protocol = "HTTP" + newListener = false ) if albConfig != nil && albConfig.CertificateId != "" { protocol = "HTTPS" - port = 443 certs = append(certs, &elbv2.Certificate{ CertificateArn: &albConfig.CertificateId, }) } - var existingListener string + elbsrv := elbv2.New(sess) + + var listener *elbv2.Listener if albConfig != nil && albConfig.ListenerARN != "" { - existingListener = albConfig.ListenerARN - } + s.Update("Describing requested ALB listener (ARN: %s)", albConfig.ListenerARN) - var ( - lb *elbv2.LoadBalancer - listener *elbv2.Listener - newListener bool - ) + state.Managed = false - if existingListener != "" { - out, err := elbsrv.DescribeListeners(&elbv2.DescribeListenersInput{ - ListenerArns: []*string{aws.String(existingListener)}, + out, err := elbsrv.DescribeListenersWithContext(ctx, &elbv2.DescribeListenersInput{ + ListenerArns: []*string{aws.String(albConfig.ListenerARN)}, }) if err != nil { - return nil, nil, err + return status.Errorf(codes.Internal, "failed to describe requested listener ARN %q: %s", albConfig.ListenerARN, err) } listener = out.Listeners[0] s.Update("Using configured ALB Listener: %s (load-balancer: %s)", *listener.ListenerArn, *listener.LoadBalancerArn) } else { - lbName := "waypoint-ecs-" + app.App - dlb, err := elbsrv.DescribeLoadBalancers(&elbv2.DescribeLoadBalancersInput{ - Names: []*string{&lbName}, - }) - if err != nil { - if aerr, ok := err.(awserr.Error); ok { - switch aerr.Code() { - case elbv2.ErrCodeLoadBalancerNotFoundException: - // fine, means we'll create it. - default: - return nil, nil, err - } - } else { - return nil, nil, err - } - } - - if dlb != nil && len(dlb.LoadBalancers) > 0 { - lb = dlb.LoadBalancers[0] - s.Update("Using existing ALB %s (%s, dns-name: %s)", - lbName, *lb.LoadBalancerArn, *lb.DNSName) - } else { - s.Update("Creating new ALB: %s", lbName) - - scheme := elbv2.LoadBalancerSchemeEnumInternetFacing - - if albConfig != nil && albConfig.InternalScheme != nil && *albConfig.InternalScheme { - scheme = elbv2.LoadBalancerSchemeEnumInternal - } - - clb, err := elbsrv.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{ - Name: aws.String(lbName), - Subnets: subnets, - SecurityGroups: []*string{sgWebId}, - Scheme: &scheme, - }) - if err != nil { - return nil, nil, err - } - - lb = clb.LoadBalancers[0] + state.Managed = true - s.Update("Created new ALB: %s (dns-name: %s)", lbName, *lb.DNSName) + if alb == nil || alb.Arn == "" { + return status.Errorf(codes.InvalidArgument, "cannot create ALB listener - no existing ALB defined.") } - listeners, err := elbsrv.DescribeListeners(&elbv2.DescribeListenersInput{ - LoadBalancerArn: lb.LoadBalancerArn, + s.Update("No ALB listener specified - looking for listeners for ALB %q", alb.Name) + listeners, err := elbsrv.DescribeListenersWithContext(ctx, &elbv2.DescribeListenersInput{ + LoadBalancerArn: &alb.Arn, }) if err != nil { - return nil, nil, err + return status.Errorf(codes.Internal, "failed to describe listeners for alb (ARN %q): %s", alb.Arn, err) } if len(listeners.Listeners) > 0 { listener = listeners.Listeners[0] - s.Update("Using existing ALB Listener") + s.Update("Using existing ALB Listener (ARN: %q)", *listener.ListenerArn) } else { s.Update("Creating new ALB Listener") + newListener = true - L.Info("load-balancer defined", "dns-name", *lb.DNSName) + log.Info("load-balancer defined", "dns-name", alb.DnsName) tgs[0].Weight = aws.Int64(100) - lo, err := elbsrv.CreateListener(&elbv2.CreateListenerInput{ - LoadBalancerArn: lb.LoadBalancerArn, - Port: aws.Int64(port), + lo, err := elbsrv.CreateListenerWithContext(ctx, &elbv2.CreateListenerInput{ + LoadBalancerArn: &alb.Arn, + Port: aws.Int64(int64(externalIngressPort)), Protocol: aws.String(protocol), Certificates: certs, DefaultActions: []*elbv2.Action{ @@ -843,15 +987,16 @@ func createALB( }, }) if err != nil { - return nil, nil, err + return status.Errorf(codes.Internal, "failed to create listener: %s", err) } - newListener = true listener = lo.Listeners[0] - s.Update("Created new ALB Listener") + s.Update("Created ALB Listener") + log.Debug("Created ALB Listener", "arn", *listener.ListenerArn) } } + state.Arn = *listener.ListenerArn if !newListener { def := listener.DefaultActions @@ -860,16 +1005,16 @@ func createALB( for _, tg := range def[0].ForwardConfig.TargetGroups { if *tg.Weight > 0 { tgs = append(tgs, tg) - L.Debug("previous target group", "arn", *tg.TargetGroupArn) + log.Debug("previous target group", "arn", *tg.TargetGroupArn) } } } s.Update("Modifying ALB Listener to introduce target group") - _, err = elbsrv.ModifyListener(&elbv2.ModifyListenerInput{ + _, err := elbsrv.ModifyListenerWithContext(ctx, &elbv2.ModifyListenerInput{ ListenerArn: listener.ListenerArn, - Port: aws.Int64(port), + Port: aws.Int64(int64(externalIngressPort)), Protocol: aws.String(protocol), Certificates: certs, DefaultActions: []*elbv2.Action{ @@ -882,145 +1027,238 @@ func createALB( }, }) if err != nil { - return nil, nil, err + return status.Errorf(codes.Internal, "failed to introduce new target group to existing ALB listener: %s", err) } s.Update("Modified ALB Listener to introduce target group") } - if albConfig != nil && albConfig.ZoneId != "" { - r53 := route53.New(sess) + s.Done() + return nil +} - records, err := r53.ListResourceRecordSets(&route53.ListResourceRecordSetsInput{ - HostedZoneId: aws.String(albConfig.ZoneId), - StartRecordName: aws.String(albConfig.FQDN), - StartRecordType: aws.String(route53.RRTypeA), - MaxItems: aws.String("1"), - }) - if err != nil { - return nil, nil, err - } +// resourceAlbListenerDestroy destroys the ALB listener associated with this deployment +// if it is under waypoint's management, and if the only target group it forwards to +// is this deployment's target group. +func (p *Platform) resourceAlbListenerDestroy( + ctx context.Context, + sg terminal.StepGroup, + sess *session.Session, + log hclog.Logger, + state *Resource_Alb_Listener, +) error { + if !state.Managed { + log.Debug("Skipping destroy of unmanaged ALB listener with ARN %q", state.Arn) + return nil + } + if state.Arn == "" { + log.Debug("Missing alb listener ARN - it must not have been created successfully. Skipping delete.") + return nil + } + + s := sg.Add("Initiating deletion of ALB Listener (ARN: %q)", state.Arn) + defer s.Abort() - fqdn := albConfig.FQDN + elbsrv := elbv2.New(sess) + s.Update("Describing ALB listener (ARN: %q)", state.Arn) - // Add trailing period to match Route53 record name - if fqdn[len(fqdn)-1] != '.' { - fqdn += "." + listeners, err := elbsrv.DescribeListenersWithContext(ctx, &elbv2.DescribeListenersInput{ + ListenerArns: []*string{&state.Arn}, + }) + if err != nil { + // There doesn't seem to be an aws error to cast to for this code. + if strings.Contains(err.Error(), "ListenerNotFound") { + s.Update("Listener does not exist and must have been destroyed (ARN %q).", state.Arn) + s.Status(terminal.StatusWarn) + s.Done() + return nil } + return status.Errorf(codes.Internal, "failed to describe listener with ARN %q: %s", state.Arn, err) + } + + if len(listeners.Listeners) == 0 { + // Could happen if listener was deleted out-of-band + s.Update("ALB listener does not exist - not deleting (ARN: %q)", state.Arn) + s.Status(terminal.StatusWarn) + s.Done() + return nil + } + + listener := listeners.Listeners[0] - var recordExists bool + log.Debug("listener arn", "arn", *listener.ListenerArn) - if len(records.ResourceRecordSets) > 0 { - record := records.ResourceRecordSets[0] - if aws.StringValue(record.Type) == route53.RRTypeA && aws.StringValue(record.Name) == fqdn { - s.Status("Found existing Route53 record: %s", aws.StringValue(record.Name)) - L.Debug("found existing record, assuming it's correct") - recordExists = true + def := listener.DefaultActions + + var tgs []*elbv2.TargetGroupTuple + + // If there is only 1 target group, delete the listener + if len(def) == 1 && len(def[0].ForwardConfig.TargetGroups) == 1 { + log.Debug("only 1 target group, deleting listener") + + s.Update("Deleting ALB listener (ARN: %q)", state.Arn) + _, err = elbsrv.DeleteListenerWithContext(ctx, &elbv2.DeleteListenerInput{ + ListenerArn: listener.ListenerArn, + }) + + if err != nil { + return status.Errorf(codes.Internal, "failed to delete ALB listener (ARN %q): %s", *listener.ListenerArn, err) + } + s.Update("Deleted ALB Listener") + } else if len(def) > 0 && def[0].ForwardConfig != nil && len(def[0].ForwardConfig.TargetGroups) > 1 { + // Multiple target groups means we can keep the listener + var active bool + + for _, tg := range def[0].ForwardConfig.TargetGroups { + if *tg.TargetGroupArn != state.TargetGroup.Arn { + tgs = append(tgs, tg) + if *tg.Weight > 0 { + active = true + } } } - if !recordExists { - s.Status("Creating new Route53 record: %s (zone-id: %s)", - albConfig.FQDN, albConfig.ZoneId) - - L.Debug("creating new route53 record", "zone-id", albConfig.ZoneId) - input := &route53.ChangeResourceRecordSetsInput{ - ChangeBatch: &route53.ChangeBatch{ - Changes: []*route53.Change{ - { - Action: aws.String(route53.ChangeActionCreate), - ResourceRecordSet: &route53.ResourceRecordSet{ - Name: aws.String(albConfig.FQDN), - Type: aws.String(route53.RRTypeA), - AliasTarget: &route53.AliasTarget{ - DNSName: lb.DNSName, - EvaluateTargetHealth: aws.Bool(true), - HostedZoneId: lb.CanonicalHostedZoneId, - }, - }, - }, + // If there are no target groups active, then we just activate the first + // one, otherwise we can't modify the listener. + if !active && len(tgs) > 0 { + tgs[0].Weight = aws.Int64(100) + } + + log.Debug("modifying listener to remove target group", "target-groups", len(tgs)) + + s.Update("Deregistering this deployment's target group from ALB listener") + _, err = elbsrv.ModifyListenerWithContext(ctx, &elbv2.ModifyListenerInput{ + ListenerArn: listener.ListenerArn, + Port: listener.Port, + Protocol: listener.Protocol, + DefaultActions: []*elbv2.Action{ + { + ForwardConfig: &elbv2.ForwardActionConfig{ + TargetGroups: tgs, }, - Comment: aws.String("managed by waypoint"), + Type: aws.String("forward"), }, - HostedZoneId: aws.String(albConfig.ZoneId), - } + }, + }) + if err != nil { + return status.Errorf(codes.Internal, "failed to modify listener (ARN %q): %s", *listener.ListenerArn, err) + } + s.Update("Deregistered this deployment's target group from ALB listener") + } - result, err := r53.ChangeResourceRecordSets(input) - if err != nil { - return nil, nil, err - } - L.Debug("record created", "change-id", *result.ChangeInfo.Id) + s.Done() + return nil +} - s.Update("Created new Route53 record: %s (zone-id: %s)", - albConfig.FQDN, albConfig.ZoneId) - } +func (p *Platform) resourceTargetGroupCreate( + ctx context.Context, + sg terminal.StepGroup, + sess *session.Session, + log hclog.Logger, + deploymentId DeploymentId, + subnets *Resource_Subnets, // Required because we need to know which VPC we're in, and subnets discover it. + state *Resource_TargetGroup, +) error { + if p.config.DisableALB { + log.Debug("ALB disabled - skipping target group creation") + return nil } - lbArn = listener.LoadBalancerArn + s := sg.Add("Initiating target group creation...") + defer s.Abort() - return lbArn, tgArn, err -} + elbsrv := elbv2.New(sess) -func buildLoggingOptions( - lo *Logging, - region string, - logGroup string, - defaultStreamPrefix string, -) map[string]*string { + // Use our common deployment ID as the target group name + targetGroupName := string(deploymentId) - result := map[string]*string{ - "awslogs-region": aws.String(region), - "awslogs-group": aws.String(logGroup), - "awslogs-stream-prefix": aws.String(defaultStreamPrefix), - } + // We have to clamp at a length of 32 because the Name field + // requires that the name is 32 characters or less. - if lo != nil { - // We receive the error `Log driver awslogs disallows options: awslogs-endpoint` - // when setting `awslogs-endpoint`, so that is not included here of the - // available options - // https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_awslogs.html - result["awslogs-datetime-format"] = aws.String(lo.DateTimeFormat) - result["awslogs-multiline-pattern"] = aws.String(lo.MultilinePattern) - result["mode"] = aws.String(lo.Mode) - result["max-buffer-size"] = aws.String(lo.MaxBufferSize) + // NOTE(izaak): The random part of ULIDs seems to be near the end, so for long app names, we might not get unique names here. + // Should use a different source of randomness than component ID + if len(targetGroupName) > 32 { + targetGroupName = targetGroupName[:32] + log.Debug("using a shortened value for service name due to AWS's length limits", "serviceName", targetGroupName) + } - if lo.CreateGroup { - result["awslogs-create-group"] = aws.String("true") - } - if lo.StreamPrefix != "" { - result["awslogs-stream-prefix"] = aws.String(lo.StreamPrefix) - } + if subnets.VpcId == "" { + return status.Error(codes.Internal, "subnets failed to discover a VPC ID - cannot create target group") } - for k, v := range result { - if *v == "" { - delete(result, k) - } + state.Port = p.config.ServicePort + + ctg, err := elbsrv.CreateTargetGroupWithContext(ctx, &elbv2.CreateTargetGroupInput{ + HealthCheckEnabled: aws.Bool(true), + Name: &targetGroupName, + Port: &state.Port, + Protocol: aws.String("HTTP"), + TargetType: aws.String("ip"), + VpcId: &subnets.VpcId, + }) + if err != nil || ctg == nil || len(ctg.TargetGroups) == 0 { + return status.Errorf(codes.Internal, "failed to create target group: %s", err) } - return result + state.Name = *ctg.TargetGroups[0].TargetGroupName + state.Arn = *ctg.TargetGroups[0].TargetGroupArn + + s.Update("Created target group %s", state.Name) + + s.Done() + return nil } -func (p *Platform) Launch( +func (p *Platform) resourceTargetGroupDestroy( ctx context.Context, - s LifecycleStatus, - L hclog.Logger, - ui terminal.UI, + sg terminal.StepGroup, sess *session.Session, - app *component.Source, - img *docker.Image, - deployConfig *component.DeploymentConfig, - executionRoleArn, taskRoleArn, clusterName, logGroup string, -) (*Deployment, error) { - id, err := component.Id() + log hclog.Logger, + state *Resource_TargetGroup, +) error { + if state.Arn == "" { + log.Debug("Missing target group ARN - it must not have been created successfully. Skipping delete.") + return nil + } + + s := sg.Add("Deleting target group %s", state.Name) + defer s.Abort() + + elbsrv := elbv2.New(sess) + + // Destroying the listener earlier should have deregistered this target group, so it should be safe + // to just delete + _, err := elbsrv.DeleteTargetGroupWithContext(ctx, &elbv2.DeleteTargetGroupInput{ + TargetGroupArn: &state.Arn, + }) if err != nil { - return nil, err + // This doesn't seem to return an error if the target group does not exist. + return status.Errorf(codes.Internal, "failed to delete target group %s (ARN: %q): %s", state.Name, state.Arn, err) } - ecsSvc := ecs.New(sess) + s.Done() + return nil +} - defaultStreamPrefix := fmt.Sprintf("waypoint-%d", time.Now().Nanosecond()) +func (p *Platform) resourceTaskDefinitionCreate( + ctx context.Context, + sg terminal.StepGroup, + log hclog.Logger, + sess *session.Session, + src *component.Source, + img *docker.Image, + deployConfig *component.DeploymentConfig, + state *Resource_TaskDefinition, + // Outputs of other resource creation processes + executionRole *Resource_ExecutionRole, + taskRole *Resource_TaskRole, + logGroup *Resource_LogGroup, +) error { + s := sg.Add("Initiating task definition creation") + defer s.Abort() + + // Build environment variables env := []*ecs.KeyValuePair{ { Name: aws.String("PORT"), @@ -1035,6 +1273,14 @@ func (p *Platform) Launch( }) } + for k, v := range deployConfig.Env() { + env = append(env, &ecs.KeyValuePair{ + Name: aws.String(k), + Value: aws.String(v), + }) + } + + // Build secrets var secrets []*ecs.Secret for k, v := range p.config.Secrets { secrets = append(secrets, &ecs.Secret{ @@ -1043,29 +1289,24 @@ func (p *Platform) Launch( }) } - for k, v := range deployConfig.Env() { - env = append(env, &ecs.KeyValuePair{ - Name: aws.String(k), - Value: aws.String(v), - }) - } + // Build logging options + defaultStreamPrefix := fmt.Sprintf("waypoint-%d", time.Now().Nanosecond()) logOptions := buildLoggingOptions( p.config.Logging, p.config.Region, - logGroup, + logGroup.Name, defaultStreamPrefix, ) + // Define app container def := ecs.ContainerDefinition{ Essential: aws.Bool(true), - Name: aws.String(app.App), + Name: aws.String(src.App), Image: aws.String(img.Name()), - PortMappings: []*ecs.PortMapping{ - { - ContainerPort: aws.Int64(p.config.ServicePort), - }, - }, + PortMappings: []*ecs.PortMapping{{ + ContainerPort: aws.Int64(p.config.ServicePort), + }}, Environment: env, Memory: utils.OptionalInt64(int64(p.config.Memory)), MemoryReservation: utils.OptionalInt64(int64(p.config.MemoryReservation)), @@ -1076,6 +1317,7 @@ func (p *Platform) Launch( }, } + // Define sidecar containers var additionalContainers []*ecs.ContainerDefinition for _, container := range p.config.ContainersConfig { var secrets []*ecs.Secret @@ -1105,36 +1347,38 @@ func (p *Platform) Launch( Protocol: aws.String(container.Protocol), }, }, - HealthCheck: &ecs.HealthCheck{ + Secrets: secrets, + Environment: env, + Memory: utils.OptionalInt64(int64(container.Memory)), + MemoryReservation: utils.OptionalInt64(int64(container.MemoryReservation)), + } + + if container.HealthCheck != nil { + c.SetHealthCheck(&ecs.HealthCheck{ Command: aws.StringSlice(container.HealthCheck.Command), Interval: aws.Int64(container.HealthCheck.Interval), Timeout: aws.Int64(container.HealthCheck.Timeout), Retries: aws.Int64(container.HealthCheck.Retries), StartPeriod: aws.Int64(container.HealthCheck.StartPeriod), - }, - Secrets: secrets, - Environment: env, - Memory: utils.OptionalInt64(int64(container.Memory)), - MemoryReservation: utils.OptionalInt64(int64(container.MemoryReservation)), + }) } additionalContainers = append(additionalContainers, c) } - L.Debug("registering task definition", "id", id) - - var cpuShares int - family := "waypoint-" + app.App + containerDefinitions := append([]*ecs.ContainerDefinition{&def}, additionalContainers...) - s.Status("Registering Task definition: %s", family) + family := "waypoint-" + src.App + s.Update("Registering Task definition: %s", family) + var cpuShares int runtime := aws.String("FARGATE") if p.config.EC2Cluster { runtime = aws.String("EC2") cpuShares = p.config.CPU } else { if err := utils.ValidateEcsMemCPUPair(p.config.Memory, p.config.CPU); err != nil { - return nil, err + return err } cpuValues := fargateResources[p.config.Memory] @@ -1154,12 +1398,10 @@ func (p *Platform) Launch( } mems := strconv.Itoa(p.config.Memory) - containerDefinitions := append([]*ecs.ContainerDefinition{&def}, additionalContainers...) - registerTaskDefinitionInput := ecs.RegisterTaskDefinitionInput{ ContainerDefinitions: containerDefinitions, - ExecutionRoleArn: aws.String(executionRoleArn), + ExecutionRoleArn: aws.String(executionRole.Arn), Cpu: cpus, Memory: aws.String(mems), Family: aws.String(family), @@ -1170,330 +1412,856 @@ func (p *Platform) Launch( Tags: []*ecs.Tag{ { Key: aws.String("waypoint-app"), - Value: aws.String(app.App), + Value: aws.String(src.App), }, }, } - if taskRoleArn != "" { - registerTaskDefinitionInput.SetTaskRoleArn(taskRoleArn) + if taskRole != nil && taskRole.Arn != "" { + registerTaskDefinitionInput.SetTaskRoleArn(taskRole.Arn) } - var taskOut *ecs.RegisterTaskDefinitionOutput + ecsSvc := ecs.New(sess) + var taskOut *ecs.RegisterTaskDefinitionOutput + var err error // AWS is eventually consistent so even though we probably created the resources that // are referenced by the task definition, it can error out if we try to reference those resources // too quickly. So we're forced to guard actions which reference other AWS services // with loops like this. - for i := 0; i < 30; i++ { - taskOut, err = ecsSvc.RegisterTaskDefinition(®isterTaskDefinitionInput) + +OUTER: + for i := 0; i <= awsCreateRetries; i++ { + taskOut, err = ecsSvc.RegisterTaskDefinitionWithContext(ctx, ®isterTaskDefinitionInput) if err == nil { break } - // if we encounter an unrecoverable error, exit now. - if aerr, ok := err.(awserr.Error); ok { + // if we encounter an unrecoverable error, exit and skip this loop + if aerr, ok := err.(awserr.Error); ok && aerr.Code() == "ResourceConflictException" { switch aerr.Code() { case "ResourceConflictException": - return nil, err + break OUTER } } + s.Update("Failed to register ecs task definition. Will retry in %d seconds (up to %d more times)\nError: %s", awsCreateRetryIntervalSeconds, awsCreateRetries-i, err) + s.Status(terminal.StatusWarn) + // otherwise sleep and try again - time.Sleep(2 * time.Second) + time.Sleep(awsCreateRetryIntervalSeconds * time.Second) } - if err != nil { - return nil, err + return status.Errorf(codes.Internal, "failed registering ecs task definition: %s", err) } s.Update("Registered Task definition: %s", family) - serviceName := fmt.Sprintf("%s-%s", app.App, id) + state.Runtime = *runtime + state.Arn = *taskOut.TaskDefinition.TaskDefinitionArn - // We have to clamp at a length of 32 because the Name field to CreateTargetGroup - // requires that the name is 32 characters or less. - if len(serviceName) > 32 { - serviceName = serviceName[:32] - L.Debug("using a shortened value for service name due to AWS's length limits", "serviceName", serviceName) + s.Done() + return nil +} + +func (p *Platform) resourceAlbCreate( + ctx context.Context, + sg terminal.StepGroup, + sess *session.Session, + log hclog.Logger, + src *component.Source, + securityGroups *Resource_ExternalSecurityGroups, + subnets *Resource_Subnets, // Required because we need to know which VPC we're in, and subnets discover it. + state *Resource_Alb, +) error { + if p.config.DisableALB { + log.Debug("ALB disabled - skipping target group creation") + return nil + } + + albConfig := p.config.ALB + + if albConfig != nil && albConfig.ListenerARN != "" { + log.Debug("Existing ALB listener specified - no need to create or discover an ALB") + return nil + } + + // If not using an existing listener, the load balancer is owned by waypoint + state.Managed = true + + s := sg.Add("Initiating ALB creation") + defer s.Abort() + + var certs []*elbv2.Certificate + if albConfig != nil && albConfig.CertificateId != "" { + certs = append(certs, &elbv2.Certificate{ + CertificateArn: &albConfig.CertificateId, + }) + } + + elbsrv := elbv2.New(sess) + + lbName := "waypoint-ecs-" + src.App + state.Name = lbName + + s.Update("Looking for an existing load balancer named %s", lbName) + + var lb *elbv2.LoadBalancer + dlb, err := elbsrv.DescribeLoadBalancersWithContext(ctx, &elbv2.DescribeLoadBalancersInput{ + Names: []*string{&lbName}, + }) + if err != nil { + // If the load balancer wasn't found, we'll create it. + if aerr, ok := err.(awserr.Error); ok && aerr.Code() != elbv2.ErrCodeLoadBalancerNotFoundException { + return status.Errorf(codes.Internal, "failed to describe load balancers with name %q: %s", lbName, err) + } + log.Debug("load balancer %s was not found - will create it.") + } + + if dlb != nil && len(dlb.LoadBalancers) > 0 { + lb = dlb.LoadBalancers[0] + s.Update("Using existing ALB %s (%s, dns-name: %s)", + lbName, *lb.LoadBalancerArn, *lb.DNSName) + } else { + s.Update("Creating new ALB: %s", lbName) + + scheme := elbv2.LoadBalancerSchemeEnumInternetFacing + + if albConfig != nil && albConfig.InternalScheme != nil && *albConfig.InternalScheme { + log.Debug("Creating an internal scheme ALB") + scheme = elbv2.LoadBalancerSchemeEnumInternal + } + + subnetIds := make([]*string, len(subnets.Subnets)) + for i, subnet := range subnets.Subnets { + subnetIds[i] = &subnet.Id + } + + securityGroupIds := make([]*string, len(securityGroups.SecurityGroups)) + for i, securityGroup := range securityGroups.SecurityGroups { + securityGroupIds[i] = &securityGroup.Id + } + + clb, err := elbsrv.CreateLoadBalancerWithContext(ctx, &elbv2.CreateLoadBalancerInput{ + Name: aws.String(lbName), + Subnets: subnetIds, + SecurityGroups: securityGroupIds, + Scheme: &scheme, + }) + if err != nil { + return status.Errorf(codes.Internal, "failed to create ALB %q: %s", lbName, err) + } + + lb = clb.LoadBalancers[0] + + s.Update("Created ALB: %s (dns-name: %s)", lbName, *lb.DNSName) } + state.Arn = *lb.LoadBalancerArn + + state.Arn = *lb.LoadBalancerArn + state.DnsName = *lb.DNSName + state.CanonicalHostedZoneId = *lb.CanonicalHostedZoneId - taskArn := *taskOut.TaskDefinition.TaskDefinitionArn + s.Update("Using Application Load Balancer %q", state.Name) + s.Done() + return nil +} + +func (p *Platform) resourceRoute53RecordCreate( + ctx context.Context, + sg terminal.StepGroup, + sess *session.Session, + log hclog.Logger, + alb *Resource_Alb, + state *Resource_Route53Record, +) error { + albConfig := p.config.ALB + if p.config.DisableALB || albConfig == nil || albConfig.ZoneId == "" || albConfig.FQDN == "" { + log.Debug("Not creating a route53 record") + return nil + } + + s := sg.Add("Route53 record is required - checking if one already exists") + defer s.Abort() + + r53 := route53.New(sess) + + records, err := r53.ListResourceRecordSetsWithContext(ctx, &route53.ListResourceRecordSetsInput{ + HostedZoneId: aws.String(albConfig.ZoneId), + StartRecordName: aws.String(albConfig.FQDN), + StartRecordType: aws.String(route53.RRTypeA), + MaxItems: aws.String("1"), + }) + if err != nil { + return status.Errorf(codes.Internal, "failed to list resource records for alb %q: %s", state.Name, err) + } + + fqdn := albConfig.FQDN + + // Add trailing period to match Route53 record name + if fqdn[len(fqdn)-1] != '.' { + fqdn += "." + } + + var recordExists bool + + if len(records.ResourceRecordSets) > 0 { + record := records.ResourceRecordSets[0] + if aws.StringValue(record.Type) == route53.RRTypeA && aws.StringValue(record.Name) == fqdn { + s.Update("Found existing Route53 record: %s", aws.StringValue(record.Name)) + log.Debug("found existing record, assuming it's correct") + recordExists = true + } + } + + if !recordExists { + s.Update("Creating new Route53 record: %s (zone-id: %s)", + albConfig.FQDN, albConfig.ZoneId) + + log.Debug("creating new route53 record", "zone-id", albConfig.ZoneId) + input := &route53.ChangeResourceRecordSetsInput{ + ChangeBatch: &route53.ChangeBatch{ + Changes: []*route53.Change{ + { + Action: aws.String(route53.ChangeActionCreate), + ResourceRecordSet: &route53.ResourceRecordSet{ + Name: aws.String(albConfig.FQDN), + Type: aws.String(route53.RRTypeA), + AliasTarget: &route53.AliasTarget{ + DNSName: &alb.DnsName, + EvaluateTargetHealth: aws.Bool(true), + HostedZoneId: &alb.CanonicalHostedZoneId, + }, + }, + }, + }, + Comment: aws.String("managed by waypoint"), + }, + HostedZoneId: aws.String(albConfig.ZoneId), + } + + result, err := r53.ChangeResourceRecordSetsWithContext(ctx, input) + if err != nil { + return status.Errorf(codes.Internal, "failed to create route53 record %q: %s", albConfig.FQDN, err) + } + log.Debug("record created", "change-id", *result.ChangeInfo.Id) + + s.Update("Created Route53 record: %s (zone-id: %s)", + albConfig.FQDN, albConfig.ZoneId) + } + + s.Done() + return nil +} + +func (p *Platform) resourceSubnetsDiscover( + ctx context.Context, + sg terminal.StepGroup, + sess *session.Session, + state *Resource_Subnets, +) error { + s := sg.Add("Discovering which subnets to use") + defer s.Abort() var subnets []*string + var err error if len(p.config.Subnets) == 0 { s.Update("Using default subnets for Service networking") - subnets, err = defaultSubnets(ctx, sess) + subnets, state.VpcId, err = defaultSubnets(ctx, sess) if err != nil { - return nil, err + return status.Errorf(codes.Internal, "failed to determine default subnets: %s", err) } } else { + s.Update("Using defined subnets for Service networking") subnets = make([]*string, len(p.config.Subnets)) for i := range p.config.Subnets { subnets[i] = &p.config.Subnets[i] } + + // We need to determine the vpc id via the API if we were given subnet IDs. + ec2srv := ec2.New(sess) + + subnetInfo, err := ec2srv.DescribeSubnetsWithContext(ctx, &ec2.DescribeSubnetsInput{ + SubnetIds: subnets, + }) + if err != nil { + return status.Errorf(codes.Internal, "failed to describe subnets %q: %s", strings.Join(p.config.Subnets, ", "), err) + } + if len(subnetInfo.Subnets) == 0 { + return status.Errorf(codes.Internal, "failed to find any subnets with IDs %q", strings.Join(p.config.Subnets, ", ")) + } + + state.VpcId = *subnetInfo.Subnets[0].VpcId + } + for _, subnet := range subnets { + state.Subnets = append(state.Subnets, &Resource_Subnets_Subnet{Id: *subnet}) + } + + s.Done() + return nil +} + +func (p *Platform) resourceExternalSecurityGroupsCreate( + ctx context.Context, + sg terminal.StepGroup, + sess *session.Session, + src *component.Source, + subnets *Resource_Subnets, // Required because we need to know which VPC we're in, and subnets discover it. + externalIngressPort ExternalIngressPort, + state *Resource_ExternalSecurityGroups, +) error { + name := fmt.Sprintf("%s-inbound", src.App) + s := sg.Add("Initiating creation of external security group named %s", name) + defer s.Abort() + + protocol := "tcp" + cidr := "0.0.0.0/0" + cidrDescription := "all traffic" + port := int64(externalIngressPort) + perms := []*ec2.IpPermission{{ + IpProtocol: &protocol, + FromPort: &port, + ToPort: &port, + IpRanges: []*ec2.IpRange{{ + CidrIp: &cidr, + Description: &cidrDescription, + }}, + }} + + securityGroup, err := upsertSecurityGroup(ctx, sess, s, name, subnets.VpcId, perms) + if err != nil { + return status.Errorf(codes.Internal, "failed to upsert security group %q: %s", name, err) + } + + state.SecurityGroups = append(state.SecurityGroups, securityGroup) + s.Update("Using external security group %s", securityGroup.Name) + + s.Done() + return nil +} + +func (p *Platform) resourceInternalSecurityGroupsCreate( + ctx context.Context, + sg terminal.StepGroup, + sess *session.Session, + src *component.Source, + subnets *Resource_Subnets, // Required because we need to know which VPC we're in, and subnets discover it. + extSecurityGroup *Resource_ExternalSecurityGroups, + state *Resource_InternalSecurityGroups, +) error { + s := sg.Add("Initiating security group creation...") + defer s.Abort() + + if p.config.SecurityGroupIDs != nil { + s.Update("Using specified security group IDs") + for _, sgId := range p.config.SecurityGroupIDs { + state.SecurityGroups = append(state.SecurityGroups, &Resource_SecurityGroup{Id: *sgId, Managed: false}) + } + s.Done() + return nil } + name := fmt.Sprintf("%s-inbound-internal", src.App) + + s.Update("No security groups specified - checking for existing security group named %q", name) + + if extSecurityGroup == nil || len(extSecurityGroup.SecurityGroups) == 0 || extSecurityGroup.SecurityGroups[0] == nil { + return status.Errorf(codes.Internal, "cannot create internal security group without a reference to the external security group ID") + } + + extSgId := extSecurityGroup.SecurityGroups[0].Id + + protocol := "tcp" + perms := []*ec2.IpPermission{{ + IpProtocol: &protocol, + FromPort: &p.config.ServicePort, + ToPort: &p.config.ServicePort, + UserIdGroupPairs: []*ec2.UserIdGroupPair{{ + GroupId: &extSgId, + }}, + }} + + securityGroup, err := upsertSecurityGroup(ctx, sess, s, name, subnets.VpcId, perms) + if err != nil { + return status.Errorf(codes.Internal, "failed to upsert security group %q: %s", name, err) + } + + state.SecurityGroups = append(state.SecurityGroups, securityGroup) + s.Update("Using internal security group %s", securityGroup.Name) + + s.Done() + return nil +} + +// Finds a security group by name, and creates one if it does not exist. +func upsertSecurityGroup( + ctx context.Context, + sess *session.Session, + s terminal.Step, + + name string, + vpcId string, + perms []*ec2.IpPermission, +) (*Resource_SecurityGroup, error) { ec2srv := ec2.New(sess) - subnetInfo, err := ec2srv.DescribeSubnets(&ec2.DescribeSubnetsInput{ - SubnetIds: subnets, + s.Update("Looking for existing security group named %q", name) + dsg, err := ec2srv.DescribeSecurityGroupsWithContext(ctx, &ec2.DescribeSecurityGroupsInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("group-name"), + Values: []*string{aws.String(name)}, + }, + }, }) if err != nil { - return nil, err + return nil, status.Errorf(codes.Internal, "failed to describe security groups named %q: %s", name, err) } - vpcId := subnetInfo.Subnets[0].VpcId + // We only upsert security groups that we manage + sg := &Resource_SecurityGroup{Managed: true} - var lbArn, tgArn *string - if !p.config.DisableALB { - L.Debug("creating security group for ports 80 and 443") - sgweb, err := createSG(ctx, s, sess, fmt.Sprintf("%s-inbound", app.App), vpcId, 80, 443) + if len(dsg.SecurityGroups) != 0 { + sg.Id = *dsg.SecurityGroups[0].GroupId + sg.Name = *dsg.SecurityGroups[0].GroupName + s.Update("Using existing security group with ID %s", sg.Id) + } else { + s.Update("Creating new security group named %s", name) + + out, err := ec2srv.CreateSecurityGroupWithContext(ctx, &ec2.CreateSecurityGroupInput{ + Description: aws.String("created by waypoint"), + GroupName: aws.String(name), + VpcId: &vpcId, + }) if err != nil { - return nil, err + return nil, status.Errorf(codes.Internal, "failed to create security group %q: %s", name, err) } - lbArn, tgArn, err = createALB( - ctx, s, L, sess, - app, - p.config.ALB, - vpcId, - &serviceName, - sgweb, - &p.config.ServicePort, - subnets, - ) - if err != nil { - return nil, err - } + sg.Id = *out.GroupId + sg.Name = name + + s.Update("Authorizing ingress on newly created security group %s", name) + _, err = ec2srv.AuthorizeSecurityGroupIngressWithContext(ctx, &ec2.AuthorizeSecurityGroupIngressInput{ + GroupId: &sg.Id, + IpPermissions: perms, + }) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to authorize ingress to security group %q: %s", sg.Name, err) + } + + s.Update("Created and configured security group %s", name) + } + + return sg, nil +} + +func (p *Platform) resourceLogGroupCreate( + ctx context.Context, + sg terminal.StepGroup, + sess *session.Session, + state *Resource_LogGroup, +) error { + s := sg.Add("Initiating log group creation...") + defer s.Abort() + + logGroup := p.config.LogGroup + if logGroup == "" { + logGroup = "waypoint-logs" + } + + s.Update("Looking for existing log group named %s", logGroup) + + cwl := cloudwatchlogs.New(sess) + groups, err := cwl.DescribeLogGroupsWithContext(ctx, &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int64(1), + LogGroupNamePrefix: aws.String(logGroup), + }) + if err != nil { + return status.Errorf(codes.Internal, "failed to describe log groups: %s", err) + } + + if len(groups.LogGroups) == 1 { + s.Update("Using existing log group %s", logGroup) + lg := groups.LogGroups[0] + state.Name = *lg.LogGroupName + state.Arn = *lg.Arn + s.Done() + return nil + } + + s.Update("No existing log group found - creating new CloudWatchLogs group to store logs in: %s", logGroup) + _, err = cwl.CreateLogGroup(&cloudwatchlogs.CreateLogGroupInput{ + LogGroupName: aws.String(logGroup), + }) + if err != nil { + return status.Errorf(codes.Internal, "failed creating log group %s: %s", logGroup, err) } - // Create the service + //NOTE(izaak): CreateLogGroup doesn't return the log group ARN. + state.Name = logGroup - L.Debug("creating service", "arn", *taskOut.TaskDefinition.TaskDefinitionArn) + s.Update("Created CloudWatchLogs group to store logs in: %s", logGroup) + s.Done() + return nil +} - if p.config.SecurityGroupIDs == nil { - sgecsport, err := createSG(ctx, s, sess, fmt.Sprintf("%s-inbound-internal", app.App), vpcId, int(p.config.ServicePort)) - if err != nil { - return nil, err - } +func (p *Platform) resourceTaskRoleCreate( + ctx context.Context, + sg terminal.StepGroup, + log hclog.Logger, + src *component.Source, + sess *session.Session, + state *Resource_TaskRole, +) error { + roleName := p.config.TaskRoleName - p.config.SecurityGroupIDs = append(p.config.SecurityGroupIDs, sgecsport) + if roleName == "" && len(p.config.TaskRolePolicyArns) == 0 { + log.Debug("No task role name or task role policies specified - skipping task role creation") + return nil } - count := int64(p.config.Count) - if count == 0 { - count = 1 - } + s := sg.Add("Initiating task role creation...") + defer s.Abort() - netCfg := &ecs.AwsVpcConfiguration{ - Subnets: subnets, - SecurityGroups: p.config.SecurityGroupIDs, + if roleName == "" { + roleName = src.App + "-task-role" } + state.Name = roleName - if !p.config.EC2Cluster { - netCfg.AssignPublicIp = aws.String("ENABLED") + // role names have to be 64 characters or less, and the client side doesn't validate this. + if len(roleName) > 64 { + roleName = roleName[:64] + log.Debug("using a shortened value for role name due to AWS's length limits", "roleName", roleName) } - createServiceInput := &ecs.CreateServiceInput{ - Cluster: &clusterName, - DesiredCount: aws.Int64(count), - LaunchType: runtime, - ServiceName: aws.String(serviceName), - TaskDefinition: aws.String(taskArn), - NetworkConfiguration: &ecs.NetworkConfiguration{ - AwsvpcConfiguration: netCfg, - }, - } + s.Update("Attempting to find an existing role named %q", roleName) - if !p.config.DisableALB { - createServiceInput.SetLoadBalancers([]*ecs.LoadBalancer{ - { - ContainerName: aws.String(app.App), - ContainerPort: aws.Int64(p.config.ServicePort), - TargetGroupArn: tgArn, - }, - }) + queryInput := &iam.GetRoleInput{ + RoleName: aws.String(roleName), } - s.Status("Creating ECS Service (%s, cluster-name: %s)", serviceName, clusterName) - - var servOut *ecs.CreateServiceOutput + svc := iam.New(sess) - // AWS is eventually consistent so even though we probably created the resources that - // are referenced by the service, it can error out if we try to reference those resources - // too quickly. So we're forced to guard actions which reference other AWS services - // with loops like this. - for i := 0; i < 30; i++ { - servOut, err = ecsSvc.CreateService(createServiceInput) - if err == nil { - break + roleFound := true + getOut, err := svc.GetRoleWithContext(ctx, queryInput) + if err != nil { + if aerr, ok := err.(awserr.Error); ok && aerr.Code() == "NoSuchEntity" { + roleFound = false + } else { + return status.Errorf(codes.Internal, "failed creating execution role %q: %s", roleName, err) } + } - // if we encounter an unrecoverable error, exit now. - if aerr, ok := err.(awserr.Error); ok { - switch aerr.Code() { - case "AccessDeniedException", "UnsupportedFeatureException", - "PlatformUnknownException", - "PlatformTaskDefinitionIncompatibilityException": - return nil, err - } - } + if roleFound { + s.Update("Found existing task IAM role: %s", *getOut.Role.RoleName) + state.Arn = *getOut.Role.Arn + } else { + s.Update("No existing task role found - creating role %s", roleName) - // otherwise sleep and try again - time.Sleep(2 * time.Second) - } + input := &iam.CreateRoleInput{ + AssumeRolePolicyDocument: aws.String(rolePolicy), + Path: aws.String("/"), + RoleName: aws.String(roleName), + } - if err != nil { - return nil, err + result, err := svc.CreateRoleWithContext(ctx, input) + if err != nil { + return status.Errorf(codes.Internal, "failed creating execution role %q: %s", roleName, err) + } + state.Arn = *result.Role.Arn } - s.Update("Created ECS Service (%s, cluster-name: %s)", serviceName, clusterName) - L.Debug("service started", "arn", servOut.Service.ServiceArn) + if len(p.config.TaskRolePolicyArns) > 0 { + s.Update("Task role policies specified - checking the existing policies on the task role %s", roleName) + listOut, err := svc.ListAttachedRolePoliciesWithContext(ctx, &iam.ListAttachedRolePoliciesInput{ + RoleName: &roleName, + }) + if err != nil { + return status.Errorf(codes.Internal, "failed to list attached role policies on role (ARN: %q): %s", roleName, err) + } - dep := &Deployment{ - Cluster: clusterName, - TaskArn: taskArn, - ServiceArn: *servOut.Service.ServiceArn, - } + for _, policyArn := range p.config.TaskRolePolicyArns { + alreadyAttached := false + for _, policy := range listOut.AttachedPolicies { + if policyArn == *policy.PolicyArn { + log.Debug("Requested policy arn is already attached to role", "policy arn", policyArn, "role name", roleName) + alreadyAttached = true + break + } + } - // the TargetGroupArn set here is used by Releaser to set the active - // TargetGroup's weight to 100 - if !p.config.DisableALB { - dep.TargetGroupArn = *tgArn - dep.LoadBalancerArn = *lbArn + if !alreadyAttached { + s.Update("Attaching the following policy to role %s: %q", roleName, policyArn) + _, err = svc.AttachRolePolicyWithContext(ctx, &iam.AttachRolePolicyInput{ + PolicyArn: &policyArn, + RoleName: &roleName, + }) + if err != nil { + return status.Errorf(codes.Internal, "failed to attach policy (ARN: %q) to role (name: %q): %s", policyArn, roleName, err) + } + } + } + s.Update("All requested policies are attached to role %s", roleName) } - - return dep, nil + s.Done() + return nil } -func destroyALB( +func (p *Platform) resourceExecutionRoleCreate( ctx context.Context, + sg terminal.StepGroup, log hclog.Logger, sess *session.Session, - deployment *Deployment, + src *component.Source, + state *Resource_ExecutionRole, ) error { - log.Debug("removing deployment target group from load balancer") - - elbsrv := elbv2.New(sess) + s := sg.Add("Initiating execution role creation...") + defer s.Abort() - log.Debug("load balancer arn", "arn", deployment.LoadBalancerArn) + roleName := p.config.ExecutionRoleName - listeners, err := elbsrv.DescribeListeners(&elbv2.DescribeListenersInput{ - LoadBalancerArn: &deployment.LoadBalancerArn, - }) - if err != nil { - return err + if roleName == "" { + roleName = "ecr-" + src.App + state.Managed = true + } else { + // If the role name is defined, we're not managing this role, and shouldn't destroy it later. + state.Managed = false } + // role names have to be 64 characters or less, and the client side doesn't validate this. + if len(roleName) > 64 { + roleName = roleName[:64] + log.Debug("using a shortened value for role name due to AWS's length limits", "roleName", roleName) + } + state.Name = roleName - var listener *elbv2.Listener - - if len(listeners.Listeners) > 0 { - listener = listeners.Listeners[0] - - log.Debug("listener arn", "arn", *listener.ListenerArn) - - def := listener.DefaultActions + s.Update("Attempting to find an existing role named %q", roleName) - var tgs []*elbv2.TargetGroupTuple + queryInput := &iam.GetRoleInput{ + RoleName: aws.String(roleName), + } - // If there is only 1 target group, delete the listener - if len(def) == 1 && len(def[0].ForwardConfig.TargetGroups) == 1 { - log.Debug("only 1 target group, deleting listener") - _, err = elbsrv.DeleteListener(&elbv2.DeleteListenerInput{ - ListenerArn: listener.ListenerArn, - }) + svc := iam.New(sess) - if err != nil { - return err - } - } else if len(def) > 0 && def[0].ForwardConfig != nil && len(def[0].ForwardConfig.TargetGroups) > 1 { - // Multiple target groups means we can keep the listener - var active bool + getOut, err := svc.GetRoleWithContext(ctx, queryInput) + if err == nil { + s.Update("Using existing execution IAM role %q", *getOut.Role.RoleName) - for _, tg := range def[0].ForwardConfig.TargetGroups { - if *tg.TargetGroupArn != deployment.TargetGroupArn { - tgs = append(tgs, tg) - if *tg.Weight > 0 { - active = true - } - } - } + // NOTE(izaak): We're verifying that the role exists, but not that it has the correct policy attached. + // It's possible that we failed on that step earlier. We could call AttachRolePolicy every time, but + // we're trying to minimize per-deployment aws api invocations for to stay under rate limits. - // If there are no target groups active, then we just activate the first - // one, otherwise we can't modify the listener. - if !active && len(tgs) > 0 { - tgs[0].Weight = aws.Int64(100) - } + state.Arn = *getOut.Role.Arn + s.Done() + return nil + } + // NOTE(izaak): the error returned here is an awserr.requestError, which cannot be cast to the public awserr.Error. + // So we're forced to do this. + if !strings.Contains(strings.ToLower(err.Error()), "status code: 404") { + return status.Errorf(codes.Internal, "failed to get role with name %q: %s", roleName, err) + } - log.Debug("modifying listener to remove target group", "target-groups", len(tgs)) + s.Update("No existing execution role found: creating IAM role %q", roleName) - _, err = elbsrv.ModifyListener(&elbv2.ModifyListenerInput{ - ListenerArn: listener.ListenerArn, - Port: listener.Port, - Protocol: listener.Protocol, - DefaultActions: []*elbv2.Action{ - { - ForwardConfig: &elbv2.ForwardActionConfig{ - TargetGroups: tgs, - }, - Type: aws.String("forward"), - }, - }, - }) + input := &iam.CreateRoleInput{ + AssumeRolePolicyDocument: aws.String(rolePolicy), + Path: aws.String("/"), + RoleName: aws.String(roleName), + } - if err != nil { - return err - } - } + result, err := svc.CreateRoleWithContext(ctx, input) + if err != nil { + return status.Errorf(codes.Internal, "failed creating execution role %q: %s", roleName, err) } + state.Arn = *result.Role.Arn - log.Debug("deleting target group", "arn", deployment.TargetGroupArn) + s.Update("Attaching default execution policy to role %q", roleName) + aInput := &iam.AttachRolePolicyInput{ + RoleName: aws.String(roleName), + PolicyArn: aws.String(executionRolePolicyArn), + } - _, err = elbsrv.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{ - TargetGroupArn: &deployment.TargetGroupArn, - }) + _, err = svc.AttachRolePolicyWithContext(ctx, aInput) if err != nil { - return err + return status.Errorf(codes.Internal, "failed to attach policy %q to role %q: %s", executionRolePolicyArn, roleName, err) } + s.Update("Created execution IAM role: %s", roleName) + s.Done() return nil } -func (p *Platform) Destroy( - ctx context.Context, - log hclog.Logger, - deployment *Deployment, - ui terminal.UI, -) error { +// getSession is a value provider for resource manager and provides a client +// for use by resources to interact with AWS +func (p *Platform) getSession(log hclog.Logger) (*session.Session, error) { sess, err := utils.GetSession(&utils.SessionConfig{ Region: p.config.Region, Logger: log, }) if err != nil { - return err + return nil, status.Error(codes.Internal, fmt.Sprintf("failed to create aws session: %s", err)) + } + return sess, nil +} + +// Loads state from the old locations on the top-level Deployment struct into resource manager. +// This is only necessary for backwards-compat with deployments created by waypoint pre-0.5.2. +// Newer waypoint deployments will have the complete resource manager state stored in deployment.ResourceState +func (p *Platform) loadResourceManagerState( + ctx context.Context, + rm *resource.Manager, + deployment *Deployment, + log hclog.Logger, + sg terminal.StepGroup, +) error { + log.Debug("Missing deployment resource state - must be an old deployment. Recovering state.") + s := sg.Add("Gathering deployment resource state") + defer s.Abort() + + rm.Resource("service").SetState(&Resource_Service{ + Cluster: deployment.Cluster, + Arn: deployment.ServiceArn, + }) + + targetGroupResource := Resource_TargetGroup{ + Arn: deployment.TargetGroupArn, } + rm.Resource("target group").SetState(&targetGroupResource) - if deployment.TargetGroupArn != "" && deployment.LoadBalancerArn != "" { - err = destroyALB(ctx, log, sess, deployment) + // Restore state of ALB listener. Difficult because it may only be defined on the load balancer. + var listenerResource Resource_Alb_Listener + listenerResource.TargetGroup = &targetGroupResource + if p.config.ALB != nil && p.config.ALB.ListenerARN != "" { + listenerResource.Arn = p.config.ALB.ListenerARN + listenerResource.Managed = false + log.Debug("Using existing listener arn %s", listenerResource.Arn) + } else { + listenerResource.Managed = true + s.Update("Describing load balancer %s", deployment.LoadBalancerArn) + sess, err := p.getSession(log) if err != nil { - return err + return status.Errorf(codes.Internal, "failed to get aws session: %s", err) + } + elbsrv := elbv2.New(sess) + + listeners, err := elbsrv.DescribeListenersWithContext(ctx, &elbv2.DescribeListenersInput{ + LoadBalancerArn: &deployment.LoadBalancerArn, + }) + if err != nil { + return status.Errorf(codes.Internal, "failed to describe listeners for ALB %q: %s", deployment.LoadBalancerArn, err) + } + if len(listeners.Listeners) == 0 { + s.Update("No listeners found for ALB %q", deployment.LoadBalancerArn) + } else { + listenerResource.Arn = *listeners.Listeners[0].ListenerArn + s.Update("Found existing listener (ARN: %q)", listenerResource.Arn) } } + rm.Resource("alb listener").SetState(&listenerResource) - log.Debug("deleting ecs service", "arn", deployment.ServiceArn) + s.Update("Finished gathering resource state") + s.Done() + return nil +} - _, err = ecs.New(sess).DeleteService(&ecs.DeleteServiceInput{ - Cluster: &deployment.Cluster, - Force: aws.Bool(true), - Service: &deployment.ServiceArn, +func defaultSubnets(ctx context.Context, sess *session.Session) (names []*string, vpcId string, err error) { + svc := ec2.New(sess) + + desc, err := svc.DescribeSubnetsWithContext(ctx, &ec2.DescribeSubnetsInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("default-for-az"), + Values: []*string{aws.String("true")}, + }, + }, }) if err != nil { - return err + return nil, "", err } - return nil + var subnets []*string + + for _, subnet := range desc.Subnets { + subnets = append(subnets, subnet.SubnetId) + } + + // Return tye vpc id if possible + if len(desc.Subnets) != 0 && desc.Subnets[0].VpcId != nil { + return subnets, *desc.Subnets[0].VpcId, nil + } + + return subnets, "", nil +} + +// A policy required for ECS roles - roles must be assumable by ECS tasks. +const rolePolicy = `{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +}` + +// The valid combinations of fargate cpu/memory resources. This will need to be updated +// over time as aws changes allowed combinations. It would be nice to get this from +// aws at runtime instead of hard-coding it in the future. +var fargateResources = map[int][]int{ + 512: {256}, + 1024: {256, 512}, + 2048: {256, 512, 1024}, + 3072: {512, 1024}, + 4096: {512, 1024}, + 5120: {1024}, + 6144: {1024}, + 7168: {1024}, + 8192: {1024}, +} + +// Builds logging options for use in an ECS task definition. +func buildLoggingOptions( + lo *Logging, + region string, + logGroup string, + defaultStreamPrefix string, +) map[string]*string { + + result := map[string]*string{ + "awslogs-region": aws.String(region), + "awslogs-group": aws.String(logGroup), + "awslogs-stream-prefix": aws.String(defaultStreamPrefix), + } + + if lo != nil { + // We receive the error `Log driver awslogs disallows options: awslogs-endpoint` + // when setting `awslogs-endpoint`, so that is not included here of the + // available options + // https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_awslogs.html + result["awslogs-datetime-format"] = aws.String(lo.DateTimeFormat) + result["awslogs-multiline-pattern"] = aws.String(lo.MultilinePattern) + result["mode"] = aws.String(lo.Mode) + result["max-buffer-size"] = aws.String(lo.MaxBufferSize) + + if lo.CreateGroup { + result["awslogs-create-group"] = aws.String("true") + } + if lo.StreamPrefix != "" { + result["awslogs-stream-prefix"] = aws.String(lo.StreamPrefix) + } + } + + for k, v := range result { + if *v == "" { + delete(result, k) + } + } + + return result } type ALBConfig struct { @@ -1513,6 +2281,9 @@ type ALBConfig struct { // Indicates, when creating an ALB, that it should be internal rather than // internet facing. InternalScheme *bool `hcl:"internal,optional"` + + // Internet-facing traffic port. Defaults to 80 if CertificateId is unset, 443 if set. + IngressPort int64 `hcl:"ingress_port,optional"` } type HealthCheckConfig struct { @@ -1812,6 +2583,13 @@ deploy { "to that ALB", ), ) + + doc.SetField( + "ingress_port", + "Internet-facing traffic port. Defaults to 80 if 'certificate' is unset, 443 if set.", + docs.Summary("used to set the ALB listener port, and the ALB security group ingress port"), + ) + }), ) @@ -1956,3 +2734,10 @@ deploy { return doc, nil } + +var ( + mixedHealthWarn = strings.TrimSpace(` +Waypoint detected that the current deployment is not ready, however your application +might be available or still starting up. +`) +) diff --git a/builtin/aws/ecs/plugin.pb.go b/builtin/aws/ecs/plugin.pb.go index ea45224f434..944159a6e79 100644 --- a/builtin/aws/ecs/plugin.pb.go +++ b/builtin/aws/ecs/plugin.pb.go @@ -9,6 +9,7 @@ package ecs import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" ) @@ -25,12 +26,13 @@ type Deployment struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` - TaskArn string `protobuf:"bytes,2,opt,name=task_arn,json=taskArn,proto3" json:"task_arn,omitempty"` - ServiceArn string `protobuf:"bytes,3,opt,name=service_arn,json=serviceArn,proto3" json:"service_arn,omitempty"` - TargetGroupArn string `protobuf:"bytes,4,opt,name=target_group_arn,json=targetGroupArn,proto3" json:"target_group_arn,omitempty"` - LoadBalancerArn string `protobuf:"bytes,5,opt,name=load_balancer_arn,json=loadBalancerArn,proto3" json:"load_balancer_arn,omitempty"` - Cluster string `protobuf:"bytes,6,opt,name=cluster,proto3" json:"cluster,omitempty"` + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` + TaskArn string `protobuf:"bytes,2,opt,name=task_arn,json=taskArn,proto3" json:"task_arn,omitempty"` + ServiceArn string `protobuf:"bytes,3,opt,name=service_arn,json=serviceArn,proto3" json:"service_arn,omitempty"` + TargetGroupArn string `protobuf:"bytes,4,opt,name=target_group_arn,json=targetGroupArn,proto3" json:"target_group_arn,omitempty"` + LoadBalancerArn string `protobuf:"bytes,5,opt,name=load_balancer_arn,json=loadBalancerArn,proto3" json:"load_balancer_arn,omitempty"` + Cluster string `protobuf:"bytes,6,opt,name=cluster,proto3" json:"cluster,omitempty"` + ResourceState *anypb.Any `protobuf:"bytes,7,opt,name=resource_state,json=resourceState,proto3" json:"resource_state,omitempty"` } func (x *Deployment) Reset() { @@ -107,6 +109,13 @@ func (x *Deployment) GetCluster() string { return "" } +func (x *Deployment) GetResourceState() *anypb.Any { + if x != nil { + return x.ResourceState + } + return nil +} + type Release struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -162,79 +171,1294 @@ func (x *Release) GetLoadBalancerArn() string { return "" } -var File_waypoint_builtin_aws_ecs_plugin_proto protoreflect.FileDescriptor +// Resource contains the internal resource states. +type Resource struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} -var file_waypoint_builtin_aws_ecs_plugin_proto_rawDesc = []byte{ - 0x0a, 0x25, 0x77, 0x61, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x74, - 0x69, 0x6e, 0x2f, 0x61, 0x77, 0x73, 0x2f, 0x65, 0x63, 0x73, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x65, 0x63, 0x73, 0x22, 0xca, 0x01, 0x0a, - 0x0a, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, - 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x19, 0x0a, - 0x08, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x74, 0x61, 0x73, 0x6b, 0x41, 0x72, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x72, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x41, 0x72, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, - 0x6e, 0x63, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x41, 0x72, 0x6e, 0x12, - 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x47, 0x0a, 0x07, 0x52, 0x65, 0x6c, - 0x65, 0x61, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0f, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x41, - 0x72, 0x6e, 0x42, 0x1a, 0x5a, 0x18, 0x77, 0x61, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2f, 0x62, - 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x2f, 0x61, 0x77, 0x73, 0x2f, 0x65, 0x63, 0x73, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +func (x *Resource) Reset() { + *x = Resource{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -var ( - file_waypoint_builtin_aws_ecs_plugin_proto_rawDescOnce sync.Once - file_waypoint_builtin_aws_ecs_plugin_proto_rawDescData = file_waypoint_builtin_aws_ecs_plugin_proto_rawDesc -) +func (x *Resource) String() string { + return protoimpl.X.MessageStringOf(x) +} -func file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP() []byte { - file_waypoint_builtin_aws_ecs_plugin_proto_rawDescOnce.Do(func() { - file_waypoint_builtin_aws_ecs_plugin_proto_rawDescData = protoimpl.X.CompressGZIP(file_waypoint_builtin_aws_ecs_plugin_proto_rawDescData) - }) - return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescData +func (*Resource) ProtoMessage() {} + +func (x *Resource) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_waypoint_builtin_aws_ecs_plugin_proto_goTypes = []interface{}{ - (*Deployment)(nil), // 0: ecs.Deployment - (*Release)(nil), // 1: ecs.Release +// Deprecated: Use Resource.ProtoReflect.Descriptor instead. +func (*Resource) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2} } -var file_waypoint_builtin_aws_ecs_plugin_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + +type Resource_Cluster struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Arn string `protobuf:"bytes,2,opt,name=arn,proto3" json:"arn,omitempty"` } -func init() { file_waypoint_builtin_aws_ecs_plugin_proto_init() } -func file_waypoint_builtin_aws_ecs_plugin_proto_init() { - if File_waypoint_builtin_aws_ecs_plugin_proto != nil { - return +func (x *Resource_Cluster) Reset() { + *x = Resource_Cluster{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - if !protoimpl.UnsafeEnabled { - file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Deployment); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } +} + +func (x *Resource_Cluster) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_Cluster) ProtoMessage() {} + +func (x *Resource_Cluster) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Release); i { + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_Cluster.ProtoReflect.Descriptor instead. +func (*Resource_Cluster) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 0} +} + +func (x *Resource_Cluster) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Resource_Cluster) GetArn() string { + if x != nil { + return x.Arn + } + return "" +} + +type Resource_ExecutionRole struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Arn string `protobuf:"bytes,2,opt,name=arn,proto3" json:"arn,omitempty"` + // Indicates if an execution role was created by waypoint, and should be destroyed by waypoint + Managed bool `protobuf:"varint,3,opt,name=managed,proto3" json:"managed,omitempty"` +} + +func (x *Resource_ExecutionRole) Reset() { + *x = Resource_ExecutionRole{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_ExecutionRole) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_ExecutionRole) ProtoMessage() {} + +func (x *Resource_ExecutionRole) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_ExecutionRole.ProtoReflect.Descriptor instead. +func (*Resource_ExecutionRole) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 1} +} + +func (x *Resource_ExecutionRole) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Resource_ExecutionRole) GetArn() string { + if x != nil { + return x.Arn + } + return "" +} + +func (x *Resource_ExecutionRole) GetManaged() bool { + if x != nil { + return x.Managed + } + return false +} + +type Resource_TaskRole struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Arn string `protobuf:"bytes,2,opt,name=arn,proto3" json:"arn,omitempty"` + // Indicates if the role was created by waypoint, and should be destroyed by waypoint + Managed bool `protobuf:"varint,3,opt,name=managed,proto3" json:"managed,omitempty"` +} + +func (x *Resource_TaskRole) Reset() { + *x = Resource_TaskRole{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_TaskRole) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_TaskRole) ProtoMessage() {} + +func (x *Resource_TaskRole) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_TaskRole.ProtoReflect.Descriptor instead. +func (*Resource_TaskRole) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 2} +} + +func (x *Resource_TaskRole) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Resource_TaskRole) GetArn() string { + if x != nil { + return x.Arn + } + return "" +} + +func (x *Resource_TaskRole) GetManaged() bool { + if x != nil { + return x.Managed + } + return false +} + +type Resource_Service struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Arn string `protobuf:"bytes,2,opt,name=arn,proto3" json:"arn,omitempty"` + Cluster string `protobuf:"bytes,3,opt,name=cluster,proto3" json:"cluster,omitempty"` +} + +func (x *Resource_Service) Reset() { + *x = Resource_Service{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_Service) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_Service) ProtoMessage() {} + +func (x *Resource_Service) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_Service.ProtoReflect.Descriptor instead. +func (*Resource_Service) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 3} +} + +func (x *Resource_Service) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Resource_Service) GetArn() string { + if x != nil { + return x.Arn + } + return "" +} + +func (x *Resource_Service) GetCluster() string { + if x != nil { + return x.Cluster + } + return "" +} + +type Resource_TargetGroup struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Arn string `protobuf:"bytes,2,opt,name=arn,proto3" json:"arn,omitempty"` + Port int64 `protobuf:"varint,3,opt,name=port,proto3" json:"port,omitempty"` +} + +func (x *Resource_TargetGroup) Reset() { + *x = Resource_TargetGroup{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_TargetGroup) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_TargetGroup) ProtoMessage() {} + +func (x *Resource_TargetGroup) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_TargetGroup.ProtoReflect.Descriptor instead. +func (*Resource_TargetGroup) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 4} +} + +func (x *Resource_TargetGroup) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Resource_TargetGroup) GetArn() string { + if x != nil { + return x.Arn + } + return "" +} + +func (x *Resource_TargetGroup) GetPort() int64 { + if x != nil { + return x.Port + } + return 0 +} + +type Resource_LogGroup struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Arn string `protobuf:"bytes,2,opt,name=arn,proto3" json:"arn,omitempty"` +} + +func (x *Resource_LogGroup) Reset() { + *x = Resource_LogGroup{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_LogGroup) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_LogGroup) ProtoMessage() {} + +func (x *Resource_LogGroup) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_LogGroup.ProtoReflect.Descriptor instead. +func (*Resource_LogGroup) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 5} +} + +func (x *Resource_LogGroup) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Resource_LogGroup) GetArn() string { + if x != nil { + return x.Arn + } + return "" +} + +type Resource_SecurityGroup struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + // Indicates if a security group was created by waypoint, and should be destroyed by waypoint + Managed bool `protobuf:"varint,3,opt,name=managed,proto3" json:"managed,omitempty"` +} + +func (x *Resource_SecurityGroup) Reset() { + *x = Resource_SecurityGroup{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_SecurityGroup) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_SecurityGroup) ProtoMessage() {} + +func (x *Resource_SecurityGroup) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_SecurityGroup.ProtoReflect.Descriptor instead. +func (*Resource_SecurityGroup) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 6} +} + +func (x *Resource_SecurityGroup) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Resource_SecurityGroup) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Resource_SecurityGroup) GetManaged() bool { + if x != nil { + return x.Managed + } + return false +} + +// To be attached directly to the ECS tasks +type Resource_InternalSecurityGroups struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SecurityGroups []*Resource_SecurityGroup `protobuf:"bytes,1,rep,name=security_groups,json=securityGroups,proto3" json:"security_groups,omitempty"` +} + +func (x *Resource_InternalSecurityGroups) Reset() { + *x = Resource_InternalSecurityGroups{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_InternalSecurityGroups) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_InternalSecurityGroups) ProtoMessage() {} + +func (x *Resource_InternalSecurityGroups) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_InternalSecurityGroups.ProtoReflect.Descriptor instead. +func (*Resource_InternalSecurityGroups) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 7} +} + +func (x *Resource_InternalSecurityGroups) GetSecurityGroups() []*Resource_SecurityGroup { + if x != nil { + return x.SecurityGroups + } + return nil +} + +// To be attached to the ALB +type Resource_ExternalSecurityGroups struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SecurityGroups []*Resource_SecurityGroup `protobuf:"bytes,1,rep,name=security_groups,json=securityGroups,proto3" json:"security_groups,omitempty"` +} + +func (x *Resource_ExternalSecurityGroups) Reset() { + *x = Resource_ExternalSecurityGroups{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_ExternalSecurityGroups) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_ExternalSecurityGroups) ProtoMessage() {} + +func (x *Resource_ExternalSecurityGroups) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_ExternalSecurityGroups.ProtoReflect.Descriptor instead. +func (*Resource_ExternalSecurityGroups) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 8} +} + +func (x *Resource_ExternalSecurityGroups) GetSecurityGroups() []*Resource_SecurityGroup { + if x != nil { + return x.SecurityGroups + } + return nil +} + +type Resource_Subnets struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Subnets []*Resource_Subnets_Subnet `protobuf:"bytes,1,rep,name=subnets,proto3" json:"subnets,omitempty"` + // Currently, the subnet resource is responsible for discovering which VPC we're running in, + // because we determine it by describing either the default subnets or the user-specified subnets. + VpcId string `protobuf:"bytes,2,opt,name=vpc_id,json=vpcId,proto3" json:"vpc_id,omitempty"` +} + +func (x *Resource_Subnets) Reset() { + *x = Resource_Subnets{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_Subnets) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_Subnets) ProtoMessage() {} + +func (x *Resource_Subnets) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_Subnets.ProtoReflect.Descriptor instead. +func (*Resource_Subnets) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 9} +} + +func (x *Resource_Subnets) GetSubnets() []*Resource_Subnets_Subnet { + if x != nil { + return x.Subnets + } + return nil +} + +func (x *Resource_Subnets) GetVpcId() string { + if x != nil { + return x.VpcId + } + return "" +} + +type Resource_Alb struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Arn string `protobuf:"bytes,2,opt,name=arn,proto3" json:"arn,omitempty"` + DnsName string `protobuf:"bytes,3,opt,name=dns_name,json=dnsName,proto3" json:"dns_name,omitempty"` + CanonicalHostedZoneId string `protobuf:"bytes,4,opt,name=canonical_hosted_zone_id,json=canonicalHostedZoneId,proto3" json:"canonical_hosted_zone_id,omitempty"` + // Indicates if an ALB was created by waypoint, and should be destroyed by waypoint + Managed bool `protobuf:"varint,5,opt,name=managed,proto3" json:"managed,omitempty"` +} + +func (x *Resource_Alb) Reset() { + *x = Resource_Alb{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_Alb) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_Alb) ProtoMessage() {} + +func (x *Resource_Alb) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_Alb.ProtoReflect.Descriptor instead. +func (*Resource_Alb) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 10} +} + +func (x *Resource_Alb) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Resource_Alb) GetArn() string { + if x != nil { + return x.Arn + } + return "" +} + +func (x *Resource_Alb) GetDnsName() string { + if x != nil { + return x.DnsName + } + return "" +} + +func (x *Resource_Alb) GetCanonicalHostedZoneId() string { + if x != nil { + return x.CanonicalHostedZoneId + } + return "" +} + +func (x *Resource_Alb) GetManaged() bool { + if x != nil { + return x.Managed + } + return false +} + +type Resource_TaskDefinition struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Arn string `protobuf:"bytes,2,opt,name=arn,proto3" json:"arn,omitempty"` + Runtime string `protobuf:"bytes,3,opt,name=runtime,proto3" json:"runtime,omitempty"` +} + +func (x *Resource_TaskDefinition) Reset() { + *x = Resource_TaskDefinition{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_TaskDefinition) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_TaskDefinition) ProtoMessage() {} + +func (x *Resource_TaskDefinition) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_TaskDefinition.ProtoReflect.Descriptor instead. +func (*Resource_TaskDefinition) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 11} +} + +func (x *Resource_TaskDefinition) GetArn() string { + if x != nil { + return x.Arn + } + return "" +} + +func (x *Resource_TaskDefinition) GetRuntime() string { + if x != nil { + return x.Runtime + } + return "" +} + +type Resource_Route53Record struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + ZoneId string `protobuf:"bytes,2,opt,name=zone_id,json=zoneId,proto3" json:"zone_id,omitempty"` +} + +func (x *Resource_Route53Record) Reset() { + *x = Resource_Route53Record{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_Route53Record) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_Route53Record) ProtoMessage() {} + +func (x *Resource_Route53Record) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_Route53Record.ProtoReflect.Descriptor instead. +func (*Resource_Route53Record) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 12} +} + +func (x *Resource_Route53Record) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Resource_Route53Record) GetZoneId() string { + if x != nil { + return x.ZoneId + } + return "" +} + +type Resource_Subnets_Subnet struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *Resource_Subnets_Subnet) Reset() { + *x = Resource_Subnets_Subnet{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_Subnets_Subnet) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_Subnets_Subnet) ProtoMessage() {} + +func (x *Resource_Subnets_Subnet) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_Subnets_Subnet.ProtoReflect.Descriptor instead. +func (*Resource_Subnets_Subnet) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 9, 0} +} + +func (x *Resource_Subnets_Subnet) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type Resource_Alb_Listener struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Arn string `protobuf:"bytes,1,opt,name=arn,proto3" json:"arn,omitempty"` + TargetGroup *Resource_TargetGroup `protobuf:"bytes,2,opt,name=target_group,json=targetGroup,proto3" json:"target_group,omitempty"` + // Indicates if an ALB was created by waypoint, and should be destroyed by waypoint + Managed bool `protobuf:"varint,3,opt,name=managed,proto3" json:"managed,omitempty"` +} + +func (x *Resource_Alb_Listener) Reset() { + *x = Resource_Alb_Listener{} + if protoimpl.UnsafeEnabled { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource_Alb_Listener) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource_Alb_Listener) ProtoMessage() {} + +func (x *Resource_Alb_Listener) ProtoReflect() protoreflect.Message { + mi := &file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource_Alb_Listener.ProtoReflect.Descriptor instead. +func (*Resource_Alb_Listener) Descriptor() ([]byte, []int) { + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP(), []int{2, 10, 0} +} + +func (x *Resource_Alb_Listener) GetArn() string { + if x != nil { + return x.Arn + } + return "" +} + +func (x *Resource_Alb_Listener) GetTargetGroup() *Resource_TargetGroup { + if x != nil { + return x.TargetGroup + } + return nil +} + +func (x *Resource_Alb_Listener) GetManaged() bool { + if x != nil { + return x.Managed + } + return false +} + +var File_waypoint_builtin_aws_ecs_plugin_proto protoreflect.FileDescriptor + +var file_waypoint_builtin_aws_ecs_plugin_proto_rawDesc = []byte{ + 0x0a, 0x25, 0x77, 0x61, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x74, + 0x69, 0x6e, 0x2f, 0x61, 0x77, 0x73, 0x2f, 0x65, 0x63, 0x73, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x65, 0x63, 0x73, 0x1a, 0x19, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, + 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x87, 0x02, 0x0a, 0x0a, 0x44, 0x65, 0x70, 0x6c, + 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x61, 0x73, 0x6b, + 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x61, 0x73, 0x6b, + 0x41, 0x72, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, + 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x41, 0x72, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x61, 0x72, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x72, 0x6e, 0x12, 0x2a, + 0x0a, 0x11, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x5f, + 0x61, 0x72, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x6f, 0x61, 0x64, 0x42, + 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x41, 0x72, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, + 0x6e, 0x79, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x22, 0x47, 0x0a, 0x07, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x2a, + 0x0a, 0x11, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x5f, + 0x61, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x6f, 0x61, 0x64, 0x42, + 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x41, 0x72, 0x6e, 0x22, 0xaf, 0x09, 0x0a, 0x08, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x2f, 0x0a, 0x07, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x6e, 0x1a, 0x4f, 0x0a, 0x0d, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x6e, 0x12, + 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x1a, 0x4a, 0x0a, 0x08, 0x54, 0x61, 0x73, + 0x6b, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x64, 0x1a, 0x49, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x72, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x1a, 0x47, 0x0a, 0x0b, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x61, 0x72, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x1a, 0x30, 0x0a, 0x08, 0x4c, 0x6f, 0x67, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x6e, 0x1a, 0x4d, 0x0a, 0x0d, 0x53, + 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x1a, 0x5e, 0x0a, 0x16, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x12, 0x44, 0x0a, 0x0f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, + 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x65, 0x63, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x63, + 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x75, + 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x1a, 0x5e, 0x0a, 0x16, 0x45, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x12, 0x44, 0x0a, 0x0f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, + 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x65, 0x63, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x63, + 0x75, 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x75, + 0x72, 0x69, 0x74, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x1a, 0x72, 0x0a, 0x07, 0x53, 0x75, + 0x62, 0x6e, 0x65, 0x74, 0x73, 0x12, 0x36, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x63, 0x73, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, 0x2e, 0x53, 0x75, + 0x62, 0x6e, 0x65, 0x74, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, 0x12, 0x15, 0x0a, + 0x06, 0x76, 0x70, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x70, 0x63, 0x49, 0x64, 0x1a, 0x18, 0x0a, 0x06, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x1a, 0x8f, + 0x02, 0x0a, 0x03, 0x41, 0x6c, 0x62, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x6e, 0x12, 0x19, 0x0a, 0x08, + 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x63, 0x61, 0x6e, 0x6f, 0x6e, + 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x61, 0x6e, 0x6f, 0x6e, + 0x69, 0x63, 0x61, 0x6c, 0x48, 0x6f, 0x73, 0x74, 0x65, 0x64, 0x5a, 0x6f, 0x6e, 0x65, 0x49, 0x64, + 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x1a, 0x74, 0x0a, 0x08, 0x4c, 0x69, + 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, 0x6e, 0x12, 0x3c, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x65, 0x63, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, + 0x1a, 0x3c, 0x0a, 0x0e, 0x54, 0x61, 0x73, 0x6b, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x72, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x1a, 0x3c, + 0x0a, 0x0d, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x35, 0x33, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x7a, 0x6f, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x7a, 0x6f, 0x6e, 0x65, 0x49, 0x64, 0x42, 0x1a, 0x5a, 0x18, + 0x77, 0x61, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, + 0x2f, 0x61, 0x77, 0x73, 0x2f, 0x65, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_waypoint_builtin_aws_ecs_plugin_proto_rawDescOnce sync.Once + file_waypoint_builtin_aws_ecs_plugin_proto_rawDescData = file_waypoint_builtin_aws_ecs_plugin_proto_rawDesc +) + +func file_waypoint_builtin_aws_ecs_plugin_proto_rawDescGZIP() []byte { + file_waypoint_builtin_aws_ecs_plugin_proto_rawDescOnce.Do(func() { + file_waypoint_builtin_aws_ecs_plugin_proto_rawDescData = protoimpl.X.CompressGZIP(file_waypoint_builtin_aws_ecs_plugin_proto_rawDescData) + }) + return file_waypoint_builtin_aws_ecs_plugin_proto_rawDescData +} + +var file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 18) +var file_waypoint_builtin_aws_ecs_plugin_proto_goTypes = []interface{}{ + (*Deployment)(nil), // 0: ecs.Deployment + (*Release)(nil), // 1: ecs.Release + (*Resource)(nil), // 2: ecs.Resource + (*Resource_Cluster)(nil), // 3: ecs.Resource.Cluster + (*Resource_ExecutionRole)(nil), // 4: ecs.Resource.ExecutionRole + (*Resource_TaskRole)(nil), // 5: ecs.Resource.TaskRole + (*Resource_Service)(nil), // 6: ecs.Resource.Service + (*Resource_TargetGroup)(nil), // 7: ecs.Resource.TargetGroup + (*Resource_LogGroup)(nil), // 8: ecs.Resource.LogGroup + (*Resource_SecurityGroup)(nil), // 9: ecs.Resource.SecurityGroup + (*Resource_InternalSecurityGroups)(nil), // 10: ecs.Resource.InternalSecurityGroups + (*Resource_ExternalSecurityGroups)(nil), // 11: ecs.Resource.ExternalSecurityGroups + (*Resource_Subnets)(nil), // 12: ecs.Resource.Subnets + (*Resource_Alb)(nil), // 13: ecs.Resource.Alb + (*Resource_TaskDefinition)(nil), // 14: ecs.Resource.TaskDefinition + (*Resource_Route53Record)(nil), // 15: ecs.Resource.Route53Record + (*Resource_Subnets_Subnet)(nil), // 16: ecs.Resource.Subnets.Subnet + (*Resource_Alb_Listener)(nil), // 17: ecs.Resource.Alb.Listener + (*anypb.Any)(nil), // 18: google.protobuf.Any +} +var file_waypoint_builtin_aws_ecs_plugin_proto_depIdxs = []int32{ + 18, // 0: ecs.Deployment.resource_state:type_name -> google.protobuf.Any + 9, // 1: ecs.Resource.InternalSecurityGroups.security_groups:type_name -> ecs.Resource.SecurityGroup + 9, // 2: ecs.Resource.ExternalSecurityGroups.security_groups:type_name -> ecs.Resource.SecurityGroup + 16, // 3: ecs.Resource.Subnets.subnets:type_name -> ecs.Resource.Subnets.Subnet + 7, // 4: ecs.Resource.Alb.Listener.target_group:type_name -> ecs.Resource.TargetGroup + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_waypoint_builtin_aws_ecs_plugin_proto_init() } +func file_waypoint_builtin_aws_ecs_plugin_proto_init() { + if File_waypoint_builtin_aws_ecs_plugin_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Deployment); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Release); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_Cluster); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_ExecutionRole); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_TaskRole); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_Service); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_TargetGroup); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_LogGroup); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_SecurityGroup); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_InternalSecurityGroups); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_ExternalSecurityGroups); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_Subnets); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_Alb); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_TaskDefinition); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_Route53Record); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_Subnets_Subnet); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_waypoint_builtin_aws_ecs_plugin_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource_Alb_Listener); i { case 0: return &v.state case 1: @@ -252,7 +1476,7 @@ func file_waypoint_builtin_aws_ecs_plugin_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_waypoint_builtin_aws_ecs_plugin_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 18, NumExtensions: 0, NumServices: 0, }, diff --git a/builtin/aws/ecs/plugin.proto b/builtin/aws/ecs/plugin.proto index 4bf3a53def1..c52054b6fad 100644 --- a/builtin/aws/ecs/plugin.proto +++ b/builtin/aws/ecs/plugin.proto @@ -4,6 +4,8 @@ package ecs; option go_package = "waypoint/builtin/aws/ecs"; +import "google/protobuf/any.proto"; + message Deployment { string url = 1; string task_arn = 2; @@ -11,9 +13,110 @@ message Deployment { string target_group_arn = 4; string load_balancer_arn = 5; string cluster = 6; + google.protobuf.Any resource_state = 7; } message Release { string url = 1; string load_balancer_arn = 2; } + +// Resource contains the internal resource states. +message Resource { + message Cluster { + string name = 1; + string arn = 2; + } + + message ExecutionRole { + string name = 1; + string arn = 2; + + // Indicates if an execution role was created by waypoint, and should be destroyed by waypoint + bool managed = 3; + } + + message TaskRole { + string name = 1; + string arn = 2; + + // Indicates if the role was created by waypoint, and should be destroyed by waypoint + bool managed = 3; + } + + message Service { + string name = 1; + string arn = 2; + string cluster = 3; + } + + message TargetGroup { + string name = 1; + string arn = 2; + int64 port = 3; + } + + message LogGroup { + string name = 1; + string arn = 2; + } + + message SecurityGroup { + string name = 1; + string id = 2; + + // Indicates if a security group was created by waypoint, and should be destroyed by waypoint + bool managed = 3; + } + + // To be attached directly to the ECS tasks + message InternalSecurityGroups { + repeated SecurityGroup security_groups = 1; + } + + // To be attached to the ALB + message ExternalSecurityGroups { + repeated SecurityGroup security_groups = 1; + } + + message Subnets { + repeated Subnet subnets = 1; + message Subnet { + string id = 1; + } + + // Currently, the subnet resource is responsible for discovering which VPC we're running in, + // because we determine it by describing either the default subnets or the user-specified subnets. + string vpc_id = 2; + } + + message Alb { + string name = 1; + string arn = 2; + string dns_name = 3; + string canonical_hosted_zone_id = 4; + + // Indicates if an ALB was created by waypoint, and should be destroyed by waypoint + bool managed = 5; + + message Listener { + string arn = 1; + TargetGroup target_group = 2; + + // Indicates if an ALB was created by waypoint, and should be destroyed by waypoint + bool managed = 3; + } + } + + message TaskDefinition{ + string arn = 2; + string runtime = 3; + } + + message Route53Record { + string name = 1; + string zone_id = 2; + } +} + + diff --git a/builtin/docker/plugin.pb.go b/builtin/docker/plugin.pb.go index 31056d1677f..f531e50df84 100644 --- a/builtin/docker/plugin.pb.go +++ b/builtin/docker/plugin.pb.go @@ -1,13 +1,12 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0 -// protoc v3.17.3 +// protoc-gen-go v1.26.0 +// protoc v3.15.8 // source: waypoint/builtin/docker/plugin.proto package docker import ( - proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" @@ -23,10 +22,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - // Image is the artifact type for the registry. type Image struct { state protoimpl.MessageState diff --git a/builtin/pack/plugin.pb.go b/builtin/pack/plugin.pb.go index f1ba844b0e7..af35e01db52 100644 --- a/builtin/pack/plugin.pb.go +++ b/builtin/pack/plugin.pb.go @@ -1,13 +1,12 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0 -// protoc v3.17.3 +// protoc-gen-go v1.26.0 +// protoc v3.15.8 // source: waypoint/builtin/pack/plugin.proto package pack import ( - proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -21,10 +20,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - // DockerImage is the artifact type for the builder. type DockerImage struct { state protoimpl.MessageState diff --git a/website/content/partials/components/platform-aws-ecs.mdx b/website/content/partials/components/platform-aws-ecs.mdx index cae8efa1a89..2f9740da0c5 100644 --- a/website/content/partials/components/platform-aws-ecs.mdx +++ b/website/content/partials/components/platform-aws-ecs.mdx @@ -42,6 +42,15 @@ Set along with zone_id to have DNS automatically setup for the ALB. this value s - Type: **string** - **Optional** +##### alb.ingress_port + +Internet-facing traffic port. Defaults to 80 if 'certificate' is unset, 443 if set. + +Used to set the ALB listener port, and the ALB security group ingress port. + +- Type: **int64** +- **Optional** + ##### alb.internal Whether or not the created ALB should be internal. @@ -208,33 +217,11 @@ On Fargate, possible values for this are configured by the amount of memory the 1024MB: 256, 512 2048MB: 256, 512, 1024 3072MB: 512, 1024 -4096MB: 512, 1024, 2048 -5120MB: 1024, 2048 -6144MB: 1024, 2048 -7168MB: 1024, 2048 -8192MB: 1024, 2048, 4096 -9216MB: 2048, 4096 -10240MB: 2048, 4096 -11264MB: 2048, 4096 -12288MB: 2048, 4096 -13312MB: 2048, 4096 -14336MB: 2048, 4096 -15360MB: 2048, 4096 -16384MB: 4096 -17408MB: 4096 -18432MB: 4096 -19456MB: 4096 -20480MB: 4096 -21504MB: 4096 -22528MB: 4096 -23552MB: 4096 -24576MB: 4096 -25600MB: 4096 -26624MB: 4096 -27648MB: 4096 -28672MB: 4096 -29696MB: 4096 -30720MB: 4096. +4096MB: 512, 1024 +5120MB: 1024 +6144MB: 1024 +7168MB: 1024 +8192MB: 1024. - Type: **int** - **Optional** @@ -345,6 +332,10 @@ Output attributes can be used in your `waypoint.hcl` as [variables](/docs/waypoi - Type: **string** +#### resource_state + +- Type: **anypb.Any** + #### service_arn - Type: **string**