// prepareContainerAccessEnvironment retrieves the environment, host machine, and access // for working with containers. func (p *ProvisionerAPI) prepareContainerAccessEnvironment() (environs.NetworkingEnviron, *state.Machine, common.AuthFunc, error) { cfg, err := p.st.EnvironConfig() if err != nil { return nil, nil, nil, errors.Annotate(err, "failed to get environment config") } environ, err := environs.New(cfg) if err != nil { return nil, nil, nil, errors.Annotate(err, "failed to construct an environment from config") } netEnviron, supported := environs.SupportsNetworking(environ) if !supported { // " not supported" will be appended to the message below. return nil, nil, nil, errors.NotSupportedf("environment %q networking", cfg.Name()) } canAccess, err := p.getAuthFunc() if err != nil { return nil, nil, nil, errors.Annotate(err, "cannot authenticate request") } hostAuthTag := p.authorizer.GetAuthTag() if hostAuthTag == nil { return nil, nil, nil, errors.Errorf("authenticated entity tag is nil") } hostTag, err := names.ParseMachineTag(hostAuthTag.String()) if err != nil { return nil, nil, nil, errors.Trace(err) } host, err := p.getMachine(canAccess, hostTag) if err != nil { return nil, nil, nil, errors.Trace(err) } return netEnviron, host, canAccess, nil }
func (s *suite) bootstrapTestEnviron(c *gc.C) environs.NetworkingEnviron { env, err := bootstrap.Prepare( envtesting.BootstrapContext(c), s.ControllerStore, bootstrap.PrepareParams{ ControllerConfig: testing.FakeControllerConfig(), ModelConfig: s.TestConfig, ControllerName: s.TestConfig["name"].(string), Cloud: dummy.SampleCloudSpec(), AdminSecret: AdminSecret, }, ) c.Assert(err, gc.IsNil, gc.Commentf("preparing environ %#v", s.TestConfig)) c.Assert(env, gc.NotNil) netenv, supported := environs.SupportsNetworking(env) c.Assert(supported, jc.IsTrue) err = bootstrap.Bootstrap(envtesting.BootstrapContext(c), netenv, bootstrap.BootstrapParams{ ControllerConfig: testing.FakeControllerConfig(), CloudName: "dummy", Cloud: cloud.Cloud{ Type: "dummy", AuthTypes: []cloud.AuthType{cloud.EmptyAuthType}, }, AdminSecret: AdminSecret, CAPrivateKey: testing.CAKey, }) c.Assert(err, jc.ErrorIsNil) return netenv }
func (*environSuite) TestSupportsNetworking(c *gc.C) { testConfig := minimalConfig(c) environ, err := local.Provider.Open(testConfig) c.Assert(err, jc.ErrorIsNil) _, ok := environs.SupportsNetworking(environ) c.Assert(ok, jc.IsFalse) }
// ContainerManagerConfig returns information from the environment config that is // needed for configuring the container manager. func (p *ProvisionerAPI) ContainerManagerConfig(args params.ContainerManagerConfigParams) (params.ContainerManagerConfig, error) { var result params.ContainerManagerConfig config, err := p.st.EnvironConfig() if err != nil { return result, err } cfg := make(map[string]string) cfg[container.ConfigName] = container.DefaultNamespace switch args.Type { case instance.LXC: if useLxcClone, ok := config.LXCUseClone(); ok { cfg["use-clone"] = fmt.Sprint(useLxcClone) } if useLxcCloneAufs, ok := config.LXCUseCloneAUFS(); ok { cfg["use-aufs"] = fmt.Sprint(useLxcCloneAufs) } if lxcDefaultMTU, ok := config.LXCDefaultMTU(); ok { logger.Debugf("using default MTU %v for all LXC containers NICs", lxcDefaultMTU) cfg[container.ConfigLXCDefaultMTU] = fmt.Sprintf("%d", lxcDefaultMTU) } } if !environs.AddressAllocationEnabled() { // No need to even try checking the environ for support. logger.Debugf("address allocation feature flag not enabled") result.ManagerConfig = cfg return result, nil } // Create an environment to verify networking support. env, err := environs.New(config) if err != nil { return result, err } if netEnv, ok := environs.SupportsNetworking(env); ok { // Passing network.AnySubnet below should be interpreted by // the provider as "does ANY subnet support this". supported, err := netEnv.SupportsAddressAllocation(network.AnySubnet) if err == nil && supported { cfg[container.ConfigIPForwarding] = "true" } else if err != nil { // We log the error, but it's safe to ignore as it's not // critical. logger.Debugf("address allocation not supported (%v)", err) } // AWS requires NAT in place in order for hosted containers to // reach outside. if config.Type() == provider.EC2 { cfg[container.ConfigEnableNAT] = "true" } } result.ManagerConfig = cfg return result, nil }
// NewWorker returns a machine undertaker worker that will watch for // machines that need to be removed and remove them, cleaning up any // necessary provider-level resources first. func NewWorker(api Facade, env environs.Environ) (worker.Worker, error) { envNetworking, _ := environs.SupportsNetworking(env) w, err := watcher.NewNotifyWorker(watcher.NotifyConfig{ Handler: &Undertaker{API: api, Releaser: envNetworking}, }) if err != nil { return nil, errors.Trace(err) } return w, nil }
// networkingEnviron returns a environs.NetworkingEnviron instance from the // current model config, if supported. If the model does not support // environs.Networking, an error satisfying errors.IsNotSupported() will be // returned. func networkingEnviron(getter environs.EnvironConfigGetter) (environs.NetworkingEnviron, error) { env, err := environs.GetEnviron(getter, environs.New) if err != nil { return nil, errors.Annotate(err, "opening environment") } if netEnv, ok := environs.SupportsNetworking(env); ok { return netEnv, nil } return nil, errors.NotSupportedf("model networking features") // " not supported" }
// NewWorker returns a worker that keeps track of // IP address lifecycles, releaseing and removing Dead addresses. func NewWorker(st stateAddresser) (worker.Worker, error) { config, err := st.EnvironConfig() if err != nil { return nil, errors.Trace(err) } environ, err := environs.New(config) if err != nil { return nil, errors.Trace(err) } // If netEnviron is nil the worker will start but won't do anything as // no IP addresses will be created or destroyed. netEnviron, _ := environs.SupportsNetworking(environ) a := newWorkerWithReleaser(st, netEnviron) return a, nil }
// networkingEnviron returns a environs.NetworkingEnviron instance from the // current model config, if supported. If the model does not support // environs.Networking, an error satisfying errors.IsNotSupported() will be // returned. func networkingEnviron(api NetworkBacking) (environs.NetworkingEnviron, error) { envConfig, err := api.ModelConfig() if err != nil { return nil, errors.Annotate(err, "getting model config") } env, err := environs.New(envConfig) if err != nil { return nil, errors.Annotate(err, "opening model") } if netEnv, ok := environs.SupportsNetworking(env); ok { return netEnv, nil } return nil, errors.NotSupportedf("model networking features") // " not supported" }
// networkingEnviron returns a environs.NetworkingEnviron instance // from the current environment config, if supported. If the // environment does not support environs.Networking, an error // satisfying errors.IsNotSupported() will be returned. func (api *subnetsAPI) networkingEnviron() (environs.NetworkingEnviron, error) { envConfig, err := api.backing.EnvironConfig() if err != nil { return nil, errors.Annotate(err, "getting environment config") } env, err := environs.New(envConfig) if err != nil { return nil, errors.Annotate(err, "opening environment") } if netEnv, ok := environs.SupportsNetworking(env); ok { return netEnv, nil } return nil, errors.NotSupportedf("environment networking features") // " not supported" }
func (p *ProvisionerAPI) maybeGetNetworkingEnviron() (environs.NetworkingEnviron, error) { cfg, err := p.st.ModelConfig() if err != nil { return nil, errors.Annotate(err, "failed to get model config") } environ, err := environs.New(cfg) if err != nil { return nil, errors.Annotate(err, "failed to construct a model from config") } netEnviron, supported := environs.SupportsNetworking(environ) if !supported { // " not supported" will be appended to the message below. return nil, errors.NotSupportedf("model %q networking", cfg.Name()) } return netEnviron, nil }
func (s *suite) bootstrapTestEnviron(c *gc.C, preferIPv6 bool) environs.NetworkingEnviron { s.TestConfig["prefer-ipv6"] = preferIPv6 cfg, err := config.New(config.NoDefaults, s.TestConfig) c.Assert(err, jc.ErrorIsNil) env, err := environs.Prepare(cfg, envtesting.BootstrapContext(c), s.ConfigStore) c.Assert(err, gc.IsNil, gc.Commentf("preparing environ %#v", s.TestConfig)) c.Assert(env, gc.NotNil) netenv, supported := environs.SupportsNetworking(env) c.Assert(supported, jc.IsTrue) err = bootstrap.EnsureNotBootstrapped(netenv) c.Assert(err, jc.ErrorIsNil) err = bootstrap.Bootstrap(envtesting.BootstrapContext(c), netenv, bootstrap.BootstrapParams{}) c.Assert(err, jc.ErrorIsNil) return netenv }
// supportsSpaces checks if the environment implements NetworkingEnviron // and also if it supports spaces. func (api *spacesAPI) supportsSpaces() error { config, err := api.backing.EnvironConfig() if err != nil { return errors.Annotate(err, "getting environment config") } env, err := environs.New(config) if err != nil { return errors.Annotate(err, "validating environment config") } netEnv, ok := environs.SupportsNetworking(env) if !ok { return errors.NotSupportedf("networking") } ok, err = netEnv.SupportsSpaces() if err != nil { logger.Warningf("environment does not support spaces: %v", err) } return err }
// getNetworkingEnviron checks if the environment implements NetworkingEnviron // and also if it supports IP address allocation. func (api *AddresserAPI) getNetworkingEnviron() (environs.NetworkingEnviron, bool, error) { config, err := api.st.EnvironConfig() if err != nil { return nil, false, errors.Annotate(err, "getting environment config") } env, err := environs.New(config) if err != nil { return nil, false, errors.Annotate(err, "validating environment config") } netEnv, ok := environs.SupportsNetworking(env) if !ok { return nil, false, nil } ok, err = netEnv.SupportsAddressAllocation(network.AnySubnet) if err != nil && !errors.IsNotSupported(err) { return nil, false, errors.Annotate(err, "checking allocation support") } return netEnv, ok, nil }
// NetworkingEnvironFromModelConfig constructs and returns // environs.NetworkingEnviron using the given configGetter. Returns an error // satisfying errors.IsNotSupported() if the model config does not support // networking features. func NetworkingEnvironFromModelConfig(configGetter ModelConfigGetter) (environs.NetworkingEnviron, error) { modelConfig, err := configGetter.ModelConfig() if err != nil { return nil, errors.Annotate(err, "failed to get model config") } if modelConfig.Type() == "dummy" { return nil, errors.NotSupportedf("dummy provider network config") } model, err := environs.New(modelConfig) if err != nil { return nil, errors.Annotate(err, "failed to construct a model from config") } netEnviron, supported := environs.SupportsNetworking(model) if !supported { // " not supported" will be appended to the message below. return nil, errors.NotSupportedf("model %q networking", modelConfig.Name()) } return netEnviron, nil }
func (s *suite) bootstrapTestEnviron(c *gc.C, preferIPv6 bool) environs.NetworkingEnviron { s.TestConfig["prefer-ipv6"] = preferIPv6 env, err := environs.Prepare( envtesting.BootstrapContext(c), s.ControllerStore, environs.PrepareParams{ BaseConfig: s.TestConfig, ControllerName: s.TestConfig["name"].(string), CloudName: "dummy", }, ) c.Assert(err, gc.IsNil, gc.Commentf("preparing environ %#v", s.TestConfig)) c.Assert(env, gc.NotNil) netenv, supported := environs.SupportsNetworking(env) c.Assert(supported, jc.IsTrue) err = bootstrap.Bootstrap(envtesting.BootstrapContext(c), netenv, bootstrap.BootstrapParams{}) c.Assert(err, jc.ErrorIsNil) return netenv }
func (dw *discoverspacesWorker) loop() (err error) { ensureClosed := func() { select { case <-dw.discoveringSpaces: // Already closed. return default: close(dw.discoveringSpaces) } } defer ensureClosed() modelCfg, err := dw.api.ModelConfig() if err != nil { return err } model, err := environs.New(modelCfg) if err != nil { return err } networkingModel, ok := environs.SupportsNetworking(model) if ok { err = dw.handleSubnets(networkingModel) if err != nil { return errors.Trace(err) } } close(dw.discoveringSpaces) // TODO(mfoord): we'll have a watcher here checking if we need to // update the spaces/subnets definition. dying := dw.tomb.Dying() for { select { case <-dying: return nil } } return nil }
// SupportsSpaces checks if the environment implements NetworkingEnviron // and also if it supports spaces. func SupportsSpaces(backing NetworkBacking) error { config, err := backing.ModelConfig() if err != nil { return errors.Annotate(err, "getting model config") } env, err := environs.New(config) if err != nil { return errors.Annotate(err, "validating model config") } netEnv, ok := environs.SupportsNetworking(env) if !ok { return errors.NotSupportedf("networking") } ok, err = netEnv.SupportsSpaces() if !ok { if err != nil && !errors.IsNotSupported(err) { logger.Warningf("checking model spaces support failed with: %v", err) } return errors.NotSupportedf("spaces") } return nil }
// Bootstrap bootstraps the given environment. The supplied constraints are // used to provision the instance, and are also set within the bootstrapped // environment. func Bootstrap(ctx environs.BootstrapContext, environ environs.Environ, args BootstrapParams) error { cfg := environ.Config() network.InitializeFromConfig(cfg) if secret := cfg.AdminSecret(); secret == "" { return errors.Errorf("environment configuration has no admin-secret") } if authKeys := ssh.SplitAuthorisedKeys(cfg.AuthorizedKeys()); len(authKeys) == 0 { // Apparently this can never happen, so it's not tested. But, one day, // Config will act differently (it's pretty crazy that, AFAICT, the // authorized-keys are optional config settings... but it's impossible // to actually *create* a config without them)... and when it does, // we'll be here to catch this problem early. return errors.Errorf("environment configuration has no authorized-keys") } if _, hasCACert := cfg.CACert(); !hasCACert { return errors.Errorf("environment configuration has no ca-cert") } if _, hasCAKey := cfg.CAPrivateKey(); !hasCAKey { return errors.Errorf("environment configuration has no ca-private-key") } // Set default tools metadata source, add image metadata source, // then verify constraints. Providers may rely on image metadata // for constraint validation. var imageMetadata []*imagemetadata.ImageMetadata if args.MetadataDir != "" { var err error imageMetadata, err = setPrivateMetadataSources(environ, args.MetadataDir) if err != nil { return err } } if err := validateConstraints(environ, args.Constraints); err != nil { return err } _, supportsNetworking := environs.SupportsNetworking(environ) ctx.Infof("Bootstrapping environment %q", cfg.Name()) logger.Debugf("environment %q supports service/machine networks: %v", cfg.Name(), supportsNetworking) disableNetworkManagement, _ := cfg.DisableNetworkManagement() logger.Debugf("network management by juju enabled: %v", !disableNetworkManagement) availableTools, err := findAvailableTools(environ, args.AgentVersion, args.Constraints.Arch, args.UploadTools) if errors.IsNotFound(err) { return errors.New(noToolsMessage) } else if err != nil { return err } if lxcMTU, ok := cfg.LXCDefaultMTU(); ok { logger.Debugf("using MTU %v for all created LXC containers' network interfaces", lxcMTU) } // If we're uploading, we must override agent-version; // if we're not uploading, we want to ensure we have an // agent-version set anyway, to appease FinishInstanceConfig. // In the latter case, setBootstrapTools will later set // agent-version to the correct thing. agentVersion := version.Current if args.AgentVersion != nil { agentVersion = *args.AgentVersion } if cfg, err = cfg.Apply(map[string]interface{}{ "agent-version": agentVersion.String(), }); err != nil { return err } if err = environ.SetConfig(cfg); err != nil { return err } ctx.Infof("Starting new instance for initial state server") arch, series, finalizer, err := environ.Bootstrap(ctx, environs.BootstrapParams{ Constraints: args.Constraints, Placement: args.Placement, AvailableTools: availableTools, }) if err != nil { return err } matchingTools, err := availableTools.Match(coretools.Filter{ Arch: arch, Series: series, }) if err != nil { return err } selectedTools, err := setBootstrapTools(environ, matchingTools) if err != nil { return err } if selectedTools.URL == "" { if !args.UploadTools { logger.Warningf("no prepackaged tools available") } ctx.Infof("Building tools to upload (%s)", selectedTools.Version) builtTools, err := sync.BuildToolsTarball(&selectedTools.Version.Number, cfg.AgentStream()) if err != nil { return errors.Annotate(err, "cannot upload bootstrap tools") } defer os.RemoveAll(builtTools.Dir) filename := filepath.Join(builtTools.Dir, builtTools.StorageName) selectedTools.URL = fmt.Sprintf("file://%s", filename) selectedTools.Size = builtTools.Size selectedTools.SHA256 = builtTools.Sha256Hash } ctx.Infof("Installing Juju agent on bootstrap instance") instanceConfig, err := instancecfg.NewBootstrapInstanceConfig(args.Constraints, series) if err != nil { return err } instanceConfig.Tools = selectedTools instanceConfig.CustomImageMetadata = imageMetadata if err := finalizer(ctx, instanceConfig); err != nil { return err } ctx.Infof("Bootstrap agent installed") return nil }
// Bootstrap bootstraps the given environment. The supplied constraints are // used to provision the instance, and are also set within the bootstrapped // environment. func Bootstrap(ctx environs.BootstrapContext, environ environs.Environ, args BootstrapParams) error { cfg := environ.Config() network.SetPreferIPv6(cfg.PreferIPv6()) if secret := cfg.AdminSecret(); secret == "" { return errors.Errorf("model configuration has no admin-secret") } if authKeys := ssh.SplitAuthorisedKeys(cfg.AuthorizedKeys()); len(authKeys) == 0 { // Apparently this can never happen, so it's not tested. But, one day, // Config will act differently (it's pretty crazy that, AFAICT, the // authorized-keys are optional config settings... but it's impossible // to actually *create* a config without them)... and when it does, // we'll be here to catch this problem early. return errors.Errorf("model configuration has no authorized-keys") } if _, hasCACert := cfg.CACert(); !hasCACert { return errors.Errorf("model configuration has no ca-cert") } if _, hasCAKey := cfg.CAPrivateKey(); !hasCAKey { return errors.Errorf("model configuration has no ca-private-key") } // Set default tools metadata source, add image metadata source, // then verify constraints. Providers may rely on image metadata // for constraint validation. var customImageMetadata []*imagemetadata.ImageMetadata if args.MetadataDir != "" { var err error customImageMetadata, err = setPrivateMetadataSources(environ, args.MetadataDir) if err != nil { return err } } if err := validateConstraints(environ, args.ModelConstraints); err != nil { return err } if err := validateConstraints(environ, args.BootstrapConstraints); err != nil { return err } constraintsValidator, err := environ.ConstraintsValidator() if err != nil { return err } bootstrapConstraints, err := constraintsValidator.Merge( args.ModelConstraints, args.BootstrapConstraints, ) if err != nil { return err } _, supportsNetworking := environs.SupportsNetworking(environ) var bootstrapSeries *string if args.BootstrapSeries != "" { bootstrapSeries = &args.BootstrapSeries } ctx.Infof("Bootstrapping model %q", cfg.Name()) logger.Debugf("model %q supports service/machine networks: %v", cfg.Name(), supportsNetworking) disableNetworkManagement, _ := cfg.DisableNetworkManagement() logger.Debugf("network management by juju enabled: %v", !disableNetworkManagement) availableTools, err := findAvailableTools( environ, args.AgentVersion, bootstrapConstraints.Arch, bootstrapSeries, args.UploadTools, args.BuildToolsTarball != nil, ) if errors.IsNotFound(err) { return errors.New(noToolsMessage) } else if err != nil { return err } if lxcMTU, ok := cfg.LXCDefaultMTU(); ok { logger.Debugf("using MTU %v for all created LXC containers' network interfaces", lxcMTU) } imageMetadata, err := bootstrapImageMetadata( environ, availableTools, args.BootstrapImage, &customImageMetadata, ) if err != nil { return errors.Trace(err) } // If we're uploading, we must override agent-version; // if we're not uploading, we want to ensure we have an // agent-version set anyway, to appease FinishInstanceConfig. // In the latter case, setBootstrapTools will later set // agent-version to the correct thing. agentVersion := jujuversion.Current if args.AgentVersion != nil { agentVersion = *args.AgentVersion } if cfg, err = cfg.Apply(map[string]interface{}{ "agent-version": agentVersion.String(), }); err != nil { return err } if err = environ.SetConfig(cfg); err != nil { return err } ctx.Infof("Starting new instance for initial controller") result, err := environ.Bootstrap(ctx, environs.BootstrapParams{ ModelConstraints: args.ModelConstraints, BootstrapConstraints: args.BootstrapConstraints, BootstrapSeries: args.BootstrapSeries, Placement: args.Placement, AvailableTools: availableTools, ImageMetadata: imageMetadata, }) if err != nil { return err } matchingTools, err := availableTools.Match(coretools.Filter{ Arch: result.Arch, Series: result.Series, }) if err != nil { return err } selectedToolsList, err := setBootstrapTools(environ, matchingTools) if err != nil { return err } havePrepackaged := false for i, selectedTools := range selectedToolsList { if selectedTools.URL != "" { havePrepackaged = true continue } ctx.Infof("Building tools to upload (%s)", selectedTools.Version) builtTools, err := args.BuildToolsTarball(&selectedTools.Version.Number, cfg.AgentStream()) if err != nil { return errors.Annotate(err, "cannot upload bootstrap tools") } defer os.RemoveAll(builtTools.Dir) filename := filepath.Join(builtTools.Dir, builtTools.StorageName) selectedTools.URL = fmt.Sprintf("file://%s", filename) selectedTools.Size = builtTools.Size selectedTools.SHA256 = builtTools.Sha256Hash selectedToolsList[i] = selectedTools } if !havePrepackaged && !args.UploadTools { // There are no prepackaged agents, so we must upload // even though the user didn't ask for it. We only do // this when the image-stream is not "released" and // the agent version hasn't been specified. logger.Warningf("no prepackaged tools available") } ctx.Infof("Installing Juju agent on bootstrap instance") publicKey, err := userPublicSigningKey() if err != nil { return err } instanceConfig, err := instancecfg.NewBootstrapInstanceConfig( args.BootstrapConstraints, args.ModelConstraints, result.Series, publicKey, ) if err != nil { return err } if err := instanceConfig.SetTools(selectedToolsList); err != nil { return errors.Trace(err) } instanceConfig.CustomImageMetadata = customImageMetadata instanceConfig.HostedModelConfig = args.HostedModelConfig instanceConfig.GUI = guiArchive(args.GUIDataSourceBaseURL, func(msg string) { ctx.Infof(msg) }) if err := result.Finalize(ctx, instanceConfig); err != nil { return err } ctx.Infof("Bootstrap agent installed") return nil }
func (t *localServerSuite) TestSupportsNetworking(c *gc.C) { env := t.Prepare(c) _, supported := environs.SupportsNetworking(env) c.Assert(supported, jc.IsTrue) }
func (t *localServerSuite) prepareEnviron(c *gc.C) environs.NetworkingEnviron { env := t.Prepare(c) netenv, supported := environs.SupportsNetworking(env) c.Assert(supported, jc.IsTrue) return netenv }
func (dw *discoverspacesWorker) handleSubnets() error { environ, ok := environs.SupportsNetworking(dw.config.Environ) if !ok { logger.Debugf("not a networking environ") return nil } if supported, err := environ.SupportsSpaceDiscovery(); err != nil { return errors.Trace(err) } else if !supported { logger.Debugf("environ does not support space discovery") return nil } providerSpaces, err := environ.Spaces() if err != nil { return errors.Trace(err) } facade := dw.config.Facade listSpacesResult, err := facade.ListSpaces() if err != nil { return errors.Trace(err) } stateSubnets, err := facade.ListSubnets(params.SubnetsFilters{}) if err != nil { return errors.Trace(err) } stateSubnetIds := make(set.Strings) for _, subnet := range stateSubnets.Results { stateSubnetIds.Add(subnet.ProviderId) } stateSpaceMap := make(map[string]params.ProviderSpace) spaceNames := make(set.Strings) for _, space := range listSpacesResult.Results { stateSpaceMap[space.ProviderId] = space spaceNames.Add(space.Name) } // TODO(mfoord): we need to delete spaces and subnets that no longer // exist, so long as they're not in use. var createSpacesArgs params.CreateSpacesParams var addSubnetsArgs params.AddSubnetsParams for _, space := range providerSpaces { // Check if the space is already in state, in which case we know // its name. stateSpace, ok := stateSpaceMap[string(space.ProviderId)] var spaceTag names.SpaceTag if ok { spaceName := stateSpace.Name if !names.IsValidSpace(spaceName) { // Can only happen if an invalid name is stored // in state. logger.Errorf("space %q has an invalid name, ignoring", spaceName) continue } spaceTag = names.NewSpaceTag(spaceName) } else { // The space is new, we need to create a valid name for it // in state. spaceName := string(space.Name) // Convert the name into a valid name that isn't already in // use. spaceName = dw.config.NewName(spaceName, spaceNames) spaceNames.Add(spaceName) spaceTag = names.NewSpaceTag(spaceName) // We need to create the space. createSpacesArgs.Spaces = append(createSpacesArgs.Spaces, params.CreateSpaceParams{ Public: false, SpaceTag: spaceTag.String(), ProviderId: string(space.ProviderId), }) } // TODO(mfoord): currently no way of removing subnets, or // changing the space they're in, so we can only add ones we // don't already know about. for _, subnet := range space.Subnets { if stateSubnetIds.Contains(string(subnet.ProviderId)) { continue } zones := subnet.AvailabilityZones if len(zones) == 0 { logger.Tracef( "provider does not specify zones for subnet %q; using 'default' zone as fallback", subnet.CIDR, ) zones = []string{"default"} } addSubnetsArgs.Subnets = append(addSubnetsArgs.Subnets, params.AddSubnetParams{ SubnetProviderId: string(subnet.ProviderId), SpaceTag: spaceTag.String(), Zones: zones, }) } } if err := dw.createSpacesFromArgs(createSpacesArgs); err != nil { return errors.Trace(err) } if err := dw.addSubnetsFromArgs(addSubnetsArgs); err != nil { return errors.Trace(err) } return nil }
func (s *environSuite) TestSupportsNetworking(c *gc.C) { _, ok := environs.SupportsNetworking(s.env) c.Assert(ok, jc.IsFalse) }
func (suite *environSuite) TestSupportsNetworking(c *gc.C) { env := suite.makeEnviron() _, supported := environs.SupportsNetworking(env) c.Assert(supported, jc.IsTrue) }
func (s *environSuite) TestSupportsNetworking(c *gc.C) { var _ environs.Networking = s.Env _, ok := environs.SupportsNetworking(s.Env) c.Assert(ok, jc.IsTrue) }
// Bootstrap bootstraps the given environment. The supplied constraints are // used to provision the instance, and are also set within the bootstrapped // environment. func Bootstrap(ctx environs.BootstrapContext, environ environs.Environ, args BootstrapParams) error { if err := args.Validate(); err != nil { return errors.Annotate(err, "validating bootstrap parameters") } cfg := environ.Config() if authKeys := ssh.SplitAuthorisedKeys(cfg.AuthorizedKeys()); len(authKeys) == 0 { // Apparently this can never happen, so it's not tested. But, one day, // Config will act differently (it's pretty crazy that, AFAICT, the // authorized-keys are optional config settings... but it's impossible // to actually *create* a config without them)... and when it does, // we'll be here to catch this problem early. return errors.Errorf("model configuration has no authorized-keys") } _, supportsNetworking := environs.SupportsNetworking(environ) logger.Debugf("model %q supports service/machine networks: %v", cfg.Name(), supportsNetworking) disableNetworkManagement, _ := cfg.DisableNetworkManagement() logger.Debugf("network management by juju enabled: %v", !disableNetworkManagement) // Set default tools metadata source, add image metadata source, // then verify constraints. Providers may rely on image metadata // for constraint validation. var customImageMetadata []*imagemetadata.ImageMetadata if args.MetadataDir != "" { var err error customImageMetadata, err = setPrivateMetadataSources(args.MetadataDir) if err != nil { return err } } var bootstrapSeries *string if args.BootstrapSeries != "" { bootstrapSeries = &args.BootstrapSeries } var bootstrapArchForImageSearch string if args.BootstrapConstraints.Arch != nil { bootstrapArchForImageSearch = *args.BootstrapConstraints.Arch } else if args.ModelConstraints.Arch != nil { bootstrapArchForImageSearch = *args.ModelConstraints.Arch } else { bootstrapArchForImageSearch = arch.HostArch() // We no longer support i386. if bootstrapArchForImageSearch == arch.I386 { bootstrapArchForImageSearch = arch.AMD64 } } ctx.Verbosef("Loading image metadata") imageMetadata, err := bootstrapImageMetadata(environ, bootstrapSeries, bootstrapArchForImageSearch, args.BootstrapImage, &customImageMetadata, ) if err != nil { return errors.Trace(err) } // We want to determine a list of valid architectures for which to pick tools and images. // This includes architectures from custom and other available image metadata. architectures := set.NewStrings() if len(customImageMetadata) > 0 { for _, customMetadata := range customImageMetadata { architectures.Add(customMetadata.Arch) } } if len(imageMetadata) > 0 { for _, iMetadata := range imageMetadata { architectures.Add(iMetadata.Arch) } } constraintsValidator, err := environ.ConstraintsValidator() if err != nil { return err } constraintsValidator.UpdateVocabulary(constraints.Arch, architectures.SortedValues()) bootstrapConstraints, err := constraintsValidator.Merge( args.ModelConstraints, args.BootstrapConstraints, ) if err != nil { return errors.Trace(err) } // The arch we use to find tools isn't the boostrapConstraints arch. // We copy the constraints arch to a separate variable and // update it from the host arch if not specified. // (axw) This is still not quite right: // For e.g. if there is a MAAS with only ARM64 machines, // on an AMD64 client, we're going to look for only AMD64 tools, // limiting what the provider can bootstrap anyway. var bootstrapArch string if bootstrapConstraints.Arch != nil { bootstrapArch = *bootstrapConstraints.Arch } else { // If no arch is specified as a constraint, we'll bootstrap // on the same arch as the client used to bootstrap. bootstrapArch = arch.HostArch() // We no longer support controllers on i386. // If we are bootstrapping from an i386 client, // we'll look for amd64 tools. if bootstrapArch == arch.I386 { bootstrapArch = arch.AMD64 } } var availableTools coretools.List if !args.BuildAgent { ctx.Infof("Looking for packaged Juju agent version %s for %s", args.AgentVersion, bootstrapArch) availableTools, err = findPackagedTools(environ, args.AgentVersion, &bootstrapArch, bootstrapSeries) if err != nil && !errors.IsNotFound(err) { return err } } // If there are no prepackaged tools and a specific version has not been // requested, look for or build a local binary. var builtTools *sync.BuiltAgent if len(availableTools) == 0 && (args.AgentVersion == nil || isCompatibleVersion(*args.AgentVersion, jujuversion.Current)) { if args.BuildAgentTarball == nil { return errors.New("cannot build agent binary to upload") } if err := validateUploadAllowed(environ, &bootstrapArch, bootstrapSeries, constraintsValidator); err != nil { return err } if args.BuildAgent { ctx.Infof("Building local Juju agent binary version %s for %s", args.AgentVersion, bootstrapArch) } else { ctx.Infof("No packaged binary found, preparing local Juju agent binary") } var forceVersion version.Number availableTools, forceVersion = locallyBuildableTools(bootstrapSeries) builtTools, err = args.BuildAgentTarball(args.BuildAgent, &forceVersion, cfg.AgentStream()) if err != nil { return errors.Annotate(err, "cannot package bootstrap agent binary") } defer os.RemoveAll(builtTools.Dir) for i, tool := range availableTools { if tool.URL != "" { continue } filename := filepath.Join(builtTools.Dir, builtTools.StorageName) tool.URL = fmt.Sprintf("file://%s", filename) tool.Size = builtTools.Size tool.SHA256 = builtTools.Sha256Hash availableTools[i] = tool } } if len(availableTools) == 0 { return errors.New(noToolsMessage) } // If we're uploading, we must override agent-version; // if we're not uploading, we want to ensure we have an // agent-version set anyway, to appease FinishInstanceConfig. // In the latter case, setBootstrapTools will later set // agent-version to the correct thing. agentVersion := jujuversion.Current if args.AgentVersion != nil { agentVersion = *args.AgentVersion } if cfg, err = cfg.Apply(map[string]interface{}{ "agent-version": agentVersion.String(), }); err != nil { return err } if err = environ.SetConfig(cfg); err != nil { return err } ctx.Verbosef("Starting new instance for initial controller") result, err := environ.Bootstrap(ctx, environs.BootstrapParams{ CloudName: args.CloudName, CloudRegion: args.CloudRegion, ControllerConfig: args.ControllerConfig, ModelConstraints: args.ModelConstraints, BootstrapConstraints: bootstrapConstraints, BootstrapSeries: args.BootstrapSeries, Placement: args.Placement, AvailableTools: availableTools, ImageMetadata: imageMetadata, }) if err != nil { return err } matchingTools, err := availableTools.Match(coretools.Filter{ Arch: result.Arch, Series: result.Series, }) if err != nil { return err } selectedToolsList, err := getBootstrapToolsVersion(matchingTools) if err != nil { return err } // We set agent-version to the newest version, so the agent will immediately upgrade itself. // Note that this only is relevant if a specific agent version has not been requested, since // in that case the specific version will be the only version available. newestVersion, _ := matchingTools.Newest() if err := setBootstrapToolsVersion(environ, newestVersion); err != nil { return err } logger.Infof("Installing Juju agent on bootstrap instance") publicKey, err := userPublicSigningKey() if err != nil { return err } instanceConfig, err := instancecfg.NewBootstrapInstanceConfig( args.ControllerConfig, bootstrapConstraints, args.ModelConstraints, result.Series, publicKey, ) if err != nil { return err } if err := instanceConfig.SetTools(selectedToolsList); err != nil { return errors.Trace(err) } // Make sure we have the most recent environ config as the specified // tools version has been updated there. cfg = environ.Config() if err := finalizeInstanceBootstrapConfig(ctx, instanceConfig, args, cfg, customImageMetadata); err != nil { return errors.Annotate(err, "finalizing bootstrap instance config") } if err := result.Finalize(ctx, instanceConfig, args.DialOpts); err != nil { return err } ctx.Infof("Bootstrap agent now started") return nil }
// DeployService takes a charm and various parameters and deploys it. func DeployService(st *state.State, args DeployServiceParams) (*state.Service, error) { if args.NumUnits > 1 && len(args.Placement) == 0 && args.ToMachineSpec != "" { return nil, fmt.Errorf("cannot use --num-units with --to") } settings, err := args.Charm.Config().ValidateSettings(args.ConfigSettings) if err != nil { return nil, err } if args.Charm.Meta().Subordinate { if args.NumUnits != 0 || args.ToMachineSpec != "" { return nil, fmt.Errorf("subordinate service must be deployed without units") } if !constraints.IsEmpty(&args.Constraints) { return nil, fmt.Errorf("subordinate service must be deployed without constraints") } } if args.ServiceOwner == "" { env, err := st.Environment() if err != nil { return nil, errors.Trace(err) } args.ServiceOwner = env.Owner().String() } // TODO(fwereade): transactional State.AddService including settings, constraints // (minimumUnitCount, initialMachineIds?). if len(args.Networks) > 0 || args.Constraints.HaveNetworks() { conf, err := st.EnvironConfig() if err != nil { return nil, err } env, err := environs.New(conf) if err != nil { return nil, err } if _, ok := environs.SupportsNetworking(env); !ok { return nil, fmt.Errorf("cannot deploy with networks: not suppored by the environment") } } service, err := st.AddService( args.ServiceName, args.ServiceOwner, args.Charm, args.Networks, stateStorageConstraints(args.Storage), ) if err != nil { return nil, err } if len(settings) > 0 { if err := service.UpdateConfigSettings(settings); err != nil { return nil, err } } if args.Charm.Meta().Subordinate { return service, nil } if !constraints.IsEmpty(&args.Constraints) { if err := service.SetConstraints(args.Constraints); err != nil { return nil, err } } if args.NumUnits > 0 { var err error // We either have a machine spec or a placement directive. // Placement directives take precedence. if len(args.Placement) > 0 || args.ToMachineSpec == "" { _, err = AddUnitsWithPlacement(st, service, args.NumUnits, args.Placement) } else { _, err = AddUnits(st, service, args.NumUnits, args.ToMachineSpec) } if err != nil { return nil, err } } return service, nil }