func (s *BootstrapSuite) testToolsMetadata(c *gc.C, exploded bool) { envtesting.RemoveFakeToolsMetadata(c, s.toolsStorage) _, cmd, err := s.initBootstrapCommand(c, nil, "--model-config", s.b64yamlControllerModelConfig, "--hosted-model-config", s.b64yamlHostedModelConfig, "--instance-id", string(s.instanceId), ) c.Assert(err, jc.ErrorIsNil) err = cmd.Run(nil) c.Assert(err, jc.ErrorIsNil) // We don't write metadata at bootstrap anymore. simplestreamsMetadata, err := envtools.ReadMetadata(s.toolsStorage, "released") c.Assert(err, jc.ErrorIsNil) c.Assert(simplestreamsMetadata, gc.HasLen, 0) // The tools should have been added to tools storage, and // exploded into each of the supported series of // the same operating system if the tools were uploaded. st, err := state.Open(testing.ModelTag, &mongo.MongoInfo{ Info: mongo.Info{ Addrs: []string{gitjujutesting.MgoServer.Addr()}, CACert: testing.CACert, }, Password: testPassword, }, mongotest.DialOpts(), environs.NewStatePolicy()) c.Assert(err, jc.ErrorIsNil) defer st.Close() expectedSeries := make(set.Strings) if exploded { for _, ser := range series.SupportedSeries() { os, err := series.GetOSFromSeries(ser) c.Assert(err, jc.ErrorIsNil) hostos, err := series.GetOSFromSeries(series.HostSeries()) c.Assert(err, jc.ErrorIsNil) if os == hostos { expectedSeries.Add(ser) } } } else { expectedSeries.Add(series.HostSeries()) } storage, err := st.ToolsStorage() c.Assert(err, jc.ErrorIsNil) defer storage.Close() metadata, err := storage.AllMetadata() c.Assert(err, jc.ErrorIsNil) c.Assert(metadata, gc.HasLen, expectedSeries.Size()) for _, m := range metadata { v := version.MustParseBinary(m.Version) c.Assert(expectedSeries.Contains(v.Series), jc.IsTrue) } }
func (s *syslogSuite) SetUpTest(c *gc.C) { if runtime.GOOS != "linux" { c.Skip(fmt.Sprintf("this test requires a controller, therefore does not support %q", runtime.GOOS)) } currentSeries := series.HostSeries() osFromSeries, err := series.GetOSFromSeries(currentSeries) c.Assert(err, jc.ErrorIsNil) if osFromSeries != os.Ubuntu { c.Skip(fmt.Sprintf("this test requires a controller, therefore does not support OS %q only Ubuntu", osFromSeries.String())) } s.AgentSuite.SetUpTest(c) // TODO(perrito666) 200160701: // This needs to be done to stop the test from trying to install mongo // while running, but it is a huge footprint for such little benefit. // This test should not need JujuConnSuite or AgentSuite. s.fakeEnsureMongo = agenttest.InstallFakeEnsureMongo(s) done := make(chan struct{}) s.received = make(chan rfc5424test.Message) addr := s.createSyslogServer(c, s.received, done) // Leave log forwarding disabled initially, it will be enabled // via a model config update in the test. err = s.State.UpdateModelConfig(map[string]interface{}{ "syslog-host": addr, "syslog-ca-cert": coretesting.CACert, "syslog-client-cert": coretesting.ServerCert, "syslog-client-key": coretesting.ServerKey, }, nil, nil) c.Assert(err, jc.ErrorIsNil) s.logsCh, err = logsender.InstallBufferedLogWriter(1000) c.Assert(err, jc.ErrorIsNil) }
func versionInitSystem(ser string) (string, error) { seriesos, err := series.GetOSFromSeries(ser) if err != nil { notFound := errors.NotFoundf("init system for series %q", ser) return "", errors.Wrap(err, notFound) } switch seriesos { case os.Windows: return InitSystemWindows, nil case os.Ubuntu: switch ser { case "precise", "quantal", "raring", "saucy", "trusty", "utopic": return InitSystemUpstart, nil default: // vivid and later if featureflag.Enabled(feature.LegacyUpstart) { return InitSystemUpstart, nil } return InitSystemSystemd, nil } case os.CentOS: return InitSystemSystemd, nil } return "", errors.NotFoundf("unknown os %q (from series %q), init system", seriesos, ser) }
// validateUploadAllowed returns an error if an attempt to upload tools should // not be allowed. func validateUploadAllowed(env environs.Environ, toolsArch, toolsSeries *string, validator constraints.Validator) error { // Now check that the architecture and series for which we are setting up an // environment matches that from which we are bootstrapping. hostArch := arch.HostArch() // We can't build tools for a different architecture if one is specified. if toolsArch != nil && *toolsArch != hostArch { return fmt.Errorf("cannot use agent built for %q using a machine running on %q", *toolsArch, hostArch) } hostOS := jujuos.HostOS() if toolsSeries != nil { toolsSeriesOS, err := series.GetOSFromSeries(*toolsSeries) if err != nil { return errors.Trace(err) } if !toolsSeriesOS.EquivalentTo(hostOS) { return errors.Errorf("cannot use agent built for %q using a machine running %q", *toolsSeries, hostOS) } } // If no architecture is specified, ensure the target provider supports instances matching our architecture. if _, err := validator.Validate(constraints.Value{Arch: &hostArch}); err != nil { return errors.Errorf( "model %q of type %s does not support instances running on %q", env.Config().Name(), env.Config().Type(), hostArch, ) } return nil }
// newRole creates a gwacl.Role object (an Azure Virtual Machine) which uses // the given Virtual Hard Drive. // // roleSize is the name of one of Azure's machine types, e.g. ExtraSmall, // Large, A6 etc. func (env *azureEnviron) newRole(roleSize string, vhd *gwacl.OSVirtualHardDisk, stateServer bool, userdata, ser string, snapshot *azureEnviron) (*gwacl.Role, error) { // Do some common initialization roleName := gwacl.MakeRandomRoleName("juju") hostname := roleName password := gwacl.MakeRandomPassword() os, err := series.GetOSFromSeries(ser) if err != nil { return nil, errors.Trace(err) } // Generate a Network Configuration with the initially required ports open. networkConfigurationSet := gwacl.NewNetworkConfigurationSet(env.getInitialEndpoints(stateServer), nil) var role *gwacl.Role switch os { case jujuos.Windows: role, err = makeWindowsRole(password, roleSize, roleName, userdata, vhd, networkConfigurationSet, snapshot) default: role, err = makeLinuxRole(hostname, password, roleSize, roleName, userdata, vhd, networkConfigurationSet) } if err != nil { return nil, errors.Trace(err) } role.AvailabilitySetName = "juju" return role, nil }
// UserdataConfig is supposed to take in an instanceConfig as well as a // cloudinit.cloudConfig and add attributes in the cloudinit structure based on // the values inside instanceConfig and on the series func NewUserdataConfig(icfg *instancecfg.InstanceConfig, conf cloudinit.CloudConfig) (UserdataConfig, error) { // TODO(ericsnow) bug #1426217 // Protect icfg and conf better. operatingSystem, err := series.GetOSFromSeries(icfg.Series) if err != nil { return nil, err } base := baseConfigure{ tag: names.NewMachineTag(icfg.MachineId), icfg: icfg, conf: conf, os: operatingSystem, } switch operatingSystem { case os.Ubuntu: return &unixConfigure{base}, nil case os.CentOS: return &unixConfigure{base}, nil case os.Windows: return &windowsConfigure{base}, nil default: return nil, errors.NotSupportedf("OS %s", icfg.Series) } }
func (*CurrentSuite) TestCurrentSeries(c *gc.C) { s := series.HostSeries() if s == "unknown" { s = "n/a" } out, err := exec.Command("lsb_release", "-c").CombinedOutput() if err != nil { // If the command fails (for instance if we're running on some other // platform) then CurrentSeries should be unknown. switch runtime.GOOS { case "darwin": c.Check(s, gc.Matches, `mavericks|mountainlion|lion|snowleopard`) case "windows": c.Check(s, gc.Matches, `win2012hvr2|win2012hv|win2012|win2012r2|win8|win81|win7`) default: current_os, err := series.GetOSFromSeries(s) c.Assert(err, gc.IsNil) if s != "n/a" { // There is no lsb_release command on CentOS. if current_os == os.CentOS { c.Check(s, gc.Matches, `centos7`) } } } } else { c.Assert(string(out), gc.Equals, "Codename:\t"+s+"\n") } }
// getDisks builds the raw spec for the disks that should be attached to // the new instances and returns it. This will always include a root // disk with characteristics determined by the provides args and // constraints. func getDisks(spec *instances.InstanceSpec, cons constraints.Value, ser, eUUID string) ([]google.DiskSpec, error) { size := common.MinRootDiskSizeGiB(ser) if cons.RootDisk != nil && *cons.RootDisk > size { size = common.MiBToGiB(*cons.RootDisk) } var imageURL string os, err := series.GetOSFromSeries(ser) if err != nil { return nil, errors.Trace(err) } switch os { case jujuos.Ubuntu: imageURL = ubuntuImageBasePath case jujuos.Windows: imageURL = windowsImageBasePath default: return nil, errors.Errorf("os %s is not supported on the gce provider", os.String()) } dSpec := google.DiskSpec{ Series: ser, SizeHintGB: size, ImageURL: imageURL + spec.Image.Id, Boot: true, AutoDelete: true, Description: eUUID, } if cons.RootDisk != nil && dSpec.TooSmall() { msg := "Ignoring root-disk constraint of %dM because it is smaller than the GCE image size of %dG" logger.Infof(msg, *cons.RootDisk, google.MinDiskSizeGB(ser)) } return []google.DiskSpec{dSpec}, nil }
func getContainerInstance() (cont []ContainerInstance, err error) { current_os, err := series.GetOSFromSeries(series.HostSeries()) if err != nil { return nil, err } switch current_os { case jujuos.CentOS: cont = []ContainerInstance{ {instance.LXC, [][]string{ {"lxc"}, {"cloud-image-utils"}, }}, {instance.KVM, [][]string{ {"uvtool-libvirt"}, {"uvtool"}, }}, } default: cont = []ContainerInstance{ {instance.LXC, [][]string{ {"--target-release", "precise-updates/cloud-tools", "lxc"}, {"--target-release", "precise-updates/cloud-tools", "cloud-image-utils"}, }}, {instance.KVM, [][]string{ {"uvtool-libvirt"}, {"uvtool"}, }}, } } return cont, nil }
func (s *environBrokerSuite) TestGetDisks(c *gc.C) { for _, test := range getDisksTests { diskSpecs, err := gce.GetDisks(s.spec, s.StartInstArgs.Constraints, test.Series, "32f7d570-5bac-4b72-b169-250c24a94b2b", false) if test.error != nil { c.Assert(err, gc.Equals, err) } else { c.Assert(err, jc.ErrorIsNil) c.Assert(diskSpecs, gc.HasLen, 1) diskSpec := diskSpecs[0] os, err := series.GetOSFromSeries(test.Series) c.Assert(err, jc.ErrorIsNil) switch os { case jujuos.Ubuntu: c.Check(diskSpec.SizeHintGB, gc.Equals, uint64(8)) case jujuos.Windows: c.Check(diskSpec.SizeHintGB, gc.Equals, uint64(40)) default: c.Check(diskSpec.SizeHintGB, gc.Equals, uint64(8)) } c.Check(diskSpec.ImageURL, gc.Equals, test.basePath+s.spec.Image.Id) } } diskSpecs, err := gce.GetDisks(s.spec, s.StartInstArgs.Constraints, "trusty", "32f7d570-5bac-4b72-b169-250c24a94b2b", true) c.Assert(err, jc.ErrorIsNil) c.Assert(diskSpecs, gc.HasLen, 1) spec := diskSpecs[0] c.Assert(spec.ImageURL, gc.Equals, gce.UbuntuDailyImageBasePath+s.spec.Image.Id) }
// ComposeUserData fills out the provided cloudinit configuration structure // so it is suitable for initialising a machine with the given configuration, // and then renders it and encodes it using the supplied renderer. // When calling ComposeUserData a encoding implementation must be chosen from // the providerinit/encoders package according to the need of the provider. // // If the provided cloudcfg is nil, a new one will be created internally. func ComposeUserData(icfg *instancecfg.InstanceConfig, cloudcfg cloudinit.CloudConfig, renderer renderers.ProviderRenderer) ([]byte, error) { if cloudcfg == nil { var err error cloudcfg, err = cloudinit.New(icfg.Series) if err != nil { return nil, errors.Trace(err) } } _, err := configureCloudinit(icfg, cloudcfg) if err != nil { return nil, errors.Trace(err) } operatingSystem, err := series.GetOSFromSeries(icfg.Series) if err != nil { return nil, errors.Trace(err) } // This might get replaced by a renderer.RenderUserdata which will either // render it as YAML or Bash since some CentOS images might ship without cloudnit udata, err := cloudcfg.RenderYAML() if err != nil { return nil, errors.Trace(err) } udata, err = renderer.EncodeUserdata(udata, operatingSystem) if err != nil { return nil, errors.Trace(err) } logger.Tracef("Generated cloud init:\n%s", string(udata)) return udata, err }
// populateTools stores uploaded tools in provider storage // and updates the tools metadata. func (c *BootstrapCommand) populateTools(st *state.State, env environs.Environ) error { agentConfig := c.CurrentConfig() dataDir := agentConfig.DataDir() current := version.Binary{ Number: jujuversion.Current, Arch: arch.HostArch(), Series: series.HostSeries(), } tools, err := agenttools.ReadTools(dataDir, current) if err != nil { return errors.Trace(err) } data, err := ioutil.ReadFile(filepath.Join( agenttools.SharedToolsDir(dataDir, current), "tools.tar.gz", )) if err != nil { return errors.Trace(err) } toolstorage, err := st.ToolsStorage() if err != nil { return errors.Trace(err) } defer toolstorage.Close() var toolsVersions []version.Binary if strings.HasPrefix(tools.URL, "file://") { // Tools were uploaded: clone for each series of the same OS. os, err := series.GetOSFromSeries(tools.Version.Series) if err != nil { return errors.Trace(err) } osSeries := series.OSSupportedSeries(os) for _, series := range osSeries { toolsVersion := tools.Version toolsVersion.Series = series toolsVersions = append(toolsVersions, toolsVersion) } } else { // Tools were downloaded from an external source: don't clone. toolsVersions = []version.Binary{tools.Version} } for _, toolsVersion := range toolsVersions { metadata := binarystorage.Metadata{ Version: toolsVersion.String(), Size: tools.Size, SHA256: tools.SHA256, } logger.Debugf("Adding tools: %v", toolsVersion) if err := toolstorage.Add(bytes.NewReader(data), metadata); err != nil { return errors.Trace(err) } } return nil }
func shouldWarnJuju1x() bool { // this code only applies to Ubuntu, where we renamed Juju 1.x to juju-1. ostype, err := series.GetOSFromSeries(series.HostSeries()) if err != nil || ostype != utilsos.Ubuntu { return false } return osenv.Juju1xEnvConfigExists() && !juju2xConfigDataExists() }
func (w *unixConfigure) setDataDirPermissions() string { seriesos, _ := series.GetOSFromSeries(w.icfg.Series) var user string switch seriesos { case os.CentOS: user = "******" default: user = "******" } return fmt.Sprintf("chown %s:adm %s", user, w.icfg.LogDir) }
func (s *supportedSeriesSuite) TestGetOSFromSeries(c *gc.C) { for _, t := range getOSFromSeriesTests { got, err := series.GetOSFromSeries(t.series) if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) } else { c.Check(err, jc.ErrorIsNil) c.Assert(got, gc.Equals, t.want) } } }
// osVal will lookup the value of the key valname // in the apropriate map, based on the series. This will // help reduce boilerplate code func osVal(ser string, valname osVarType) (string, error) { os, err := series.GetOSFromSeries(ser) if err != nil { return "", err } switch os { case jujuos.Windows: return winVals[valname], nil default: return nixVals[valname], nil } }
// SeriesImage gets an instances.Image for the specified series, image stream // and location. The resulting Image's ID is in the URN format expected by // Azure Resource Manager. // // For Ubuntu, we query the SKUs to determine the most recent point release // for a series. func SeriesImage( series, stream, location string, client compute.VirtualMachineImagesClient, ) (*instances.Image, error) { seriesOS, err := jujuseries.GetOSFromSeries(series) if err != nil { return nil, errors.Trace(err) } var publisher, offering, sku string switch seriesOS { case os.Ubuntu: publisher = ubuntuPublisher offering = ubuntuOffering sku, err = ubuntuSKU(series, stream, location, client) if err != nil { return nil, errors.Annotatef(err, "selecting SKU for %s", series) } case os.Windows: publisher = windowsPublisher offering = windowsOffering switch series { case "win2012": sku = "2012-Datacenter" case "win2012r2": sku = "2012-R2-Datacenter" default: return nil, errors.NotSupportedf("deploying %s", series) } case os.CentOS: publisher = centOSPublisher offering = centOSOffering switch series { case "centos7": sku = "7.1" default: return nil, errors.NotSupportedf("deploying %s", series) } default: // TODO(axw) CentOS return nil, errors.NotSupportedf("deploying %s", seriesOS) } return &instances.Image{ Id: fmt.Sprintf("%s:%s:%s:latest", publisher, offering, sku), Arch: arch.AMD64, VirtType: "Hyper-V", }, nil }
func (w *proxyWorker) writeEnvironment() error { // TODO(dfc) this should be replaced with a switch on os.HostOS() osystem, err := series.GetOSFromSeries(series.HostSeries()) if err != nil { return err } switch osystem { case os.Windows: return w.writeEnvironmentToRegistry() default: return w.writeEnvironmentFile() } }
func (sf *statusFormatter) formatApplication(name string, application params.ApplicationStatus) applicationStatus { appOS, _ := series.GetOSFromSeries(application.Series) var ( charmOrigin = "" charmName = "" charmRev = 0 ) if curl, err := charm.ParseURL(application.Charm); err != nil { // We should never fail to parse a charm url sent back // but if we do, don't crash. logger.Errorf("failed to parse charm: %v", err) } else { switch curl.Schema { case "cs": charmOrigin = "jujucharms" case "local": charmOrigin = "local" default: charmOrigin = "unknown" } charmName = curl.Name charmRev = curl.Revision } out := applicationStatus{ Err: application.Err, Charm: application.Charm, Series: application.Series, OS: strings.ToLower(appOS.String()), CharmOrigin: charmOrigin, CharmName: charmName, CharmRev: charmRev, Exposed: application.Exposed, Life: application.Life, Relations: application.Relations, CanUpgradeTo: application.CanUpgradeTo, SubordinateTo: application.SubordinateTo, Units: make(map[string]unitStatus), StatusInfo: sf.getServiceStatusInfo(application), Version: application.WorkloadVersion, } for k, m := range application.Units { out.Units[k] = sf.formatUnit(unitFormatInfo{ unit: m, unitName: k, applicationName: name, meterStatuses: application.MeterStatuses, }) } return out }
func newOSProfile( vmName string, instanceConfig *instancecfg.InstanceConfig, randomAdminPassword func() string, ) (*compute.OSProfile, os.OSType, error) { logger.Debugf("creating OS profile for %q", vmName) customData, err := providerinit.ComposeUserData(instanceConfig, nil, AzureRenderer{}) if err != nil { return nil, os.Unknown, errors.Annotate(err, "composing user data") } osProfile := &compute.OSProfile{ ComputerName: to.StringPtr(vmName), CustomData: to.StringPtr(string(customData)), } seriesOS, err := jujuseries.GetOSFromSeries(instanceConfig.Series) if err != nil { return nil, os.Unknown, errors.Trace(err) } switch seriesOS { case os.Ubuntu, os.CentOS: // SSH keys are handled by custom data, but must also be // specified in order to forego providing a password, and // disable password authentication. publicKeys := []compute.SSHPublicKey{{ Path: to.StringPtr("/home/ubuntu/.ssh/authorized_keys"), KeyData: to.StringPtr(instanceConfig.AuthorizedKeys), }} osProfile.AdminUsername = to.StringPtr("ubuntu") osProfile.LinuxConfiguration = &compute.LinuxConfiguration{ DisablePasswordAuthentication: to.BoolPtr(true), SSH: &compute.SSHConfiguration{PublicKeys: &publicKeys}, } case os.Windows: osProfile.AdminUsername = to.StringPtr("JujuAdministrator") // A password is required by Azure, but we will never use it. // We generate something sufficiently long and random that it // should be infeasible to guess. osProfile.AdminPassword = to.StringPtr(randomAdminPassword()) osProfile.WindowsConfiguration = &compute.WindowsConfiguration{ ProvisionVMAgent: to.BoolPtr(true), EnableAutomaticUpdates: to.BoolPtr(true), // TODO(?) add WinRM configuration here. } default: return nil, os.Unknown, errors.NotSupportedf("%s", seriesOS) } return osProfile, seriesOS, nil }
// newRawInstance is where the new physical instance is actually // provisioned, relative to the provided args and spec. Info for that // low-level instance is returned. func (env *environ) newRawInstance(args environs.StartInstanceParams, spec *instances.InstanceSpec) (*google.Instance, error) { machineID := common.MachineFullName(env, args.InstanceConfig.MachineId) os, err := series.GetOSFromSeries(args.InstanceConfig.Series) if err != nil { return nil, errors.Trace(err) } metadata, err := getMetadata(args, os) if err != nil { return nil, errors.Trace(err) } tags := []string{ env.globalFirewallName(), machineID, } cfg := env.Config() eUUID, ok := cfg.UUID() if !ok { return nil, errors.NotFoundf("UUID necessary to create the instance disk") } disks, err := getDisks(spec, args.Constraints, args.InstanceConfig.Series, eUUID) if err != nil { return nil, errors.Trace(err) } // TODO(ericsnow) Use the env ID for the network name (instead of default)? // TODO(ericsnow) Make the network name configurable? // TODO(ericsnow) Support multiple networks? // TODO(ericsnow) Use a different net interface name? Configurable? instSpec := google.InstanceSpec{ ID: machineID, Type: spec.InstanceType.Name, Disks: disks, NetworkInterfaces: []string{"ExternalNAT"}, Metadata: metadata, Tags: tags, // Network is omitted (left empty). } zones, err := env.parseAvailabilityZones(args) if err != nil { return nil, errors.Trace(err) } inst, err := env.gce.AddInstance(instSpec, zones...) return inst, errors.Trace(err) }
// MinDiskSizeGB is the minimum/default size (in megabytes) for // GCE disks. // // Note: GCE does not currently have an official minimum disk size. // However, in testing we found the minimum size to be 10 GB for ubuntu // and 50 GB for windows due to the image size. See gceapi message. // // gceapi: Requested disk size cannot be smaller than the image size (10 GB) func MinDiskSizeGB(ser string) uint64 { // See comment below that explains why we're ignoring the error os, _ := series.GetOSFromSeries(ser) switch os { case jujuos.Ubuntu: return 10 case jujuos.Windows: return 50 // On default we just return a "sane" default since the error // will be propagated through the api and appear in juju status anyway default: return 10 } }
// MinRootDiskSizeGiB is the minimum size for the root disk of an // instance, in Gigabytes. This value accommodates the anticipated // size of the initial image, any updates, and future application // data. func MinRootDiskSizeGiB(ser string) uint64 { // See comment below that explains why we're ignoring the error os, _ := series.GetOSFromSeries(ser) switch os { case jujuos.Ubuntu, jujuos.CentOS: return 8 case jujuos.Windows: return 40 // By default we just return a "sane" default, since the error will just // be returned by the api and seen in juju status default: return 8 } }
// newRawInstance is where the new physical instance is actually // provisioned, relative to the provided args and spec. Info for that // low-level instance is returned. func (env *environ) newRawInstance(args environs.StartInstanceParams, spec *instances.InstanceSpec) (*google.Instance, error) { hostname, err := env.namespace.Hostname(args.InstanceConfig.MachineId) if err != nil { return nil, errors.Trace(err) } os, err := series.GetOSFromSeries(args.InstanceConfig.Series) if err != nil { return nil, errors.Trace(err) } metadata, err := getMetadata(args, os) if err != nil { return nil, errors.Trace(err) } tags := []string{ env.globalFirewallName(), hostname, } disks, err := getDisks( spec, args.Constraints, args.InstanceConfig.Series, env.Config().UUID(), env.Config().ImageStream() == "daily", ) if err != nil { return nil, errors.Trace(err) } // TODO(ericsnow) Use the env ID for the network name (instead of default)? // TODO(ericsnow) Make the network name configurable? // TODO(ericsnow) Support multiple networks? // TODO(ericsnow) Use a different net interface name? Configurable? instSpec := google.InstanceSpec{ ID: hostname, Type: spec.InstanceType.Name, Disks: disks, NetworkInterfaces: []string{"ExternalNAT"}, Metadata: metadata, Tags: tags, // Network is omitted (left empty). } zones, err := env.parseAvailabilityZones(args) if err != nil { return nil, errors.Trace(err) } inst, err := env.gce.AddInstance(instSpec, zones...) return inst, errors.Trace(err) }
// uploadTools compiles jujud from $GOPATH and uploads it into the supplied // storage. If no version has been explicitly chosen, the version number // reported by the built tools will be based on the client version number. // In any case, the version number reported will have a build component higher // than that of any otherwise-matching available envtools. // uploadTools resets the chosen version and replaces the available tools // with the ones just uploaded. func (context *upgradeContext) uploadTools(buildAgent bool) (err error) { // TODO(fwereade): this is kinda crack: we should not assume that // jujuversion.Current matches whatever source happens to be built. The // ideal would be: // 1) compile jujud from $GOPATH into some build dir // 2) get actual version with `jujud version` // 3) check actual version for compatibility with CLI tools // 4) generate unique build version with reference to available tools // 5) force-version that unique version into the dir directly // 6) archive and upload the build dir // ...but there's no way we have time for that now. In the meantime, // considering the use cases, this should work well enough; but it // won't detect an incompatible major-version change, which is a shame. // // TODO(cherylj) If the determination of version changes, we will // need to also change the upgrade version checks in Run() that check // if a major upgrade is allowed. if context.chosen == version.Zero { context.chosen = context.client } context.chosen = uploadVersion(context.chosen, context.tools) builtTools, err := sync.BuildAgentTarball(buildAgent, &context.chosen, "upgrade") if err != nil { return errors.Trace(err) } defer os.RemoveAll(builtTools.Dir) uploadToolsVersion := builtTools.Version uploadToolsVersion.Number = context.chosen toolsPath := path.Join(builtTools.Dir, builtTools.StorageName) logger.Infof("uploading agent binary %v (%dkB) to Juju controller", uploadToolsVersion, (builtTools.Size+512)/1024) f, err := os.Open(toolsPath) if err != nil { return errors.Trace(err) } defer f.Close() os, err := series.GetOSFromSeries(builtTools.Version.Series) if err != nil { return errors.Trace(err) } additionalSeries := series.OSSupportedSeries(os) uploaded, err := context.apiClient.UploadTools(f, uploadToolsVersion, additionalSeries...) if err != nil { return errors.Trace(err) } context.tools = uploaded return nil }
// locallyBuildableTools returns the list of tools that // can be built locally, for series of the same OS. func locallyBuildableTools() (buildable coretools.List) { for _, ser := range series.SupportedSeries() { if os, err := series.GetOSFromSeries(ser); err != nil || os != jujuos.HostOS() { continue } binary := version.Binary{ Number: version.Current.Number, Series: ser, Arch: arch.HostArch(), } // Increment the build number so we know it's a development build. binary.Build++ buildable = append(buildable, &coretools.Tools{Version: binary}) } return buildable }
// ParseBinary parses a binary version of the form "1.2.3-series-arch". func ParseBinary(s string) (Binary, error) { m := binaryPat.FindStringSubmatch(s) if m == nil { return Binary{}, fmt.Errorf("invalid binary version %q", s) } var v Binary v.Major = atoi(m[1]) v.Minor = atoi(m[2]) v.Tag = m[4] v.Patch = atoi(m[5]) if m[6] != "" { v.Build = atoi(m[6][1:]) } v.Series = m[7] v.Arch = m[8] _, err := series.GetOSFromSeries(v.Series) return v, err }
// locallyBuildableTools returns the list of tools that // can be built locally, for series of the same OS. func locallyBuildableTools(toolsSeries *string) (buildable coretools.List, _ version.Number) { buildNumber := jujuversion.Current // Increment the build number so we know it's a custom build. buildNumber.Build++ for _, ser := range series.SupportedSeries() { if os, err := series.GetOSFromSeries(ser); err != nil || !os.EquivalentTo(jujuos.HostOS()) { continue } if toolsSeries != nil && ser != *toolsSeries { continue } binary := version.Binary{ Number: buildNumber, Series: ser, Arch: arch.HostArch(), } buildable = append(buildable, &coretools.Tools{Version: binary}) } return buildable, buildNumber }
// New returns a new Config with no options set. func New(ser string) (CloudConfig, error) { seriesos, err := series.GetOSFromSeries(ser) if err != nil { return nil, err } switch seriesos { case os.Windows: renderer, _ := shell.NewRenderer("powershell") return &windowsCloudConfig{ &cloudConfig{ series: ser, renderer: renderer, attrs: make(map[string]interface{}), }, }, nil case os.Ubuntu: renderer, _ := shell.NewRenderer("bash") return &ubuntuCloudConfig{ &cloudConfig{ series: ser, paccmder: commands.NewAptPackageCommander(), pacconfer: config.NewAptPackagingConfigurer(ser), renderer: renderer, attrs: make(map[string]interface{}), }, }, nil case os.CentOS: renderer, _ := shell.NewRenderer("bash") return ¢OSCloudConfig{ &cloudConfig{ series: ser, paccmder: commands.NewYumPackageCommander(), pacconfer: config.NewYumPackagingConfigurer(ser), renderer: renderer, attrs: make(map[string]interface{}), }, }, nil default: return nil, errors.NotFoundf("cloudconfig for series %q", ser) } }
// newOSDisk creates a gwacl.OSVirtualHardDisk object suitable for an // Azure Virtual Machine. func (env *azureEnviron) newOSDisk(sourceImageName string, ser string) (*gwacl.OSVirtualHardDisk, error) { vhdName := gwacl.MakeRandomDiskName("juju") vhdPath := fmt.Sprintf("vhds/%s", vhdName) snap := env.getSnapshot() storageAccount := snap.ecfg.storageAccountName() mediaLink := gwacl.CreateVirtualHardDiskMediaLink(storageAccount, vhdPath) os, err := series.GetOSFromSeries(ser) if err != nil { return nil, errors.Trace(err) } var OSType string switch os { case jujuos.Windows: OSType = "Windows" default: OSType = "Linux" } // The disk label is optional and the disk name can be omitted if // mediaLink is provided. return gwacl.NewOSVirtualHardDisk("", "", "", mediaLink, sourceImageName, OSType), nil }