func (s *Route) CheckChanges(a, e, changes *Route) error { if a == nil { // TODO: Create validate method? if e.RouteTable == nil { return fi.RequiredField("RouteTable") } if e.CIDR == nil { return fi.RequiredField("CIDR") } targetCount := 0 if e.InternetGateway != nil { targetCount++ } if e.Instance != nil { targetCount++ } if targetCount == 0 { return fmt.Errorf("InternetGateway or Instance is required") } if targetCount != 1 { return fmt.Errorf("Cannot set both InternetGateway and Instance") } } if a != nil { if changes.RouteTable != nil { return fi.CannotChangeField("RouteTable") } if changes.CIDR != nil { return fi.CannotChangeField("CIDR") } } return nil }
func (s *Subnet) CheckChanges(a, e, changes *Subnet) error { if a == nil { if e.VPC == nil { return fi.RequiredField("VPC") } if e.CIDR == nil { // TODO: Auto-assign CIDR? return fi.RequiredField("CIDR") } } if a != nil { if changes.VPC != nil { // TODO: Do we want to destroy & recreate the subnet? return fi.CannotChangeField("VPC") } if changes.AvailabilityZone != nil { // TODO: Do we want to destroy & recreate the subnet? return fi.CannotChangeField("AvailabilityZone") } if changes.CIDR != nil { // TODO: Do we want to destroy & recreate the subnet? return fi.CannotChangeField("CIDR") } } return nil }
func (s *LoadBalancerHealthChecks) CheckChanges(a, e, changes *LoadBalancerHealthChecks) error { if a == nil { if e.LoadBalancer == nil { return fi.RequiredField("LoadBalancer") } if e.Target == nil { return fi.RequiredField("Target") } } return nil }
func (s *LoadBalancerAttachment) CheckChanges(a, e, changes *LoadBalancerAttachment) error { if a == nil { if e.LoadBalancer == nil { return fi.RequiredField("LoadBalancer") } if e.AutoscalingGroup == nil { return fi.RequiredField("AutoscalingGroup") } } return nil }
func (s *IAMInstanceProfileRole) CheckChanges(a, e, changes *IAMInstanceProfileRole) error { if a != nil { if e.Role == nil { return fi.RequiredField("Role") } if e.InstanceProfile == nil { return fi.RequiredField("InstanceProfile") } } return nil }
func (s *LoadBalancer) CheckChanges(a, e, changes *LoadBalancer) error { if a == nil { if fi.StringValue(e.Name) == "" { return fi.RequiredField("Name") } if len(e.SecurityGroups) == 0 { return fi.RequiredField("SecurityGroups") } if len(e.Subnets) == 0 { return fi.RequiredField("Subnets") } } return nil }
func (s *LaunchConfiguration) CheckChanges(a, e, changes *LaunchConfiguration) error { if e.ImageID == nil { return fi.RequiredField("ImageID") } if e.InstanceType == nil { return fi.RequiredField("InstanceType") } if a != nil { if e.Name == nil { return fi.RequiredField("Name") } } return nil }
func (s *IAMInstanceProfile) CheckChanges(a, e, changes *IAMInstanceProfile) error { if a != nil { if fi.StringValue(e.Name) == "" { return fi.RequiredField("Name") } } return nil }
func (_ *SecurityGroupRule) CheckChanges(a, e, changes *SecurityGroupRule) error { if a == nil { if e.SecurityGroup == nil { return fi.RequiredField("SecurityGroup") } } return nil }
func (_ *Instance) CheckChanges(a, e, changes *Instance) error { if a != nil { if e.Name == nil { return fi.RequiredField("Name") } } return nil }
func (s *VPCDHCPOptionsAssociation) CheckChanges(a, e, changes *VPCDHCPOptionsAssociation) error { if e.VPC == nil { return fi.RequiredField("VPC") } if e.DHCPOptions == nil { return fi.RequiredField("DHCPOptions") } if a != nil && changes != nil { if changes.VPC != nil { // Should be impossible anyway because VPC is our primary key... return fi.CannotChangeField("VPC") } } return nil }
func (s *AutoscalingGroup) CheckChanges(a, e, changes *AutoscalingGroup) error { if a != nil { if e.Name == nil { return fi.RequiredField("Name") } } return nil }
func (s *IAMRolePolicy) CheckChanges(a, e, changes *IAMRolePolicy) error { if a != nil { if e.Name == nil { return fi.RequiredField("Name") } } return nil }
func (s *DNSName) CheckChanges(a, e, changes *DNSName) error { if a == nil { if fi.StringValue(e.Name) == "" { return fi.RequiredField("Name") } } return nil }
func (s *RouteTableAssociation) CheckChanges(a, e, changes *RouteTableAssociation) error { if a != nil { if e.RouteTable == nil { return fi.RequiredField("RouteTable") } if e.Subnet == nil { return fi.RequiredField("Subnet") } } if a != nil { if changes.RouteTable != nil { return fi.CannotChangeField("RouteTable") } if changes.Subnet != nil { return fi.CannotChangeField("Subnet") } } return nil }
func (s *IAMRole) CheckChanges(a, e, changes *IAMRole) error { if a != nil { if e.Name == nil { return fi.RequiredField("Name") } } else { if changes.Name == nil { return fi.CannotChangeField("Name") } } return nil }
func (_ *LaunchConfiguration) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *LaunchConfiguration) error { launchConfigurationName := *e.Name + "-" + fi.BuildTimestampString() glog.V(2).Infof("Creating AutoscalingLaunchConfiguration with Name:%q", launchConfigurationName) if e.ImageID == nil { return fi.RequiredField("ImageID") } image, err := t.Cloud.ResolveImage(*e.ImageID) if err != nil { return err } request := &autoscaling.CreateLaunchConfigurationInput{} request.LaunchConfigurationName = &launchConfigurationName request.ImageId = image.ImageId request.InstanceType = e.InstanceType if e.SSHKey != nil { request.KeyName = e.SSHKey.Name } securityGroupIDs := []*string{} for _, sg := range e.SecurityGroups { securityGroupIDs = append(securityGroupIDs, sg.ID) } request.SecurityGroups = securityGroupIDs request.AssociatePublicIpAddress = e.AssociatePublicIP if e.BlockDeviceMappings != nil { request.BlockDeviceMappings = []*autoscaling.BlockDeviceMapping{} for device, bdm := range e.BlockDeviceMappings { request.BlockDeviceMappings = append(request.BlockDeviceMappings, bdm.ToAutoscaling(device)) } } if e.UserData != nil { d, err := e.UserData.AsBytes() if err != nil { return fmt.Errorf("error rendering AutoScalingLaunchConfiguration UserData: %v", err) } request.UserData = aws.String(base64.StdEncoding.EncodeToString(d)) } if e.IAMInstanceProfile != nil { request.IamInstanceProfile = e.IAMInstanceProfile.Name } _, err = t.Cloud.Autoscaling.CreateLaunchConfiguration(request) if err != nil { return fmt.Errorf("error creating AutoscalingLaunchConfiguration: %v", err) } e.ID = fi.String(launchConfigurationName) return nil // No tags on a launch configuration }
func (_ *EBSVolume) CheckChanges(a, e, changes *EBSVolume) error { if a == nil { if e.Name == nil { return fi.RequiredField("Name") } } if a != nil { if changes.ID != nil { return fi.CannotChangeField("ID") } } return nil }
func (s *RouteTable) CheckChanges(a, e, changes *RouteTable) error { if a == nil { if e.VPC == nil { return fi.RequiredField("VPC") } } if a != nil { if changes.VPC != nil && changes.VPC.ID != nil { return fi.CannotChangeField("VPC") } } return nil }
func (_ *LaunchConfiguration) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *LaunchConfiguration) error { cloud := t.Cloud.(*awsup.AWSCloud) if e.ImageID == nil { return fi.RequiredField("ImageID") } image, err := cloud.ResolveImage(*e.ImageID) if err != nil { return err } tf := &terraformLaunchConfiguration{ NamePrefix: fi.String(*e.Name + "-"), ImageID: image.ImageId, InstanceType: e.InstanceType, } if e.SSHKey != nil { tf.KeyName = e.SSHKey.TerraformLink() } for _, sg := range e.SecurityGroups { tf.SecurityGroups = append(tf.SecurityGroups, sg.TerraformLink()) } tf.AssociatePublicIpAddress = e.AssociatePublicIP if e.BlockDeviceMappings != nil { tf.EphemeralBlockDevice = []*terraformBlockDevice{} for deviceName, bdm := range e.BlockDeviceMappings { tf.EphemeralBlockDevice = append(tf.EphemeralBlockDevice, &terraformBlockDevice{ VirtualName: bdm.VirtualName, DeviceName: fi.String(deviceName), }) } } if e.UserData != nil { tf.UserData, err = t.AddFile("aws_launch_configuration", *e.Name, "user_data", e.UserData) if err != nil { return err } } if e.IAMInstanceProfile != nil { tf.IAMInstanceProfile = e.IAMInstanceProfile.TerraformLink() } // So that we can update configurations tf.Lifecycle = &terraformLifecycle{CreateBeforeDestroy: fi.Bool(true)} return t.RenderResource("aws_launch_configuration", *e.Name, tf) }
func (_ *Secret) Render(c *fi.Context, a, e, changes *Secret) error { name := fi.StringValue(e.Name) if name == "" { return fi.RequiredField("Name") } secrets := c.SecretStore _, _, err := secrets.GetOrCreateSecret(name) if err != nil { return fmt.Errorf("error creating secret %q: %v", name, err) } return nil }
func (s *InstanceVolumeAttachment) CheckChanges(a, e, changes *InstanceVolumeAttachment) error { if a != nil { if changes.Device != nil { // TODO: Support this? return fi.CannotChangeField("Device") } } if a == nil { if e.Device == nil { return fi.RequiredField("Device") } } return nil }
func (s *VPC) CheckChanges(a, e, changes *VPC) error { if a == nil { if e.CIDR == nil { // TODO: Auto-assign CIDR? return fi.RequiredField("CIDR") } } if a != nil { if changes.CIDR != nil { // TODO: Do we want to destroy & recreate the VPC? return fi.CannotChangeField("CIDR") } } return nil }
func (_ *PersistentDisk) CheckChanges(a, e, changes *PersistentDisk) error { if a != nil { if changes.SizeGB != nil { return fi.CannotChangeField("SizeGB") } if changes.Zone != nil { return fi.CannotChangeField("Zone") } if changes.VolumeType != nil { return fi.CannotChangeField("VolumeType") } } else { if e.Zone == nil { return fi.RequiredField("Zone") } } return nil }
func addEphemeralDevices(instanceTypeName *string, blockDeviceMappings map[string]*BlockDeviceMapping) (map[string]*BlockDeviceMapping, error) { // TODO: Any reason not to always attach the ephemeral devices? if instanceTypeName == nil { return nil, fi.RequiredField("InstanceType") } instanceType, err := awsup.GetMachineTypeInfo(*instanceTypeName) if err != nil { return nil, err } if blockDeviceMappings == nil { blockDeviceMappings = make(map[string]*BlockDeviceMapping) } for _, ed := range instanceType.EphemeralDevices() { if _, found := blockDeviceMappings[ed.DeviceName]; found { glog.Warningf("not attach ephemeral device - found duplicate device mapping: %q", ed.DeviceName) continue } blockDeviceMappings[ed.DeviceName] = &BlockDeviceMapping{VirtualName: fi.String(ed.VirtualName)} } return blockDeviceMappings, nil }
func (s *DHCPOptions) CheckChanges(a, e, changes *DHCPOptions) error { if a == nil { if e.Name == nil { return fi.RequiredField("Name") } } if a != nil { if changes.ID != nil { return fi.CannotChangeField("ID") } // TODO: Delete & create new DHCPOptions // We can't delete the DHCPOptions while it is attached, but we can change the tag (add a timestamp suffix?) if changes.DomainName != nil { return fi.CannotChangeField("DomainName") } if changes.DomainNameServers != nil { return fi.CannotChangeField("DomainNameServers") } } return nil }
func (_ *RouteTable) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *RouteTable) error { if a == nil { vpcID := e.VPC.ID if vpcID == nil { return fi.RequiredField("VPC.ID") } glog.V(2).Infof("Creating RouteTable with VPC: %q", *vpcID) request := &ec2.CreateRouteTableInput{ VpcId: vpcID, } response, err := t.Cloud.EC2.CreateRouteTable(request) if err != nil { return fmt.Errorf("error creating RouteTable: %v", err) } rt := response.RouteTable e.ID = rt.RouteTableId } return t.AddAWSTags(*e.ID, t.Cloud.BuildTags(e.Name)) }
func (_ *Keypair) Render(c *fi.Context, a, e, changes *Keypair) error { name := fi.StringValue(e.Name) if name == "" { return fi.RequiredField("Name") } castore := c.CAStore template, err := buildCertificateTemplate(e.Type) if err != nil { return err } subjectPkix, err := parsePkixName(e.Subject) if err != nil { return fmt.Errorf("error parsing Subject: %v", err) } if len(subjectPkix.ToRDNSequence()) == 0 { return fmt.Errorf("Subject name was empty for SSL keypair %q", name) } template.Subject = *subjectPkix var alternateNames []string alternateNames = append(alternateNames, e.AlternateNames...) for _, san := range alternateNames { san = strings.TrimSpace(san) if san == "" { continue } if ip := net.ParseIP(san); ip != nil { template.IPAddresses = append(template.IPAddresses, ip) } else { template.DNSNames = append(template.DNSNames, san) } } createCertificate := false if a == nil { createCertificate = true } else if changes != nil { if changes.AlternateNames != nil { createCertificate = true } else { glog.Warningf("Ignoring changes in key: %v", fi.DebugAsJsonString(changes)) } } if createCertificate { glog.V(2).Infof("Creating PKI keypair %q", name) // TODO: Reuse private key if already exists? cert, _, err := castore.CreateKeypair(name, template) if err != nil { return err } glog.V(8).Infof("created certificate %v", cert) } // TODO: Check correct subject / flags return nil }
func (_ *LoadBalancer) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *LoadBalancer) error { elbName := e.ID if elbName == nil { elbName = e.Name } if elbName == nil { return fi.RequiredField("ID") } if a == nil { request := &elb.CreateLoadBalancerInput{} request.LoadBalancerName = elbName for _, subnet := range e.Subnets { request.Subnets = append(request.Subnets, subnet.ID) } for _, sg := range e.SecurityGroups { request.SecurityGroups = append(request.SecurityGroups, sg.ID) } request.Listeners = []*elb.Listener{} for loadBalancerPort, listener := range e.Listeners { loadBalancerPortInt, err := strconv.ParseInt(loadBalancerPort, 10, 64) if err != nil { return fmt.Errorf("error parsing load balancer listener port: %q", loadBalancerPort) } awsListener := listener.mapToAWS(loadBalancerPortInt) request.Listeners = append(request.Listeners, awsListener) } glog.V(2).Infof("Creating ELB with Name:%q", *e.ID) response, err := t.Cloud.ELB.CreateLoadBalancer(request) if err != nil { return fmt.Errorf("error creating ELB: %v", err) } e.DNSName = response.DNSName e.ID = elbName lb, err := findELB(t.Cloud, *e.ID) if err != nil { return err } if lb == nil { // TODO: Retry? Is this async return fmt.Errorf("Unable to find newly created ELB") } e.HostedZoneId = lb.CanonicalHostedZoneNameID } else { if changes.Subnets != nil { return fmt.Errorf("subnet changes on LoadBalancer not yet implemented") } if changes.Listeners != nil { request := &elb.CreateLoadBalancerListenersInput{} request.LoadBalancerName = elbName for loadBalancerPort, listener := range changes.Listeners { loadBalancerPortInt, err := strconv.ParseInt(loadBalancerPort, 10, 64) if err != nil { return fmt.Errorf("error parsing load balancer listener port: %q", loadBalancerPort) } awsListener := listener.mapToAWS(loadBalancerPortInt) request.Listeners = append(request.Listeners, awsListener) } glog.V(2).Infof("Creating LoadBalancer listeners") _, err := t.Cloud.ELB.CreateLoadBalancerListeners(request) if err != nil { return fmt.Errorf("error creating LoadBalancerListeners: %v", err) } } } return t.AddELBTags(*e.ID, t.Cloud.BuildTags(e.Name)) }
func (_ *Instance) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Instance) error { if a == nil { if e.ImageID == nil { return fi.RequiredField("ImageID") } image, err := t.Cloud.ResolveImage(*e.ImageID) if err != nil { return err } glog.V(2).Infof("Creating Instance with Name:%q", *e.Name) request := &ec2.RunInstancesInput{ ImageId: image.ImageId, InstanceType: e.InstanceType, MinCount: aws.Int64(1), MaxCount: aws.Int64(1), } if e.SSHKey != nil { request.KeyName = e.SSHKey.Name } securityGroupIDs := []*string{} for _, sg := range e.SecurityGroups { securityGroupIDs = append(securityGroupIDs, sg.ID) } request.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{ { DeviceIndex: aws.Int64(0), AssociatePublicIpAddress: e.AssociatePublicIP, SubnetId: e.Subnet.ID, PrivateIpAddress: e.PrivateIPAddress, Groups: securityGroupIDs, }, } if e.BlockDeviceMappings != nil { request.BlockDeviceMappings = []*ec2.BlockDeviceMapping{} for deviceName, bdm := range e.BlockDeviceMappings { request.BlockDeviceMappings = append(request.BlockDeviceMappings, bdm.ToEC2(deviceName)) } } if e.UserData != nil { d, err := fi.ResourceAsBytes(e.UserData) if err != nil { return fmt.Errorf("error rendering Instance UserData: %v", err) } if len(d) > MaxUserDataSize { // TODO: Re-enable gzip? // But it exposes some bugs in the AWS console, so if we can avoid it, we should //d, err = fi.GzipBytes(d) //if err != nil { // return fmt.Errorf("error while gzipping UserData: %v", err) //} return fmt.Errorf("Instance UserData was too large (%d bytes)", len(d)) } request.UserData = aws.String(base64.StdEncoding.EncodeToString(d)) } if e.IAMInstanceProfile != nil { request.IamInstanceProfile = &ec2.IamInstanceProfileSpecification{ Name: e.IAMInstanceProfile.Name, } } response, err := t.Cloud.EC2.RunInstances(request) if err != nil { return fmt.Errorf("error creating Instance: %v", err) } e.ID = response.Instances[0].InstanceId } return t.AddAWSTags(*e.ID, e.Tags) }