// StoreCharmArchive stores a charm archive in environment storage. func StoreCharmArchive(st *state.State, curl *charm.URL, ch charm.Charm, r io.Reader, size int64, sha256 string) error { storage := newStateStorage(st.EnvironUUID(), st.MongoSession()) storagePath, err := charmArchiveStoragePath(curl) if err != nil { return errors.Annotate(err, "cannot generate charm archive name") } if err := storage.Put(storagePath, r, size); err != nil { return errors.Annotate(err, "cannot add charm to storage") } // Now update the charm data in state and mark it as no longer pending. _, err = st.UpdateUploadedCharm(ch, curl, storagePath, sha256) if err != nil { alreadyUploaded := err == state.ErrCharmRevisionAlreadyModified || errors.Cause(err) == state.ErrCharmRevisionAlreadyModified || state.IsCharmAlreadyUploadedError(err) if err := storage.Remove(storagePath); err != nil { if alreadyUploaded { logger.Errorf("cannot remove duplicated charm archive from storage: %v", err) } else { logger.Errorf("cannot remove unsuccessfully recorded charm archive from storage: %v", err) } } if alreadyUploaded { // Somebody else managed to upload and update the charm in // state before us. This is not an error. return nil } } return nil }
// migrateCharmStorage copies uploaded charms from provider storage // to environment storage, and then adds the storage path into the // charm's document in state. func migrateCharmStorage(st *state.State, agentConfig agent.Config) error { logger.Debugf("migrating charms to environment storage") charms, err := st.AllCharms() if err != nil { return err } storage := storage.NewStorage(st.EnvironUUID(), st.MongoSession()) // Local and manual provider host storage on the state server's // filesystem, and serve via HTTP storage. The storage worker // doesn't run yet, so we just open the files directly. fetchCharmArchive := fetchCharmArchive providerType := agentConfig.Value(agent.ProviderType) if providerType == provider.Local || provider.IsManual(providerType) { storageDir := agentConfig.Value(agent.StorageDir) fetchCharmArchive = localstorage{storageDir}.fetchCharmArchive } storagePaths := make(map[*charm.URL]string) for _, ch := range charms { if ch.IsPlaceholder() { logger.Debugf("skipping %s, placeholder charm", ch.URL()) continue } if !ch.IsUploaded() { logger.Debugf("skipping %s, not uploaded to provider storage", ch.URL()) continue } if charmStoragePath(ch) != "" { logger.Debugf("skipping %s, already in environment storage", ch.URL()) continue } url := charmBundleURL(ch) if url == nil { logger.Debugf("skipping %s, has no bundle URL", ch.URL()) continue } uuid, err := utils.NewUUID() if err != nil { return err } data, err := fetchCharmArchive(url) if err != nil { return err } curl := ch.URL() storagePath := fmt.Sprintf("charms/%s-%s", curl, uuid) logger.Debugf("uploading %s to %q in environment storage", curl, storagePath) err = storage.Put(storagePath, bytes.NewReader(data), int64(len(data))) if err != nil { return errors.Annotatef(err, "failed to upload %s to storage", curl) } storagePaths[curl] = storagePath } return stateAddCharmStoragePaths(st, storagePaths) }
// TestingApiHandler gives you an ApiHandler that isn't connected to // anything real. It's enough to let test some basic functionality though. func TestingApiHandler(c *gc.C, srvSt, st *state.State) (*apiHandler, *common.Resources) { srv := &Server{ state: srvSt, tag: names.NewMachineTag("0"), } h, err := newApiHandler(srv, st, nil, nil, st.EnvironUUID()) c.Assert(err, jc.ErrorIsNil) return h, h.getResources() }
// dyingEnvWorker is passed to NewEnvWorkerManager in these tests. It // creates a fake Runner instance when envWorkerManager starts workers for a // dying or dead environment. func (s *suite) dyingEnvWorker(ssSt envworkermanager.InitialState, st *state.State) (worker.Worker, error) { if s.startErr != nil { return nil, s.startErr } runner := &fakeRunner{ ssEnvUUID: ssSt.EnvironUUID(), envUUID: st.EnvironUUID(), } s.runnerC <- runner return runner, nil }
func addMachineForUnit(st *state.State, unit *state.Unit, placement *instance.Placement, networks []string) (*state.Machine, error) { unitCons, err := unit.Constraints() if err != nil { return nil, err } var containerType instance.ContainerType var mid, placementDirective string // Extract container type and parent from container placement directives. if containerType, err = instance.ParseContainerType(placement.Scope); err == nil { mid = placement.Directive } else { switch placement.Scope { case st.EnvironUUID(): placementDirective = placement.Directive case instance.MachineScope: mid = placement.Directive default: return nil, errors.Errorf("invalid environment UUID %q", placement.Scope) } } // Create any new machine marked as dirty so that // nothing else will grab it before we assign the unit to it. // If a container is to be used, create it. if containerType != "" { template := state.MachineTemplate{ Series: unit.Series(), Jobs: []state.MachineJob{state.JobHostUnits}, Dirty: true, Constraints: *unitCons, RequestedNetworks: networks, } return st.AddMachineInsideMachine(template, mid, containerType) } // If a placement directive is to be used, do that here. if placementDirective != "" { template := state.MachineTemplate{ Series: unit.Series(), Jobs: []state.MachineJob{state.JobHostUnits}, Dirty: true, Constraints: *unitCons, RequestedNetworks: networks, Placement: placementDirective, } return st.AddOneMachine(template) } // Otherwise use an existing machine. return st.Machine(mid) }
func (s *imageSuite) storeFakeImage(c *gc.C, st *state.State, kind, series, arch string) { storage := st.ImageStorage() metadata := &imagestorage.Metadata{ EnvUUID: st.EnvironUUID(), Kind: kind, Series: series, Arch: arch, Size: int64(len(s.imageData)), SHA256: s.imageChecksum, SourceURL: "http://path", } err := storage.AddImage(strings.NewReader(s.imageData), metadata) c.Assert(err, gc.IsNil) }
// NewClient creates a new instance of the Client Facade. func NewClient(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*Client, error) { if !authorizer.AuthClient() { return nil, common.ErrPerm } urlGetter := common.NewToolsURLGetter(st.EnvironUUID(), st) return &Client{ api: &API{ state: st, auth: authorizer, resources: resources, statusSetter: common.NewStatusSetter(st, common.AuthAlways()), toolsFinder: common.NewToolsFinder(st, st, urlGetter), }, check: common.NewBlockChecker(st)}, nil }
// NewEnvironmentManagerAPI creates a new api server endpoint for managing // environments. func NewEnvironmentManagerAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*EnvironmentManagerAPI, error) { if !authorizer.AuthClient() { return nil, common.ErrPerm } urlGetter := common.NewToolsURLGetter(st.EnvironUUID(), st) return &EnvironmentManagerAPI{ state: getState(st), authorizer: authorizer, toolsFinder: common.NewToolsFinder(st, st, urlGetter), }, nil }
func addCharm(st *state.State, curl *charm.URL, ch charm.Charm) (*state.Charm, error) { var f *os.File name := charm.Quote(curl.String()) switch ch := ch.(type) { case *charm.CharmDir: var err error if f, err = ioutil.TempFile("", name); err != nil { return nil, err } defer os.Remove(f.Name()) defer f.Close() err = ch.ArchiveTo(f) if err != nil { return nil, fmt.Errorf("cannot bundle charm: %v", err) } if _, err := f.Seek(0, 0); err != nil { return nil, err } case *charm.CharmArchive: var err error if f, err = os.Open(ch.Path); err != nil { return nil, fmt.Errorf("cannot read charm bundle: %v", err) } defer f.Close() default: return nil, fmt.Errorf("unknown charm type %T", ch) } digest, size, err := utils.ReadSHA256(f) if err != nil { return nil, err } if _, err := f.Seek(0, 0); err != nil { return nil, err } stor := statestorage.NewStorage(st.EnvironUUID(), st.MongoSession()) storagePath := fmt.Sprintf("/charms/%s-%s", curl.String(), digest) if err := stor.Put(storagePath, f, size); err != nil { return nil, fmt.Errorf("cannot put charm: %v", err) } sch, err := st.AddCharm(ch, curl, storagePath, digest) if err != nil { return nil, fmt.Errorf("cannot add charm: %v", err) } return sch, nil }
// migrateCustomImageMetadata copies uploaded image metadata from provider // storage to environment storage, preserving paths. func migrateCustomImageMetadata(st *state.State, agentConfig agent.Config) error { logger.Debugf("migrating custom image metadata to environment storage") estor := newStateStorage(st.EnvironUUID(), st.MongoSession()) // Local and manual provider host storage on the state server's // filesystem, and serve via HTTP storage. The storage worker // doesn't run yet, so we just open the files directly. var pstor storage.StorageReader providerType := agentConfig.Value(agent.ProviderType) if providerType == provider.Local || provider.IsManual(providerType) { storageDir := agentConfig.Value(agent.StorageDir) var err error pstor, err = filestorage.NewFileStorageReader(storageDir) if err != nil { return errors.Annotate(err, "cannot get local filesystem storage reader") } } else { var err error pstor, err = environs.LegacyStorage(st) if errors.IsNotSupported(err) { return nil } else if err != nil { return errors.Annotate(err, "cannot get provider storage") } } paths, err := pstor.List(storage.BaseImagesPath) if err != nil { return err } for _, path := range paths { logger.Infof("migrating image metadata at path %q", path) data, err := readImageMetadata(pstor, path) if err != nil { return errors.Annotate(err, "failed to read image metadata") } err = estor.Put(path, bytes.NewReader(data), int64(len(data))) if err != nil { return errors.Annotate(err, "failed to write image metadata") } } return nil }
// newApiHandler returns a new apiHandler. func newApiHandler(srv *Server, st *state.State, rpcConn *rpc.Conn, reqNotifier *requestNotifier, envUUID string) (*apiHandler, error) { r := &apiHandler{ state: st, closeState: st.EnvironUUID() != srv.state.EnvironUUID(), resources: common.NewResources(), rpcConn: rpcConn, envUUID: envUUID, } if err := r.resources.RegisterNamed("machineID", common.StringResource(srv.tag.Id())); err != nil { return nil, errors.Trace(err) } if err := r.resources.RegisterNamed("dataDir", common.StringResource(srv.dataDir)); err != nil { return nil, errors.Trace(err) } if err := r.resources.RegisterNamed("logDir", common.StringResource(srv.logDir)); err != nil { return nil, errors.Trace(err) } return r, nil }
// downloadCharm downloads the given charm name from the provider storage and // saves the corresponding zip archive to the given charmArchivePath. func (h *charmsHandler) downloadCharm(st *state.State, curl *charm.URL, charmArchivePath string) error { storage := storage.NewStorage(st.EnvironUUID(), st.MongoSession()) ch, err := st.Charm(curl) if err != nil { return errors.Annotate(err, "cannot get charm from state") } // In order to avoid races, the archive is saved in a temporary file which // is then atomically renamed. The temporary file is created in the // charm cache directory so that we can safely assume the rename source and // target live in the same file system. cacheDir := filepath.Dir(charmArchivePath) if err = os.MkdirAll(cacheDir, 0755); err != nil { return errors.Annotate(err, "cannot create the charms cache") } tempCharmArchive, err := ioutil.TempFile(cacheDir, "charm") if err != nil { return errors.Annotate(err, "cannot create charm archive temp file") } defer tempCharmArchive.Close() // Use the storage to retrieve and save the charm archive. reader, _, err := storage.Get(ch.StoragePath()) if err != nil { defer cleanupFile(tempCharmArchive) return errors.Annotate(err, "cannot get charm from environment storage") } defer reader.Close() if _, err = io.Copy(tempCharmArchive, reader); err != nil { defer cleanupFile(tempCharmArchive) return errors.Annotate(err, "error processing charm archive download") } tempCharmArchive.Close() if err = os.Rename(tempCharmArchive.Name(), charmArchivePath); err != nil { defer cleanupFile(tempCharmArchive) return errors.Annotate(err, "error renaming the charm archive") } return nil }
// newMacaroonAuth returns an authenticator that can authenticate // macaroon-based logins. This is just a helper function for authCtxt.macaroonAuth. func newMacaroonAuth(st *state.State) (*authentication.MacaroonAuthenticator, error) { envCfg, err := st.EnvironConfig() if err != nil { return nil, errors.Annotate(err, "cannot get environment config") } idURL := envCfg.IdentityURL() if idURL == "" { return nil, errMacaroonAuthNotConfigured } // The identity server has been configured, // so configure the bakery service appropriately. idPK := envCfg.IdentityPublicKey() if idPK == nil { // No public key supplied - retrieve it from the identity manager. idPK, err = httpbakery.PublicKeyForLocation(http.DefaultClient, idURL) if err != nil { return nil, errors.Annotate(err, "cannot get identity public key") } } svc, err := bakery.NewService( bakery.NewServiceParams{ Location: "juju environment " + st.EnvironUUID(), Locator: bakery.PublicKeyLocatorMap{ idURL: idPK, }, }, ) if err != nil { return nil, errors.Annotate(err, "cannot make bakery service") } var auth authentication.MacaroonAuthenticator auth.Service = svc auth.Macaroon, err = svc.NewMacaroon("api-login", nil, nil) if err != nil { return nil, errors.Annotate(err, "cannot make macaroon") } auth.IdentityLocation = idURL return &auth, nil }
func (s *LogsSuite) countLogs(c *gc.C, st *state.State) int { count, err := s.logsColl.Find(bson.M{"e": st.EnvironUUID()}).Count() c.Assert(err, jc.ErrorIsNil) return count }
// Run initializes state for an environment. func (c *BootstrapCommand) Run(_ *cmd.Context) error { envCfg, err := config.New(config.NoDefaults, c.EnvConfig) if err != nil { return err } err = c.ReadConfig("machine-0") if err != nil { return err } agentConfig := c.CurrentConfig() network.InitializeFromConfig(agentConfig) // agent.Jobs is an optional field in the agent config, and was // introduced after 1.17.2. We default to allowing units on // machine-0 if missing. jobs := agentConfig.Jobs() if len(jobs) == 0 { jobs = []multiwatcher.MachineJob{ multiwatcher.JobManageEnviron, multiwatcher.JobHostUnits, multiwatcher.JobManageNetworking, } } // Get the bootstrap machine's addresses from the provider. env, err := environs.New(envCfg) if err != nil { return err } instanceId := instance.Id(c.InstanceId) instances, err := env.Instances([]instance.Id{instanceId}) if err != nil { return err } addrs, err := instances[0].Addresses() if err != nil { return err } // Generate a private SSH key for the state servers, and add // the public key to the environment config. We'll add the // private key to StateServingInfo below. privateKey, publicKey, err := sshGenerateKey(config.JujuSystemKey) if err != nil { return errors.Annotate(err, "failed to generate system key") } authorizedKeys := config.ConcatAuthKeys(envCfg.AuthorizedKeys(), publicKey) envCfg, err = env.Config().Apply(map[string]interface{}{ config.AuthKeysConfig: authorizedKeys, }) if err != nil { return errors.Annotate(err, "failed to add public key to environment config") } // Generate a shared secret for the Mongo replica set, and write it out. sharedSecret, err := mongo.GenerateSharedSecret() if err != nil { return err } info, ok := agentConfig.StateServingInfo() if !ok { return fmt.Errorf("bootstrap machine config has no state serving info") } info.SharedSecret = sharedSecret info.SystemIdentity = privateKey err = c.ChangeConfig(func(agentConfig agent.ConfigSetter) error { agentConfig.SetStateServingInfo(info) return nil }) if err != nil { return fmt.Errorf("cannot write agent config: %v", err) } agentConfig = c.CurrentConfig() // Create system-identity file if err := agent.WriteSystemIdentityFile(agentConfig); err != nil { return err } if err := c.startMongo(addrs, agentConfig); err != nil { return err } logger.Infof("started mongo") // Initialise state, and store any agent config (e.g. password) changes. var st *state.State var m *state.Machine err = c.ChangeConfig(func(agentConfig agent.ConfigSetter) error { var stateErr error dialOpts := mongo.DefaultDialOpts() // Set a longer socket timeout than usual, as the machine // will be starting up and disk I/O slower than usual. This // has been known to cause timeouts in queries. timeouts := envCfg.BootstrapSSHOpts() dialOpts.SocketTimeout = timeouts.Timeout if dialOpts.SocketTimeout < minSocketTimeout { dialOpts.SocketTimeout = minSocketTimeout } // We shouldn't attempt to dial peers until we have some. dialOpts.Direct = true adminTag := names.NewLocalUserTag(c.AdminUsername) st, m, stateErr = agentInitializeState( adminTag, agentConfig, envCfg, agent.BootstrapMachineConfig{ Addresses: addrs, Constraints: c.Constraints, Jobs: jobs, InstanceId: instanceId, Characteristics: c.Hardware, SharedSecret: sharedSecret, }, dialOpts, environs.NewStatePolicy(), ) return stateErr }) if err != nil { return err } defer st.Close() // Populate the tools catalogue. if err := c.populateTools(st, env); err != nil { return err } // Add custom image metadata to environment storage. if c.ImageMetadataDir != "" { if err := c.saveCustomImageMetadata(st); err != nil { return err } // TODO (anastasiamac 2015-09-24) Remove this once search path is updated.. stor := newStateStorage(st.EnvironUUID(), st.MongoSession()) if err := c.storeCustomImageMetadata(stor); err != nil { return err } } // Populate the storage pools. if err := c.populateDefaultStoragePools(st); err != nil { return err } // bootstrap machine always gets the vote return m.SetHasVote(true) }
// Run initializes state for an environment. func (c *BootstrapCommand) Run(_ *cmd.Context) error { envCfg, err := config.New(config.NoDefaults, c.EnvConfig) if err != nil { return err } err = c.ReadConfig("machine-0") if err != nil { return err } agentConfig := c.CurrentConfig() network.SetPreferIPv6(agentConfig.PreferIPv6()) // agent.Jobs is an optional field in the agent config, and was // introduced after 1.17.2. We default to allowing units on // machine-0 if missing. jobs := agentConfig.Jobs() if len(jobs) == 0 { jobs = []multiwatcher.MachineJob{ multiwatcher.JobManageEnviron, multiwatcher.JobHostUnits, multiwatcher.JobManageNetworking, } } // Get the bootstrap machine's addresses from the provider. env, err := environs.New(envCfg) if err != nil { return err } newConfigAttrs := make(map[string]interface{}) // Check to see if a newer agent version has been requested // by the bootstrap client. desiredVersion, ok := envCfg.AgentVersion() if ok && desiredVersion != version.Current { // If we have been asked for a newer version, ensure the newer // tools can actually be found, or else bootstrap won't complete. stream := envtools.PreferredStream(&desiredVersion, envCfg.Development(), envCfg.AgentStream()) logger.Infof("newer tools requested, looking for %v in stream %v", desiredVersion, stream) filter := tools.Filter{ Number: desiredVersion, Arch: arch.HostArch(), Series: series.HostSeries(), } _, toolsErr := envtools.FindTools(env, -1, -1, stream, filter) if toolsErr == nil { logger.Infof("tools are available, upgrade will occur after bootstrap") } if errors.IsNotFound(toolsErr) { // Newer tools not available, so revert to using the tools // matching the current agent version. logger.Warningf("newer tools for %q not available, sticking with version %q", desiredVersion, version.Current) newConfigAttrs["agent-version"] = version.Current.String() } else if toolsErr != nil { logger.Errorf("cannot find newer tools: %v", toolsErr) return toolsErr } } instanceId := instance.Id(c.InstanceId) instances, err := env.Instances([]instance.Id{instanceId}) if err != nil { return err } addrs, err := instances[0].Addresses() if err != nil { return err } // Generate a private SSH key for the state servers, and add // the public key to the environment config. We'll add the // private key to StateServingInfo below. privateKey, publicKey, err := sshGenerateKey(config.JujuSystemKey) if err != nil { return errors.Annotate(err, "failed to generate system key") } authorizedKeys := config.ConcatAuthKeys(envCfg.AuthorizedKeys(), publicKey) newConfigAttrs[config.AuthKeysConfig] = authorizedKeys // Generate a shared secret for the Mongo replica set, and write it out. sharedSecret, err := mongo.GenerateSharedSecret() if err != nil { return err } info, ok := agentConfig.StateServingInfo() if !ok { return fmt.Errorf("bootstrap machine config has no state serving info") } info.SharedSecret = sharedSecret info.SystemIdentity = privateKey err = c.ChangeConfig(func(agentConfig agent.ConfigSetter) error { agentConfig.SetStateServingInfo(info) return nil }) if err != nil { return fmt.Errorf("cannot write agent config: %v", err) } agentConfig = c.CurrentConfig() // Create system-identity file if err := agent.WriteSystemIdentityFile(agentConfig); err != nil { return err } if err := c.startMongo(addrs, agentConfig); err != nil { return err } logger.Infof("started mongo") // Initialise state, and store any agent config (e.g. password) changes. envCfg, err = env.Config().Apply(newConfigAttrs) if err != nil { return errors.Annotate(err, "failed to update environment config") } var st *state.State var m *state.Machine err = c.ChangeConfig(func(agentConfig agent.ConfigSetter) error { var stateErr error dialOpts := mongo.DefaultDialOpts() // Set a longer socket timeout than usual, as the machine // will be starting up and disk I/O slower than usual. This // has been known to cause timeouts in queries. timeouts := envCfg.BootstrapSSHOpts() dialOpts.SocketTimeout = timeouts.Timeout if dialOpts.SocketTimeout < minSocketTimeout { dialOpts.SocketTimeout = minSocketTimeout } // We shouldn't attempt to dial peers until we have some. dialOpts.Direct = true adminTag := names.NewLocalUserTag(c.AdminUsername) st, m, stateErr = agentInitializeState( adminTag, agentConfig, envCfg, agent.BootstrapMachineConfig{ Addresses: addrs, BootstrapConstraints: c.BootstrapConstraints, EnvironConstraints: c.EnvironConstraints, Jobs: jobs, InstanceId: instanceId, Characteristics: c.Hardware, SharedSecret: sharedSecret, }, dialOpts, environs.NewStatePolicy(), ) return stateErr }) if err != nil { return err } defer st.Close() // Populate the tools catalogue. if err := c.populateTools(st, env); err != nil { return err } // Add custom image metadata to environment storage. if c.ImageMetadataDir != "" { if err := c.saveCustomImageMetadata(st, env); err != nil { return err } stor := newStateStorage(st.EnvironUUID(), st.MongoSession()) if err := c.storeCustomImageMetadata(stor); err != nil { return err } } // Populate the storage pools. if err = c.populateDefaultStoragePools(st); err != nil { return err } // bootstrap machine always gets the vote return m.SetHasVote(true) }