func (_ *AutoscalingGroup) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *AutoscalingGroup) error { tf := &terraformAutoscalingGroup{ Name: e.Name, MinSize: e.MinSize, MaxSize: e.MaxSize, LaunchConfigurationName: e.LaunchConfiguration.TerraformLink(), } for _, s := range e.Subnets { tf.VPCZoneIdentifier = append(tf.VPCZoneIdentifier, s.TerraformLink()) } tags := e.buildTags(t.Cloud) // Make sure we output in a stable order var tagKeys []string for k := range tags { tagKeys = append(tagKeys, k) } sort.Strings(tagKeys) for _, k := range tagKeys { v := tags[k] tf.Tags = append(tf.Tags, &terraformASGTag{ Key: fi.String(k), Value: fi.String(v), PropagateAtLaunch: fi.Bool(true), }) } return t.RenderResource("aws_autoscaling_group", *e.Name, tf) }
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 (e *PersistentDisk) Find(c *fi.Context) (*PersistentDisk, error) { cloud := c.Cloud.(*gce.GCECloud) r, err := cloud.Compute.Disks.Get(cloud.Project, *e.Zone, *e.Name).Do() if err != nil { if gce.IsNotFound(err) { return nil, nil } return nil, fmt.Errorf("error listing PersistentDisks: %v", err) } actual := &PersistentDisk{} actual.Name = &r.Name actual.VolumeType = fi.String(lastComponent(r.Type)) actual.Zone = fi.String(lastComponent(r.Zone)) actual.SizeGB = &r.SizeGb return actual, nil }
func (e *ManagedInstanceGroup) Find(c *fi.Context) (*ManagedInstanceGroup, error) { cloud := c.Cloud.(*gce.GCECloud) r, err := cloud.Compute.InstanceGroupManagers.Get(cloud.Project, *e.Zone, *e.Name).Do() if err != nil { if gce.IsNotFound(err) { return nil, nil } return nil, fmt.Errorf("error listing ManagedInstanceGroups: %v", err) } actual := &ManagedInstanceGroup{} actual.Name = &r.Name actual.Zone = fi.String(lastComponent(r.Zone)) actual.BaseInstanceName = &r.BaseInstanceName actual.TargetSize = &r.TargetSize actual.InstanceTemplate = &InstanceTemplate{Name: fi.String(lastComponent(r.InstanceTemplate))} return actual, 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 (e *Package) Find(c *fi.Context) (*Package, error) { args := []string{"dpkg-query", "-f", "${db:Status-Abbrev}${Version}\\n", "-W", e.Name} human := strings.Join(args, " ") glog.V(2).Infof("Listing installed packages: %s", human) cmd := exec.Command(args[0], args[1:]...) output, err := cmd.CombinedOutput() if err != nil { if strings.Contains(string(output), "no packages found") { return nil, nil } return nil, fmt.Errorf("error listing installed packages: %v: %s", err, string(output)) } installed := false installedVersion := "" for _, line := range strings.Split(string(output), "\n") { if line == "" { continue } tokens := strings.Split(line, " ") if len(tokens) != 2 { return nil, fmt.Errorf("error parsing dpkg-query line %q", line) } state := tokens[0] version := tokens[1] switch state { case "ii": installed = true installedVersion = version case "rc": // removed installed = false case "un": // unknown installed = false default: return nil, fmt.Errorf("unknown package state %q in line %q", state, line) } } if !installed { return nil, nil } return &Package{ Name: e.Name, Version: fi.String(installedVersion), }, nil }
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 *Service) Find(c *fi.Context) (*Service, error) { servicePath := path.Join(systemdSystemPath, e.Name) d, err := ioutil.ReadFile(servicePath) if err != nil { if !os.IsNotExist(err) { return nil, fmt.Errorf("Error reading systemd file %q: %v", servicePath, err) } // Not found return &Service{ Name: e.Name, Definition: nil, Running: fi.Bool(false), }, nil } actual := &Service{ Name: e.Name, Definition: fi.String(string(d)), // Avoid spurious changes ManageState: e.ManageState, SmartRestart: e.SmartRestart, } properties, err := getSystemdStatus(e.Name) if err != nil { return nil, err } activeState := properties["ActiveState"] switch activeState { case "active": actual.Running = fi.Bool(true) case "failed", "inactive": actual.Running = fi.Bool(false) default: glog.Warningf("Unknown ActiveState=%q; will treat as not running", activeState) actual.Running = fi.Bool(false) } return actual, nil }
func findFile(p string) (*File, error) { stat, err := os.Lstat(p) if err != nil { if os.IsNotExist(err) { return nil, nil } } actual := &File{} actual.Path = p actual.Mode = fi.String(fi.FileModeToString(stat.Mode() & os.ModePerm)) uid := int(stat.Sys().(*syscall.Stat_t).Uid) owner, err := fi.LookupUserById(uid) if err != nil { return nil, err } if owner != nil { actual.Owner = fi.String(owner.Name) } else { actual.Owner = fi.String(strconv.Itoa(uid)) } gid := int(stat.Sys().(*syscall.Stat_t).Gid) group, err := fi.LookupGroupById(gid) if err != nil { return nil, err } if group != nil { actual.Group = fi.String(group.Name) } else { actual.Group = fi.String(strconv.Itoa(gid)) } if (stat.Mode() & os.ModeSymlink) != 0 { target, err := os.Readlink(p) if err != nil { return nil, fmt.Errorf("error reading symlink target: %v", err) } actual.Type = FileType_Symlink actual.Symlink = fi.String(target) } else if (stat.Mode() & os.ModeDir) != 0 { actual.Type = FileType_Directory } else { actual.Type = FileType_File actual.Contents = fi.NewFileResource(p) } return actual, 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 (e *FirewallRule) Find(c *fi.Context) (*FirewallRule, error) { cloud := c.Cloud.(*gce.GCECloud) r, err := cloud.Compute.Firewalls.Get(cloud.Project, *e.Name).Do() if err != nil { if gce.IsNotFound(err) { return nil, nil } return nil, fmt.Errorf("error listing FirewallRules: %v", err) } actual := &FirewallRule{} actual.Name = &r.Name actual.Network = &Network{Name: fi.String(lastComponent(r.Network))} actual.TargetTags = r.TargetTags actual.SourceRanges = r.SourceRanges actual.SourceTags = r.SourceTags for _, a := range r.Allowed { actual.Allowed = append(actual.Allowed, serializeFirewallAllowed(a)) } return actual, nil }
func NewService(name string, contents string, meta string) (fi.Task, error) { s := &Service{Name: name} s.Definition = fi.String(contents) if meta != "" { err := json.Unmarshal([]byte(meta), s) if err != nil { return nil, fmt.Errorf("error parsing json for service %q: %v", name, err) } } // Default some values to true: Running, SmartRestart, ManageState if s.Running == nil { s.Running = fi.Bool(true) } if s.SmartRestart == nil { s.SmartRestart = fi.Bool(true) } if s.ManageState == nil { s.ManageState = fi.Bool(true) } return s, nil }
func (e *InstanceTemplate) Find(c *fi.Context) (*InstanceTemplate, error) { cloud := c.Cloud.(*gce.GCECloud) r, err := cloud.Compute.InstanceTemplates.Get(cloud.Project, *e.Name).Do() if err != nil { if gce.IsNotFound(err) { return nil, nil } return nil, fmt.Errorf("error listing InstanceTemplates: %v", err) } actual := &InstanceTemplate{} actual.Name = &r.Name p := r.Properties for _, tag := range p.Tags.Items { actual.Tags = append(actual.Tags, tag) } actual.MachineType = fi.String(lastComponent(p.MachineType)) actual.CanIPForward = &p.CanIpForward bootDiskImage, err := ShortenImageURL(cloud.Project, p.Disks[0].InitializeParams.SourceImage) if err != nil { return nil, fmt.Errorf("error parsing source image URL: %v", err) } actual.BootDiskImage = fi.String(bootDiskImage) actual.BootDiskType = &p.Disks[0].InitializeParams.DiskType actual.BootDiskSizeGB = &p.Disks[0].InitializeParams.DiskSizeGb if p.Scheduling != nil { actual.Preemptible = &p.Scheduling.Preemptible } if len(p.NetworkInterfaces) != 0 { ni := p.NetworkInterfaces[0] actual.Network = &Network{Name: fi.String(lastComponent(ni.Network))} } for _, serviceAccount := range p.ServiceAccounts { for _, scope := range serviceAccount.Scopes { actual.Scopes = append(actual.Scopes, scopeToShortForm(scope)) } } //for i, disk := range p.Disks { // if i == 0 { // source := disk.Source // // // TODO: Parse source URL instead of assuming same project/zone? // name := lastComponent(source) // d, err := cloud.Compute.Disks.Get(cloud.Project, *e.Zone, name).Do() // if err != nil { // if gce.IsNotFound(err) { // return nil, fmt.Errorf("disk not found %q: %v", source, err) // } // return nil, fmt.Errorf("error querying for disk %q: %v", source, err) // } else { // imageURL, err := gce.ParseGoogleCloudURL(d.SourceImage) // if err != nil { // return nil, fmt.Errorf("unable to parse image URL: %q", d.SourceImage) // } // actual.Image = fi.String(imageURL.Project + "/" + imageURL.Name) // } // } //} if p.Metadata != nil { actual.Metadata = make(map[string]fi.Resource) for _, meta := range p.Metadata.Items { actual.Metadata[meta.Key] = fi.NewStringResource(*meta.Value) } } return actual, 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) Find(c *fi.Context) (*Instance, error) { cloud := c.Cloud.(*gce.GCECloud) r, err := cloud.Compute.Instances.Get(cloud.Project, *e.Zone, *e.Name).Do() if err != nil { if gce.IsNotFound(err) { return nil, nil } return nil, fmt.Errorf("error listing Instances: %v", err) } actual := &Instance{} actual.Name = &r.Name for _, tag := range r.Tags.Items { actual.Tags = append(actual.Tags, tag) } actual.Zone = fi.String(lastComponent(r.Zone)) actual.MachineType = fi.String(lastComponent(r.MachineType)) actual.CanIPForward = &r.CanIpForward if r.Scheduling != nil { actual.Preemptible = &r.Scheduling.Preemptible } if len(r.NetworkInterfaces) != 0 { ni := r.NetworkInterfaces[0] actual.Network = &Network{Name: fi.String(lastComponent(ni.Network))} if len(ni.AccessConfigs) != 0 { ac := ni.AccessConfigs[0] if ac.NatIP != "" { addr, err := cloud.Compute.Addresses.List(cloud.Project, cloud.Region).Filter("address eq " + ac.NatIP).Do() if err != nil { return nil, fmt.Errorf("error querying for address %q: %v", ac.NatIP, err) } else if len(addr.Items) != 0 { actual.IPAddress = &IPAddress{Name: &addr.Items[0].Name} } else { return nil, fmt.Errorf("address not found %q: %v", ac.NatIP, err) } } } } for _, serviceAccount := range r.ServiceAccounts { for _, scope := range serviceAccount.Scopes { actual.Scopes = append(actual.Scopes, scopeToShortForm(scope)) } } actual.Disks = make(map[string]*PersistentDisk) for i, disk := range r.Disks { if i == 0 { source := disk.Source // TODO: Parse source URL instead of assuming same project/zone? name := lastComponent(source) d, err := cloud.Compute.Disks.Get(cloud.Project, *e.Zone, name).Do() if err != nil { if gce.IsNotFound(err) { return nil, fmt.Errorf("disk not found %q: %v", source, err) } return nil, fmt.Errorf("error querying for disk %q: %v", source, err) } image, err := ShortenImageURL(cloud.Project, d.SourceImage) if err != nil { return nil, fmt.Errorf("error parsing source image URL: %v", err) } actual.Image = fi.String(image) } else { url, err := gce.ParseGoogleCloudURL(disk.Source) if err != nil { return nil, fmt.Errorf("unable to parse disk source URL: %q", disk.Source) } actual.Disks[disk.DeviceName] = &PersistentDisk{Name: &url.Name} } } if r.Metadata != nil { actual.Metadata = make(map[string]fi.Resource) for _, i := range r.Metadata.Items { if i.Value == nil { glog.Warningf("ignoring GCE instance metadata entry with nil-value: %q", i.Key) continue } actual.Metadata[i.Key] = fi.NewStringResource(*i.Value) } actual.metadataFingerprint = r.Metadata.Fingerprint } return actual, 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 }