func (_ *VPC) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *VPC) error { shared := fi.BoolValue(e.Shared) if shared { // Verify the VPC was found and matches our required settings if a == nil { return fmt.Errorf("VPC with id %q not found", fi.StringValue(e.ID)) } if changes != nil && changes.EnableDNSSupport != nil { return fmt.Errorf("VPC with id %q was set to be shared, but did not have EnableDNSSupport=true", fi.StringValue(e.ID)) } if changes != nil && changes.EnableDNSHostnames != nil { return fmt.Errorf("VPC with id %q was set to be shared, but did not have EnableDNSHostnames=true", fi.StringValue(e.ID)) } return nil } if a == nil { glog.V(2).Infof("Creating VPC with CIDR: %q", *e.CIDR) request := &ec2.CreateVpcInput{ CidrBlock: e.CIDR, } response, err := t.Cloud.EC2.CreateVpc(request) if err != nil { return fmt.Errorf("error creating VPC: %v", err) } e.ID = response.Vpc.VpcId } if changes.EnableDNSSupport != nil { request := &ec2.ModifyVpcAttributeInput{ VpcId: e.ID, EnableDnsSupport: &ec2.AttributeBooleanValue{Value: changes.EnableDNSSupport}, } _, err := t.Cloud.EC2.ModifyVpcAttribute(request) if err != nil { return fmt.Errorf("error modifying VPC attribute: %v", err) } } if changes.EnableDNSHostnames != nil { request := &ec2.ModifyVpcAttributeInput{ VpcId: e.ID, EnableDnsHostnames: &ec2.AttributeBooleanValue{Value: changes.EnableDNSHostnames}, } _, err := t.Cloud.EC2.ModifyVpcAttribute(request) if err != nil { return fmt.Errorf("error modifying VPC attribute: %v", err) } } return t.AddAWSTags(*e.ID, t.Cloud.BuildTags(e.Name)) }
func (_ *SecurityGroupRule) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *SecurityGroupRule) error { if a == nil { protocol := e.Protocol if protocol == nil { protocol = aws.String("-1") } ipPermission := &ec2.IpPermission{ IpProtocol: protocol, FromPort: e.FromPort, ToPort: e.ToPort, } if e.SourceGroup != nil { ipPermission.UserIdGroupPairs = []*ec2.UserIdGroupPair{ { GroupId: e.SourceGroup.ID, }, } } else { // Default to 0.0.0.0/0 ? ipPermission.IpRanges = []*ec2.IpRange{ {CidrIp: e.CIDR}, } } if fi.BoolValue(e.Egress) { request := &ec2.AuthorizeSecurityGroupEgressInput{ GroupId: e.SecurityGroup.ID, } request.IpPermissions = []*ec2.IpPermission{ipPermission} glog.V(2).Infof("Calling EC2 AuthorizeSecurityGroupEgress") _, err := t.Cloud.EC2.AuthorizeSecurityGroupEgress(request) if err != nil { return fmt.Errorf("error creating SecurityGroupEgress: %v", err) } } else { request := &ec2.AuthorizeSecurityGroupIngressInput{ GroupId: e.SecurityGroup.ID, } request.IpPermissions = []*ec2.IpPermission{ipPermission} glog.V(2).Infof("Calling EC2 AuthorizeSecurityGroupIngress") _, err := t.Cloud.EC2.AuthorizeSecurityGroupIngress(request) if err != nil { return fmt.Errorf("error creating SecurityGroupIngress: %v", err) } } } // No tags on security group rules (there are tags on the group though) return nil }
func (e *InternetGateway) TerraformLink() *terraform.Literal { shared := fi.BoolValue(e.Shared) if shared { if e.ID == nil { glog.Fatalf("ID must be set, if InternetGateway is shared: %s", e) } glog.V(4).Infof("reusing existing InternetGateway with id %q", *e.ID) return terraform.LiteralFromStringValue(*e.ID) } return terraform.LiteralProperty("aws_internet_gateway", *e.Name, "id") }
func (e *InternetGateway) Find(c *fi.Context) (*InternetGateway, error) { cloud := c.Cloud.(*awsup.AWSCloud) request := &ec2.DescribeInternetGatewaysInput{} shared := fi.BoolValue(e.Shared) if shared { if fi.StringValue(e.VPC.ID) == "" { return nil, fmt.Errorf("VPC ID is required when InternetGateway is shared") } request.Filters = []*ec2.Filter{awsup.NewEC2Filter("attachment.vpc-id", *e.VPC.ID)} } else { if e.ID != nil { request.InternetGatewayIds = []*string{e.ID} } else { request.Filters = cloud.BuildFilters(e.Name) } } response, err := cloud.EC2.DescribeInternetGateways(request) if err != nil { return nil, fmt.Errorf("error listing InternetGateways: %v", err) } if response == nil || len(response.InternetGateways) == 0 { return nil, nil } if len(response.InternetGateways) != 1 { return nil, fmt.Errorf("found multiple InternetGateways matching tags") } igw := response.InternetGateways[0] actual := &InternetGateway{ ID: igw.InternetGatewayId, Name: findNameTag(igw.Tags), } glog.V(2).Infof("found matching InternetGateway %q", *actual.ID) for _, attachment := range igw.Attachments { actual.VPC = &VPC{ID: attachment.VpcId} } // Prevent spurious comparison failures actual.Shared = e.Shared if e.ID == nil { e.ID = actual.ID } return actual, nil }
func (_ *InternetGateway) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *InternetGateway) error { shared := fi.BoolValue(e.Shared) if shared { // Not terraform owned / managed return nil } cloud := t.Cloud.(*awsup.AWSCloud) tf := &terraformInternetGateway{ VPCID: e.VPC.TerraformLink(), Tags: cloud.BuildTags(e.Name), } return t.RenderResource("aws_internet_gateway", *e.Name, tf) }
func (_ *Service) RenderCloudInit(t *cloudinit.CloudInitTarget, a, e, changes *Service) error { serviceName := e.Name servicePath := path.Join(systemdSystemPath, serviceName) err := t.WriteFile(servicePath, fi.NewStringResource(*e.Definition), 0644, 0755) if err != nil { return err } if fi.BoolValue(e.ManageState) { t.AddCommand(cloudinit.Once, "systemctl", "daemon-reload") t.AddCommand(cloudinit.Once, "systemctl", "start", "--no-block", serviceName) } return nil }
func (_ *VPC) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *VPC) error { cloud := t.Cloud.(*awsup.AWSCloud) shared := fi.BoolValue(e.Shared) if shared { // Not terraform owned / managed return nil } tf := &terraformVPC{ CIDR: e.CIDR, Tags: cloud.BuildTags(e.Name), EnableDNSHostnames: e.EnableDNSHostnames, EnableDNSSupport: e.EnableDNSSupport, } return t.RenderResource("aws_vpc", *e.Name, tf) }
func (_ *InternetGateway) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *InternetGateway) error { shared := fi.BoolValue(e.Shared) if shared { // Verify the InternetGateway was found and matches our required settings if a == nil { return fmt.Errorf("InternetGateway for shared VPC was not found") } return nil } if a == nil { glog.V(2).Infof("Creating InternetGateway") request := &ec2.CreateInternetGatewayInput{} response, err := t.Cloud.EC2.CreateInternetGateway(request) if err != nil { return fmt.Errorf("error creating InternetGateway: %v", err) } e.ID = response.InternetGateway.InternetGatewayId } if a == nil || (changes != nil && changes.VPC != nil) { glog.V(2).Infof("Creating InternetGatewayAttachment") attachRequest := &ec2.AttachInternetGatewayInput{ VpcId: e.VPC.ID, InternetGatewayId: e.ID, } _, err := t.Cloud.EC2.AttachInternetGateway(attachRequest) if err != nil { return fmt.Errorf("error attaching InternetGateway to VPC: %v", err) } } return t.AddAWSTags(*e.ID, t.Cloud.BuildTags(e.Name)) }
func (_ *SecurityGroupRule) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *SecurityGroupRule) error { tf := &terraformSecurityGroupIngress{ Type: fi.String("ingress"), SecurityGroup: e.SecurityGroup.TerraformLink(), FromPort: e.FromPort, ToPort: e.ToPort, Protocol: e.Protocol, } if fi.BoolValue(e.Egress) { tf.Type = fi.String("egress") } if e.Protocol == nil { tf.Protocol = fi.String("-1") tf.FromPort = fi.Int64(0) tf.ToPort = fi.Int64(0) } if tf.FromPort == nil { // FromPort is required by tf tf.FromPort = fi.Int64(0) } if tf.ToPort == nil { // ToPort is required by tf tf.ToPort = fi.Int64(65535) } if e.SourceGroup != nil { tf.SourceGroup = e.SourceGroup.TerraformLink() } if e.CIDR != nil { tf.CIDRBlocks = append(tf.CIDRBlocks, *e.CIDR) } return t.RenderResource("aws_security_group_rule", *e.Name, tf) }
func (e *SecurityGroupRule) Find(c *fi.Context) (*SecurityGroupRule, error) { cloud := c.Cloud.(*awsup.AWSCloud) if e.SecurityGroup == nil || e.SecurityGroup.ID == nil { return nil, nil } request := &ec2.DescribeSecurityGroupsInput{ Filters: []*ec2.Filter{ awsup.NewEC2Filter("group-id", *e.SecurityGroup.ID), }, } response, err := cloud.EC2.DescribeSecurityGroups(request) if err != nil { return nil, fmt.Errorf("error listing SecurityGroup: %v", err) } if response == nil || len(response.SecurityGroups) == 0 { return nil, nil } if len(response.SecurityGroups) != 1 { glog.Fatalf("found multiple security groups for id=%s", *e.SecurityGroup.ID) } sg := response.SecurityGroups[0] //glog.V(2).Info("found existing security group") var foundRule *ec2.IpPermission matchProtocol := "-1" // Wildcard if e.Protocol != nil { matchProtocol = *e.Protocol } ipPermissions := sg.IpPermissions if fi.BoolValue(e.Egress) { ipPermissions = sg.IpPermissionsEgress } for _, rule := range ipPermissions { if aws.Int64Value(rule.FromPort) != aws.Int64Value(e.FromPort) { continue } if aws.Int64Value(rule.ToPort) != aws.Int64Value(e.ToPort) { continue } if aws.StringValue(rule.IpProtocol) != matchProtocol { continue } if e.CIDR != nil { // TODO: Only if len 1? match := false for _, ipRange := range rule.IpRanges { if aws.StringValue(ipRange.CidrIp) == *e.CIDR { match = true break } } if !match { continue } } if e.SourceGroup != nil { // TODO: Only if len 1? match := false for _, spec := range rule.UserIdGroupPairs { if aws.StringValue(spec.GroupId) == *e.SourceGroup.ID { match = true break } } if !match { continue } } foundRule = rule break } if foundRule != nil { actual := &SecurityGroupRule{ Name: e.Name, SecurityGroup: &SecurityGroup{ID: e.SecurityGroup.ID}, FromPort: foundRule.FromPort, ToPort: foundRule.ToPort, Protocol: foundRule.IpProtocol, Egress: e.Egress, } if aws.StringValue(actual.Protocol) == "-1" { actual.Protocol = nil } if e.CIDR != nil { actual.CIDR = e.CIDR } if e.SourceGroup != nil { actual.SourceGroup = &SecurityGroup{ID: e.SourceGroup.ID} } return actual, nil } return nil, nil }
func (e *InstanceTemplate) mapToGCE(project string) (*compute.InstanceTemplate, error) { // TODO: This is similar to Instance... var scheduling *compute.Scheduling if fi.BoolValue(e.Preemptible) { scheduling = &compute.Scheduling{ AutomaticRestart: false, OnHostMaintenance: "TERMINATE", Preemptible: true, } } else { scheduling = &compute.Scheduling{ AutomaticRestart: true, // TODO: Migrate or terminate? OnHostMaintenance: "MIGRATE", Preemptible: false, } } glog.Infof("We should be using NVME for GCE") var disks []*compute.AttachedDisk disks = append(disks, &compute.AttachedDisk{ InitializeParams: &compute.AttachedDiskInitializeParams{ SourceImage: BuildImageURL(project, *e.BootDiskImage), DiskSizeGb: *e.BootDiskSizeGB, DiskType: *e.BootDiskType, }, Boot: true, DeviceName: "persistent-disks-0", Index: 0, AutoDelete: true, Mode: "READ_WRITE", Type: "PERSISTENT", }) var tags *compute.Tags if e.Tags != nil { tags = &compute.Tags{ Items: e.Tags, } } var networkInterfaces []*compute.NetworkInterface ni := &compute.NetworkInterface{ AccessConfigs: []*compute.AccessConfig{{ //NatIP: *e.IPAddress.Address, Type: "ONE_TO_ONE_NAT", }}, Network: e.Network.URL(project), } if e.Subnet != nil { ni.Subnetwork = *e.Subnet.Name } networkInterfaces = append(networkInterfaces, ni) var serviceAccounts []*compute.ServiceAccount if e.Scopes != nil { var scopes []string for _, s := range e.Scopes { s = expandScopeAlias(s) scopes = append(scopes, s) } serviceAccounts = append(serviceAccounts, &compute.ServiceAccount{ Email: "default", Scopes: scopes, }) } var metadataItems []*compute.MetadataItems for key, r := range e.Metadata { v, err := fi.ResourceAsString(r) if err != nil { return nil, fmt.Errorf("error rendering InstanceTemplate metadata %q: %v", key, err) } metadataItems = append(metadataItems, &compute.MetadataItems{ Key: key, Value: fi.String(v), }) } i := &compute.InstanceTemplate{ Name: *e.Name, Properties: &compute.InstanceProperties{ CanIpForward: *e.CanIPForward, Disks: disks, MachineType: *e.MachineType, Metadata: &compute.Metadata{ Items: metadataItems, }, NetworkInterfaces: networkInterfaces, Scheduling: scheduling, ServiceAccounts: serviceAccounts, Tags: tags, }, } return i, nil }
func (e *Instance) mapToGCE(project string, ipAddressResolver func(*IPAddress) (*string, error)) (*compute.Instance, error) { zone := *e.Zone var scheduling *compute.Scheduling if fi.BoolValue(e.Preemptible) { scheduling = &compute.Scheduling{ OnHostMaintenance: "TERMINATE", Preemptible: true, } } else { scheduling = &compute.Scheduling{ AutomaticRestart: true, // TODO: Migrate or terminate? OnHostMaintenance: "MIGRATE", Preemptible: false, } } var disks []*compute.AttachedDisk disks = append(disks, &compute.AttachedDisk{ InitializeParams: &compute.AttachedDiskInitializeParams{ SourceImage: BuildImageURL(project, *e.Image), }, Boot: true, DeviceName: "persistent-disks-0", Index: 0, AutoDelete: true, Mode: "READ_WRITE", Type: "PERSISTENT", }) for name, disk := range e.Disks { disks = append(disks, &compute.AttachedDisk{ Source: disk.URL(project), AutoDelete: false, Mode: "READ_WRITE", DeviceName: name, }) } var tags *compute.Tags if e.Tags != nil { tags = &compute.Tags{ Items: e.Tags, } } var networkInterfaces []*compute.NetworkInterface if e.IPAddress != nil { addr, err := ipAddressResolver(e.IPAddress) if err != nil { return nil, fmt.Errorf("unable to resolve IP for instance: %v", err) } if addr == nil { return nil, fmt.Errorf("instance IP address has not yet been created") } networkInterface := &compute.NetworkInterface{ AccessConfigs: []*compute.AccessConfig{{ NatIP: *addr, Type: "ONE_TO_ONE_NAT", }}, Network: e.Network.URL(project), } if e.Subnet != nil { networkInterface.Subnetwork = *e.Subnet.Name } networkInterfaces = append(networkInterfaces, networkInterface) } var serviceAccounts []*compute.ServiceAccount if e.Scopes != nil { var scopes []string for _, s := range e.Scopes { s = expandScopeAlias(s) scopes = append(scopes, s) } serviceAccounts = append(serviceAccounts, &compute.ServiceAccount{ Email: "default", Scopes: scopes, }) } var metadataItems []*compute.MetadataItems for key, r := range e.Metadata { v, err := fi.ResourceAsString(r) if err != nil { return nil, fmt.Errorf("error rendering Instance metadata %q: %v", key, err) } metadataItems = append(metadataItems, &compute.MetadataItems{ Key: key, Value: fi.String(v), }) } i := &compute.Instance{ CanIpForward: *e.CanIPForward, Disks: disks, MachineType: BuildMachineTypeURL(project, zone, *e.MachineType), Metadata: &compute.Metadata{ Items: metadataItems, }, Name: *e.Name, NetworkInterfaces: networkInterfaces, Scheduling: scheduling, ServiceAccounts: serviceAccounts, Tags: tags, } return i, nil }
func (_ *Service) RenderLocal(t *local.LocalTarget, a, e, changes *Service) error { serviceName := e.Name action := "" if changes.Running != nil && fi.BoolValue(e.ManageState) { if fi.BoolValue(e.Running) { action = "restart" } else { action = "stop" } } if changes.Definition != nil { servicePath := path.Join(systemdSystemPath, serviceName) err := fi.WriteFile(servicePath, fi.NewStringResource(*e.Definition), 0644, 0755) if err != nil { return fmt.Errorf("error writing systemd service file: %v", err) } glog.Infof("Reloading systemd configuration") cmd := exec.Command("systemctl", "daemon-reload") output, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("error doing systemd daemon-reload: %v\nOutput: %s", err, output) } } // "SmartRestart" - look at the obvious dependencies in the systemd service, restart if start time older if fi.BoolValue(e.ManageState) && fi.BoolValue(e.SmartRestart) { definition := fi.StringValue(e.Definition) if definition == "" && a != nil { definition = fi.StringValue(a.Definition) } if action == "" && fi.BoolValue(e.Running) && definition != "" { dependencies, err := getSystemdDependencies(serviceName, definition) if err != nil { return err } var newest time.Time for _, dependency := range dependencies { stat, err := os.Stat(dependency) if err != nil { glog.Infof("Ignoring error checking service dependency %q: %v", dependency, err) continue } modTime := stat.ModTime() if newest.IsZero() || newest.Before(modTime) { newest = modTime } } if !newest.IsZero() { properties, err := getSystemdStatus(e.Name) if err != nil { return err } startedAt := properties["ExecMainStartTimestamp"] if startedAt == "" { glog.Warningf("service was running, but did not have ExecMainStartTimestamp: %q", serviceName) } else { startedAtTime, err := time.Parse("Mon 2006-01-02 15:04:05 MST", startedAt) if err != nil { return fmt.Errorf("unable to parse service ExecMainStartTimestamp: %q", startedAt) } if startedAtTime.Before(newest) { glog.V(2).Infof("will restart service %q because dependency changed after service start", serviceName) action = "restart" } else { glog.V(2).Infof("will not restart service %q - started after dependencies", serviceName) } } } } } if action != "" && fi.BoolValue(e.ManageState) { glog.Infof("Restarting service %q", serviceName) cmd := exec.Command("systemctl", action, serviceName) output, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("error doing systemd %s %s: %v\nOutput: %s", action, serviceName, err, output) } } return nil }