diff --git a/Gopkg.lock b/Gopkg.lock index e9c17b48eab..4238d58df9b 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -678,7 +678,7 @@ version = "v1.14.0" [[projects]] - digest = "1:2df7f3427c2bd044b207d209484a897e583152cd2d2b18b052b392528b484267" + digest = "1:9a534a24bb6e27a51fb71d7dac2602bd8660c98b6321090dd37adeb7fbc7442a" name = "github.com/openshift/installer" packages = [ "pkg/asset/installconfig/aws", @@ -699,7 +699,7 @@ "pkg/version", ] pruneopts = "NUT" - revision = "ae2baf820f22b9bf1ed40932e7b702d790087574" + revision = "6fdffbb9c21d90ba97fc79dff3ccbeac22fb2a0e" [[projects]] branch = "master" @@ -1889,6 +1889,7 @@ "k8s.io/apimachinery/pkg/runtime/serializer", "k8s.io/apimachinery/pkg/runtime/serializer/json", "k8s.io/apimachinery/pkg/types", + "k8s.io/apimachinery/pkg/util/clock", "k8s.io/apimachinery/pkg/util/diff", "k8s.io/apimachinery/pkg/util/errors", "k8s.io/apimachinery/pkg/util/rand", diff --git a/Gopkg.toml b/Gopkg.toml index 8f60ec4d14f..7d2d8f92c7d 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -102,7 +102,7 @@ version="v1.4.7" [[constraint]] name = "github.com/openshift/installer" - revision = "ae2baf820f22b9bf1ed40932e7b702d790087574" + revision = "6fdffbb9c21d90ba97fc79dff3ccbeac22fb2a0e" [[constraint]] name = "github.com/openshift/generic-admission-server" diff --git a/vendor/github.com/openshift/installer/pkg/asset/installconfig/aws/permissions.go b/vendor/github.com/openshift/installer/pkg/asset/installconfig/aws/permissions.go index 833f487fee2..06ab6d4bb6a 100644 --- a/vendor/github.com/openshift/installer/pkg/asset/installconfig/aws/permissions.go +++ b/vendor/github.com/openshift/installer/pkg/asset/installconfig/aws/permissions.go @@ -26,6 +26,7 @@ var installPermissions = []string{ "ec2:CreateDhcpOptions", "ec2:CreateInternetGateway", "ec2:CreateNatGateway", + "ec2:CreateNetworkInterface", "ec2:CreateRoute", "ec2:CreateRouteTable", "ec2:CreateSecurityGroup", diff --git a/vendor/github.com/openshift/installer/pkg/destroy/aws/aws.go b/vendor/github.com/openshift/installer/pkg/destroy/aws/aws.go index ddf31641052..6d3bddf90d1 100644 --- a/vendor/github.com/openshift/installer/pkg/destroy/aws/aws.go +++ b/vendor/github.com/openshift/installer/pkg/destroy/aws/aws.go @@ -131,7 +131,6 @@ func (o *ClusterUninstaller) Run() error { tagClientNames[tagClient] = "us-east-1" } - deleted := map[string]struct{}{} iamClient := iam.New(awsSession) iamRoleSearch := &iamRoleSearch{ client: iamClient, @@ -144,6 +143,11 @@ func (o *ClusterUninstaller) Run() error { logger: o.Logger, } + deleted, err := terminateEC2InstancesByTags(ec2.New(awsSession), iamClient, o.Filters, o.Logger) + if err != nil { + return err + } + err = wait.PollImmediateInfinite( time.Second*10, func() (done bool, err error) { @@ -532,7 +536,7 @@ func deleteEC2(session *session.Session, arn arn.ARN, filter Filter, logger logr case "image": return deleteEC2Image(client, id, filter, logger) case "instance": - return deleteEC2Instance(client, iam.New(session), id, logger) + return terminateEC2Instance(client, iam.New(session), id, logger) case "internet-gateway": return deleteEC2InternetGateway(client, id, logger) case "natgateway": @@ -628,7 +632,7 @@ func deleteEC2ElasticIP(client *ec2.EC2, id string, logger logrus.FieldLogger) e return nil } -func deleteEC2Instance(ec2Client *ec2.EC2, iamClient *iam.IAM, id string, logger logrus.FieldLogger) error { +func terminateEC2Instance(ec2Client *ec2.EC2, iamClient *iam.IAM, id string, logger logrus.FieldLogger) error { response, err := ec2Client.DescribeInstances(&ec2.DescribeInstancesInput{ InstanceIds: []*string{aws.String(id)}, }) @@ -641,35 +645,116 @@ func deleteEC2Instance(ec2Client *ec2.EC2, iamClient *iam.IAM, id string, logger for _, reservation := range response.Reservations { for _, instance := range reservation.Instances { - // Skip 'terminated' instances since they take a while to really get cleaned up - if *instance.State.Name == "terminated" { - continue - } - if instance.IamInstanceProfile != nil { - parsed, err := arn.Parse(*instance.IamInstanceProfile.Arn) - if err != nil { - return errors.Wrap(err, "parse ARN for IAM instance profile") - } - - err = deleteIAMInstanceProfile(iamClient, parsed, logger) - if err != nil { - return errors.Wrapf(err, "deleting %s", parsed.String()) - } - } - - _, err := ec2Client.TerminateInstances(&ec2.TerminateInstancesInput{ - InstanceIds: []*string{instance.InstanceId}, - }) + err = terminateEC2InstanceByInstance(ec2Client, iamClient, instance, logger) if err != nil { return err } + } + } + return nil +} + +func terminateEC2InstanceByInstance(ec2Client *ec2.EC2, iamClient *iam.IAM, instance *ec2.Instance, logger logrus.FieldLogger) error { + // Skip 'shutting-down' and 'terminated' instances since they take a while to get cleaned up + if instance.State == nil || *instance.State.Name == "shutting-down" || *instance.State.Name == "terminated" { + return nil + } + + if instance.IamInstanceProfile != nil { + parsed, err := arn.Parse(*instance.IamInstanceProfile.Arn) + if err != nil { + return errors.Wrap(err, "parse ARN for IAM instance profile") + } - logger.Info("Deleted") + err = deleteIAMInstanceProfile(iamClient, parsed, logger) + if err != nil { + return errors.Wrapf(err, "deleting %s", parsed.String()) } } + + _, err := ec2Client.TerminateInstances(&ec2.TerminateInstancesInput{ + InstanceIds: []*string{instance.InstanceId}, + }) + if err != nil { + return err + } + + logger.Info("Terminating") return nil } +// terminateEC2InstancesByTags loops until there all instances which +// match the given tags are terminated. +func terminateEC2InstancesByTags(ec2Client *ec2.EC2, iamClient *iam.IAM, filters []Filter, logger logrus.FieldLogger) (map[string]struct{}, error) { + if ec2Client.Config.Region == nil { + return nil, errors.New("EC2 client does not have region configured") + } + + terminated := map[string]struct{}{} + err := wait.PollImmediateInfinite( + time.Second*10, + func() (done bool, err error) { + var loopError error + matched := false + for _, filter := range filters { + logger.Debugf("search for and delete matching instances by tag matching %#+v", filter) + instanceFilters := make([]*ec2.Filter, 0, len(filter)) + for key, value := range filter { + instanceFilters = append(instanceFilters, &ec2.Filter{ + Name: aws.String("tag:" + key), + Values: []*string{aws.String(value)}, + }) + } + err = ec2Client.DescribeInstancesPages( + &ec2.DescribeInstancesInput{Filters: instanceFilters}, + func(results *ec2.DescribeInstancesOutput, lastPage bool) bool { + for _, reservation := range results.Reservations { + if reservation.OwnerId == nil { + continue + } + + for _, instance := range reservation.Instances { + if instance.InstanceId == nil || instance.State == nil { + continue + } + + instanceLogger := logger.WithField("instance", *instance.InstanceId) + if *instance.State.Name == "terminated" { + arn := fmt.Sprintf("arn:aws:ec2:%s:%s:instance/%s", *ec2Client.Config.Region, *reservation.OwnerId, *instance.InstanceId) + if _, ok := terminated[arn]; !ok { + instanceLogger.Debug("Terminated") + terminated[arn] = exists + } + continue + } + + matched = true + err := terminateEC2InstanceByInstance(ec2Client, iamClient, instance, instanceLogger) + if err != nil { + instanceLogger.Debug(err) + loopError = errors.Wrapf(err, "terminating %s", *instance.InstanceId) + continue + } + } + } + + return !lastPage + }, + ) + if err != nil { + err = errors.Wrap(err, "describe instances") + logger.Info(err) + matched = true + loopError = err + } + } + + return !matched && loopError == nil, nil + }, + ) + return terminated, err +} + // This is a bit of hack. Some objects, like Instance Profiles, can not be tagged in AWS. // We "normally" find those objects by their relation to other objects. We have found, // however, that people regularly delete all of their instances and roles outside of @@ -880,31 +965,45 @@ func deleteEC2SecurityGroup(client *ec2.EC2, id string, logger logrus.FieldLogge } for _, group := range response.SecurityGroups { - if len(group.IpPermissions) > 0 { - _, err := client.RevokeSecurityGroupIngress(&ec2.RevokeSecurityGroupIngressInput{ - GroupId: group.GroupId, - IpPermissions: group.IpPermissions, - }) - if err != nil { - return errors.Wrap(err, "revoking ingress permissions") - } - logger.Debug("Revoked ingress permissions") + err = deleteEC2SecurityGroupObject(client, group, logger) + if err != nil { + return err } + } - if len(group.IpPermissionsEgress) > 0 { - _, err := client.RevokeSecurityGroupEgress(&ec2.RevokeSecurityGroupEgressInput{ - GroupId: group.GroupId, - IpPermissions: group.IpPermissionsEgress, - }) - if err != nil { - return errors.Wrap(err, "revoking egress permissions") - } - logger.Debug("Revoked egress permissions") + return nil +} + +func deleteEC2SecurityGroupObject(client *ec2.EC2, group *ec2.SecurityGroup, logger logrus.FieldLogger) error { + if group.GroupName != nil && *group.GroupName == "default" { + logger.Debug("Skipping default security group") + return nil + } + + if len(group.IpPermissions) > 0 { + _, err := client.RevokeSecurityGroupIngress(&ec2.RevokeSecurityGroupIngressInput{ + GroupId: group.GroupId, + IpPermissions: group.IpPermissions, + }) + if err != nil { + return errors.Wrap(err, "revoking ingress permissions") + } + logger.Debug("Revoked ingress permissions") + } + + if len(group.IpPermissionsEgress) > 0 { + _, err := client.RevokeSecurityGroupEgress(&ec2.RevokeSecurityGroupEgressInput{ + GroupId: group.GroupId, + IpPermissions: group.IpPermissionsEgress, + }) + if err != nil { + return errors.Wrap(err, "revoking egress permissions") } + logger.Debug("Revoked egress permissions") } - _, err = client.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{ - GroupId: aws.String(id), + _, err := client.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{ + GroupId: group.GroupId, }) if err != nil { if err.(awserr.Error).Code() == "InvalidGroup.NotFound" { @@ -917,6 +1016,41 @@ func deleteEC2SecurityGroup(client *ec2.EC2, id string, logger logrus.FieldLogge return nil } +func deleteEC2SecurityGroupsByVPC(client *ec2.EC2, vpc string, failFast bool, logger logrus.FieldLogger) error { + var lastError error + err := client.DescribeSecurityGroupsPages( + &ec2.DescribeSecurityGroupsInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("vpc-id"), + Values: []*string{&vpc}, + }, + }, + }, + func(results *ec2.DescribeSecurityGroupsOutput, lastPage bool) bool { + for _, group := range results.SecurityGroups { + err := deleteEC2SecurityGroupObject(client, group, logger.WithField("security group", *group.GroupId)) + if err != nil { + if lastError != nil { + logger.Debug(err) + } + lastError = errors.Wrapf(err, "deleting EC2 security group %s", *group.GroupId) + if failFast { + return false + } + } + } + + return !lastPage + }, + ) + + if lastError != nil { + return lastError + } + return err +} + func deleteEC2Snapshot(client *ec2.EC2, id string, logger logrus.FieldLogger) error { _, err := client.DeleteSnapshot(&ec2.DeleteSnapshotInput{ SnapshotId: &id, @@ -997,6 +1131,35 @@ func deleteEC2Subnet(client *ec2.EC2, id string, logger logrus.FieldLogger) erro return nil } +func deleteEC2SubnetsByVPC(client *ec2.EC2, vpc string, failFast bool, logger logrus.FieldLogger) error { + // FIXME: port to DescribeSubnetsPages once we bump our vendored AWS package past v1.19.30 + results, err := client.DescribeSubnets( + &ec2.DescribeSubnetsInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("vpc-id"), + Values: []*string{&vpc}, + }, + }, + }, + ) + if err != nil { + return err + } + + for _, subnet := range results.Subnets { + err := deleteEC2Subnet(client, *subnet.SubnetId, logger.WithField("subnet", *subnet.SubnetId)) + if err != nil { + err = errors.Wrapf(err, "deleting EC2 subnet %s", *subnet.SubnetId) + if failFast { + return err + } + } + } + + return nil +} + func deleteEC2Volume(client *ec2.EC2, id string, logger logrus.FieldLogger) error { _, err := client.DeleteVolume(&ec2.DeleteVolumeInput{ VolumeId: aws.String(id), @@ -1029,6 +1192,8 @@ func deleteEC2VPC(ec2Client *ec2.EC2, elbClient *elb.ELB, elbv2Client *elbv2.ELB deleteEC2NATGatewaysByVPC, // not always tagged deleteEC2NetworkInterfaceByVPC, // not always tagged deleteEC2RouteTablesByVPC, // not always tagged + deleteEC2SecurityGroupsByVPC, // not always tagged + deleteEC2SubnetsByVPC, // not always tagged deleteEC2VPCEndpointsByVPC, // not taggable } { err := helper(ec2Client, id, true, logger) diff --git a/vendor/github.com/openshift/installer/pkg/types/aws/machinepool.go b/vendor/github.com/openshift/installer/pkg/types/aws/machinepool.go index 7b499e48e76..9e118fecc7c 100644 --- a/vendor/github.com/openshift/installer/pkg/types/aws/machinepool.go +++ b/vendor/github.com/openshift/installer/pkg/types/aws/machinepool.go @@ -10,7 +10,7 @@ type MachinePool struct { // eg. m4-large InstanceType string `json:"type"` - // EC2RootVolume defines the storage for ec2 instance. + // EC2RootVolume defines the root volume for EC2 instances in the machine pool. EC2RootVolume `json:"rootVolume"` } @@ -41,10 +41,11 @@ func (a *MachinePool) Set(required *MachinePool) { // EC2RootVolume defines the storage for an ec2 instance. type EC2RootVolume struct { - // IOPS defines the iops for the storage. + // IOPS defines the amount of provisioned IOPS. This is only valid + // for type io1. IOPS int `json:"iops"` - // Size defines the size of the storage. + // Size defines the size of the volume in gibibytes (GiB). Size int `json:"size"` - // Type defines the type of the storage. + // Type defines the type of the volume. Type string `json:"type"` } diff --git a/vendor/github.com/openshift/installer/pkg/types/aws/platform.go b/vendor/github.com/openshift/installer/pkg/types/aws/platform.go index 5ecf153a5bb..93153f4b8c7 100644 --- a/vendor/github.com/openshift/installer/pkg/types/aws/platform.go +++ b/vendor/github.com/openshift/installer/pkg/types/aws/platform.go @@ -10,7 +10,9 @@ type Platform struct { // Region specifies the AWS region where the cluster will be created. Region string `json:"region"` - // UserTags specifies additional tags for AWS resources created for the cluster. + // UserTags additional keys and values that the installer will add + // as tags to all resources that it creates. Resources created by the + // cluster itself may not include these tags. // +optional UserTags map[string]string `json:"userTags,omitempty"` diff --git a/vendor/github.com/openshift/installer/pkg/types/azure/platform.go b/vendor/github.com/openshift/installer/pkg/types/azure/platform.go index 1b0b833a1c9..ce761599b81 100644 --- a/vendor/github.com/openshift/installer/pkg/types/azure/platform.go +++ b/vendor/github.com/openshift/installer/pkg/types/azure/platform.go @@ -8,7 +8,7 @@ type Platform struct { // Region specifies the Azure region where the cluster will be created. Region string `json:"region"` - // BaseDomainResourceGroupName specifies the resource group where the azure DNS zone for the base domain is found + // BaseDomainResourceGroupName specifies the resource group where the Azure DNS zone for the base domain is found. BaseDomainResourceGroupName string `json:"baseDomainResourceGroupName,omitempty"` // DefaultMachinePlatform is the default configuration used when // installing on Azure for machine pools which do not define their own diff --git a/vendor/github.com/openshift/installer/pkg/types/installconfig.go b/vendor/github.com/openshift/installer/pkg/types/installconfig.go index 72afcf41b08..29d0d054960 100644 --- a/vendor/github.com/openshift/installer/pkg/types/installconfig.go +++ b/vendor/github.com/openshift/installer/pkg/types/installconfig.go @@ -54,14 +54,15 @@ type InstallConfig struct { // +optional AdditionalTrustBundle string `json:"additionalTrustBundle,omitempty"` - // SSHKey is the public ssh key to provide access to instances. + // SSHKey is the public Secure Shell (SSH) key to provide access to instances. // +optional SSHKey string `json:"sshKey,omitempty"` // BaseDomain is the base domain to which the cluster should belong. BaseDomain string `json:"baseDomain"` - // Networking defines the pod network provider in the cluster. + // Networking is the configuration for the pod network provider in + // the cluster. *Networking `json:"networking,omitempty"` // ControlPlane is the configuration for the machines that comprise the @@ -69,7 +70,8 @@ type InstallConfig struct { // +optional ControlPlane *MachinePool `json:"controlPlane,omitempty"` - // Compute is the list of compute MachinePools that need to be installed. + // Compute is the configuration for the machines that comprise the + // compute nodes. // +optional Compute []MachinePool `json:"compute,omitempty"` @@ -86,6 +88,7 @@ type InstallConfig struct { Proxy *Proxy `json:"proxy,omitempty"` // ImageContentSources lists sources/repositories for the release-image content. + // +optional ImageContentSources []ImageContentSource `json:"imageContentSources,omitempty"` } @@ -160,10 +163,10 @@ func (p *Platform) Name() string { // Networking defines the pod network provider in the cluster. type Networking struct { - // MachineCIDR is the IP address space from which to assign machine IPs. + // MachineCIDR is the IP address pool for machines. // +optional - // Default is 10.0.0.0/16 for all platforms other than Libvirt. - // For Libvirt, the default is 192.168.126.0/24. + // Default is 10.0.0.0/16 for all platforms other than libvirt. + // For libvirt, the default is 192.168.126.0/24. MachineCIDR *ipnet.IPNet `json:"machineCIDR,omitempty"` // NetworkType is the type of network to install. @@ -171,14 +174,14 @@ type Networking struct { // Default is OpenShiftSDN. NetworkType string `json:"networkType,omitempty"` - // ClusterNetwork is the IP address pool to use for pod IPs. + // ClusterNetwork is the IP address pool for pods. // +optional - // Default is 10.128.0.0/14 and a host prefix of /23 + // Default is 10.128.0.0/14 and a host prefix of /23. ClusterNetwork []ClusterNetworkEntry `json:"clusterNetwork,omitempty"` - // ServiceNetwork is the IP address pool to use for service IPs. + // ServiceNetwork is the IP address pool for services. // +optional - // Default is 172.30.0.0/16 + // Default is 172.30.0.0/16. // NOTE: currently only one entry is supported. ServiceNetwork []ipnet.IPNet `json:"serviceNetwork,omitempty"` @@ -200,7 +203,7 @@ type Networking struct { // ClusterNetworkEntry is a single IP address block for pod IP blocks. IP blocks // are allocated with size 2^HostSubnetLength. type ClusterNetworkEntry struct { - // The IP block address pool + // CIDR is the IP block address pool. CIDR ipnet.IPNet `json:"cidr"` // HostPrefix is the prefix size to allocate to each node from the CIDR. diff --git a/vendor/github.com/openshift/installer/pkg/types/machinepools.go b/vendor/github.com/openshift/installer/pkg/types/machinepools.go index a544da94486..16665bdcdf5 100644 --- a/vendor/github.com/openshift/installer/pkg/types/machinepools.go +++ b/vendor/github.com/openshift/installer/pkg/types/machinepools.go @@ -27,13 +27,13 @@ type MachinePool struct { // For the compute machine pools, the only valid name is "worker". Name string `json:"name"` - // Replicas is the count of machines for this machine pool. + // Replicas is the machine count for the machine pool. Replicas *int64 `json:"replicas,omitempty"` // Platform is configuration for machine pool specific to the platform. Platform MachinePoolPlatform `json:"platform"` - // Hyperthreading determines the mode of hyperthreading that machines in this + // Hyperthreading determines the mode of hyperthreading that machines in the // pool will utilize. // +optional // Default is for hyperthreading to be enabled.