// NewSimpleAuthenticator gets the state and api info once from the environ. func NewSimpleAuthenticator(environ environs.Environ) (AuthenticationProvider, error) { stateInfo, apiInfo, err := environ.StateInfo() if err != nil { return nil, err } return &simpleAuth{stateInfo, apiInfo}, nil }
// WritablePublicStorage returns a Storage instance which is authorised to write to the PublicStorage bucket. // It is used by tests which need to upload files. func WritablePublicStorage(e environs.Environ) environs.Storage { // In the case of ec2, access to the public storage instance is created with the user's AWS credentials. // So write access is there implicitly, and we just need to cast to a writable storage instance. // This contrasts with the openstack case, where the public storage instance truly is read only and we need // to create a separate writable instance. If the ec2 case ever changes, the changes are confined to this method. return e.PublicStorage().(environs.Storage) }
// fetchAllInstances returns a map[string]environs.Instance representing // a mapping of instance ids to their respective instance. func fetchAllInstances(env environs.Environ) (map[state.InstanceId]environs.Instance, error) { m := make(map[state.InstanceId]environs.Instance) insts, err := env.AllInstances() if err != nil { return nil, err } for _, i := range insts { m[i.Id()] = i } return m, nil }
func publicAttrs(e environs.Environ) map[string]interface{} { cfg := e.Config() secrets, err := e.Provider().SecretAttrs(cfg) if err != nil { panic(err) } attrs := cfg.AllAttrs() for attr := range secrets { delete(attrs, attr) } return attrs }
// NewConn returns a new Conn that uses the // given environment. The environment must have already // been bootstrapped. func NewConn(environ environs.Environ) (*Conn, error) { info, _, err := environ.StateInfo() if err != nil { return nil, err } password := environ.Config().AdminSecret() if password == "" { return nil, fmt.Errorf("cannot connect without admin-secret") } err = environs.CheckEnvironment(environ) if err != nil { return nil, err } info.Password = password opts := state.DefaultDialOpts() st, err := state.Open(info, opts) if errors.IsUnauthorizedError(err) { log.Noticef("juju: authorization error while connecting to state server; retrying") // We can't connect with the administrator password,; // perhaps this was the first connection and the // password has not been changed yet. info.Password = utils.PasswordHash(password) // We try for a while because we might succeed in // connecting to mongo before the state has been // initialized and the initial password set. for a := redialStrategy.Start(); a.Next(); { st, err = state.Open(info, opts) if !errors.IsUnauthorizedError(err) { break } } if err != nil { return nil, err } if err := st.SetAdminMongoPassword(password); err != nil { return nil, err } } else if err != nil { return nil, err } conn := &Conn{ Environ: environ, State: st, } if err := conn.updateSecrets(); err != nil { conn.Close() return nil, fmt.Errorf("unable to push secrets: %v", err) } return conn, nil }
// putNames puts a set of names into the environ's private // and public storage. The data in the private storage is // the name itself; in the public storage the name is preceded with "public-". func putNames(c *C, env environs.Environ, private, public []string) { for _, name := range private { err := env.Storage().Put(name, strings.NewReader(name), int64(len(name))) c.Assert(err, IsNil) } // The contents of all files in the public storage is prefixed with "public-" so // that we can easily tell if we've got the right thing. for _, name := range public { data := "public-" + name err := env.PublicStorage().(environs.Storage).Put(name, strings.NewReader(data), int64(len(data))) c.Assert(err, IsNil) } }
// StartInstanceWithConstraints is a test helper function that starts an instance on the // environment with the specified constraints, using the current series and invalid info states. func StartInstanceWithConstraints(c *C, env environs.Environ, machineId string, cons constraints.Value) (instance.Instance, *instance.HardwareCharacteristics) { series := config.DefaultSeries inst, metadata, err := env.StartInstance( machineId, "fake_nonce", series, cons, FakeStateInfo(machineId), FakeAPIInfo(machineId), ) c.Assert(err, IsNil) return inst, metadata }
// NewConn returns a new Conn that uses the // given environment. The environment must have already // been bootstrapped. func NewConn(environ environs.Environ) (*Conn, error) { info, err := environ.StateInfo() if err != nil { return nil, err } password := environ.Config().AdminSecret() if password == "" { return nil, fmt.Errorf("cannot connect without admin-secret") } info.Password = password st, err := state.Open(info) if err == state.ErrUnauthorized { // We can't connect with the administrator password,; // perhaps this was the first connection and the // password has not been changed yet. info.Password = trivial.PasswordHash(password) // We try for a while because we might succeed in // connecting to mongo before the state has been // initialized and the initial password set. for a := redialStrategy.Start(); a.Next(); { st, err = state.Open(info) if err != state.ErrUnauthorized { break } } if err != nil { return nil, err } if err := st.SetAdminMongoPassword(password); err != nil { return nil, err } } else if err != nil { return nil, err } conn := &Conn{ Environ: environ, State: st, } if err := conn.updateSecrets(); err != nil { conn.Close() return nil, fmt.Errorf("unable to push secrets: %v", err) } return conn, nil }
// NewAPIConn returns a new Conn that uses the // given environment. The environment must have already // been bootstrapped. func NewAPIConn(environ environs.Environ, dialOpts api.DialOpts) (*APIConn, error) { _, info, err := environ.StateInfo() if err != nil { return nil, err } info.Tag = "user-admin" password := environ.Config().AdminSecret() if password == "" { return nil, fmt.Errorf("cannot connect without admin-secret") } info.Password = password st, err := api.Open(info, dialOpts) // TODO(rog): handle errUnauthorized when the API handles passwords. if err != nil { return nil, err } // TODO(rog): implement updateSecrets (see Conn.updateSecrets) return &APIConn{ Environ: environ, State: st, }, nil }
// RemoveAllTools deletes all tools from the supplied environment. func RemoveAllTools(c *C, env environs.Environ) { c.Logf("clearing private storage") RemoveTools(c, env.Storage()) c.Logf("clearing public storage") RemoveTools(c, env.PublicStorage().(environs.Storage)) }
func (u *Upgrader) run() error { // Let the state know the version that is currently running. currentTools, err := tools.ReadTools(u.dataDir, version.Current) if err != nil { // Don't abort everything because we can't find the tools directory. // The problem should sort itself out as we will immediately // download some more tools and upgrade. log.Warningf("upgrader cannot read current tools: %v", err) currentTools = &tools.Tools{ Binary: version.Current, } } err = u.agentState.SetAgentTools(currentTools) if err != nil { return err } // TODO(fwereade): this whole package should be ignorant of environs, // so it shouldn't be watching environ config (and it shouldn't be // looking in storage): we should be able to find out what to download // from state, exactly as we do for charms. w := u.st.WatchEnvironConfig() defer watcher.Stop(w, &u.tomb) // Rather than using worker.WaitForEnviron, invalid environments are // managed explicitly so that all configuration changes are observed // by the loop below. var environ environs.Environ // TODO(rog) retry downloads when they fail. var ( download *downloader.Download downloadTools *tools.Tools downloadDone <-chan downloader.Status ) // If we're killed early on (probably as a result of some other // task dying) we allow ourselves some time to try to connect to // the state and download a new version. We return to normal // undelayed behaviour when: // 1) We find there's no upgrade to do. // 2) A download fails. tomb := delayedTomb(&u.tomb, upgraderKillDelay) noDelay := func() { if tomb != &u.tomb { tomb.Kill(nil) tomb = &u.tomb } } for { // We wait for the tools to change while we're downloading // so that if something goes wrong (for instance a bad URL // hangs up) another change to the proposed tools can // potentially fix things. select { case cfg, ok := <-w.Changes(): if !ok { return watcher.MustErr(w) } var err error if environ == nil { environ, err = environs.New(cfg) if err != nil { log.Errorf("upgrader loaded invalid initial environment configuration: %v", err) break } } else { err = environ.SetConfig(cfg) if err != nil { log.Warningf("upgrader loaded invalid environment configuration: %v", err) // continue on, because the version number is still significant. } } proposed, ok := cfg.AgentVersion() if !ok { // This shouldn't be possible; but if it happens it's no reason // to kill this task. Just wait for the config to change again. continue } if download != nil { // There's a download in progress, stop it if we need to. if downloadTools.Number == proposed { // We are already downloading the requested tools. break } // Tools changed. We need to stop and restart. download.Stop() download, downloadTools, downloadDone = nil, nil, nil } // TODO: major version upgrades. if proposed.Major != version.Current.Major { log.Errorf("major version upgrades are not supported yet") noDelay() break } if proposed == version.Current.Number { noDelay() break } required := version.Binary{ Number: proposed, Series: version.Current.Series, Arch: version.Current.Arch, } if tools, err := tools.ReadTools(u.dataDir, required); err == nil { // The exact tools have already been downloaded, so use them. return u.upgradeReady(currentTools, tools) } tools, err := environs.FindExactTools(environ, required) if err != nil { log.Errorf("upgrader error finding tools for %v: %v", required, err) if !errors.IsNotFoundError(err) { return err } noDelay() // TODO(rog): poll until tools become available. break } log.Infof("upgrader downloading %q", tools.URL) download = downloader.New(tools.URL, "") downloadTools = tools downloadDone = download.Done() case status := <-downloadDone: newTools := downloadTools download, downloadTools, downloadDone = nil, nil, nil if status.Err != nil { log.Errorf("upgrader download of %v failed: %v", newTools.Binary, status.Err) noDelay() break } err := tools.UnpackTools(u.dataDir, newTools, status.File) status.File.Close() if err := os.Remove(status.File.Name()); err != nil { log.Warningf("upgrader cannot remove temporary download file: %v", err) } if err != nil { log.Errorf("upgrader cannot unpack %v tools: %v", newTools.Binary, err) noDelay() break } return u.upgradeReady(currentTools, newTools) case <-tomb.Dying(): if download != nil { return fmt.Errorf("upgrader aborted download of %q", downloadTools.URL) } return nil } } panic("not reached") }
func (u *Upgrader) run() error { // Let the state know the version that is currently running. currentTools, err := environs.ReadTools(u.dataDir, version.Current) if err != nil { // Don't abort everything because we can't find the tools directory. // The problem should sort itself out as we will immediately // download some more tools and upgrade. log.Printf("cmd/jujud: upgrader cannot read current tools: %v", err) currentTools = &state.Tools{ Binary: version.Current, } } err = u.agentState.SetAgentTools(currentTools) if err != nil { return err } w := u.st.WatchEnvironConfig() defer watcher.Stop(w, &u.tomb) // Rather than using worker.WaitForEnviron, invalid environments are // managed explicitly so that all configuration changes are observed // by the loop below. var environ environs.Environ // TODO(rog) retry downloads when they fail. var ( download *downloader.Download downloadTools *state.Tools downloadDone <-chan downloader.Status ) // If we're killed early on (probably as a result of some other // task dying) we allow ourselves some time to try to connect to // the state and download a new version. We return to normal // undelayed behaviour when: // 1) We find there's no upgrade to do. // 2) A download fails. tomb := delayedTomb(&u.tomb, upgraderKillDelay) noDelay := func() { if tomb != &u.tomb { tomb.Kill(nil) tomb = &u.tomb } } for { // We wait for the tools to change while we're downloading // so that if something goes wrong (for instance a bad URL // hangs up) another change to the proposed tools can // potentially fix things. select { case cfg, ok := <-w.Changes(): if !ok { return watcher.MustErr(w) } var err error if environ == nil { environ, err = environs.New(cfg) if err != nil { log.Printf("cmd/jujud: upgrader loaded invalid initial environment configuration: %v", err) break } } else { err = environ.SetConfig(cfg) if err != nil { log.Printf("cmd/jujud: upgrader loaded invalid environment configuration: %v", err) // continue on, because the version number is still significant. } } vers := cfg.AgentVersion() if download != nil { // There's a download in progress, stop it if we need to. if vers == downloadTools.Number { // We are already downloading the requested tools. break } // Tools changed. We need to stop and restart. download.Stop() download, downloadTools, downloadDone = nil, nil, nil } // Ignore the proposed tools if we're already running the // proposed version. if vers == version.Current.Number { noDelay() break } binary := version.Current binary.Number = vers if tools, err := environs.ReadTools(u.dataDir, binary); err == nil { // The tools have already been downloaded, so use them. return u.upgradeReady(currentTools, tools) } flags := environs.CompatVersion if cfg.Development() { flags |= environs.DevVersion } tools, err := environs.FindTools(environ, binary, flags) if err != nil { log.Printf("cmd/jujud: upgrader error finding tools for %v: %v", binary, err) noDelay() // TODO(rog): poll until tools become available. break } if tools.Binary != binary { if tools.Number == version.Current.Number { // TODO(rog): poll until tools become available. log.Printf("cmd/jujud: upgrader: version %v requested but found only current version: %v", binary, tools.Number) noDelay() break } log.Printf("cmd/jujud: upgrader cannot find exact tools match for %s; using %s instead", binary, tools.Binary) } log.Printf("cmd/jujud: upgrader downloading %q", tools.URL) download = downloader.New(tools.URL, "") downloadTools = tools downloadDone = download.Done() case status := <-downloadDone: tools := downloadTools download, downloadTools, downloadDone = nil, nil, nil if status.Err != nil { log.Printf("cmd/jujud: upgrader download of %v failed: %v", tools.Binary, status.Err) noDelay() break } err := environs.UnpackTools(u.dataDir, tools, status.File) status.File.Close() if err := os.Remove(status.File.Name()); err != nil { log.Printf("cmd/jujud: upgrader cannot remove temporary download file: %v", err) } if err != nil { log.Printf("cmd/jujud: upgrader cannot unpack %v tools: %v", tools.Binary, err) noDelay() break } return u.upgradeReady(currentTools, tools) case <-tomb.Dying(): if download != nil { return fmt.Errorf("upgrader aborted download of %q", downloadTools.URL) } return nil } } panic("not reached") }