// SendMetrics will send any unsent metrics onto the metric collection service. func (api *MetricsManagerAPI) SendMetrics(args params.Entities) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canAccess, err := api.accessEnviron() if err != nil { return result, err } for i, arg := range args.Entities { tag, err := names.ParseEnvironTag(arg.Tag) if err != nil { result.Results[i].Error = common.ServerError(err) continue } if !canAccess(tag) { result.Results[i].Error = common.ServerError(common.ErrPerm) continue } err = metricsender.SendMetrics(api.state, sender, maxBatchesPerSend) if err != nil { err = errors.Annotate(err, "failed to send metrics") logger.Warningf("%v", err) result.Results[i].Error = common.ServerError(err) continue } } return result, nil }
// CleanupOldMetrics removes old metrics from the collection. // The single arg params is expected to contain and environment uuid. // Even though the call will delete all metrics across environments // it serves to validate that the connection has access to at least one environment. func (api *MetricsManagerAPI) CleanupOldMetrics(args params.Entities) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canAccess, err := api.accessEnviron() if err != nil { return result, err } for i, arg := range args.Entities { tag, err := names.ParseEnvironTag(arg.Tag) if err != nil { result.Results[i].Error = common.ServerError(common.ErrPerm) continue } if !canAccess(tag) { result.Results[i].Error = common.ServerError(common.ErrPerm) continue } err = api.state.CleanupOldMetrics() if err != nil { err = errors.Annotate(err, "failed to cleanup old metrics") result.Results[i].Error = common.ServerError(err) } } return result, nil }
// EnvironmentStatus returns a status summary for each environment tag passed in. func (c *Client) EnvironmentStatus(tags ...names.EnvironTag) ([]base.EnvironmentStatus, error) { result := params.EnvironmentStatusResults{} envs := make([]params.Entity, len(tags)) for i, tag := range tags { envs[i] = params.Entity{Tag: tag.String()} } req := params.Entities{ Entities: envs, } if err := c.facade.FacadeCall("EnvironmentStatus", req, &result); err != nil { return nil, err } results := make([]base.EnvironmentStatus, len(result.Results)) for i, r := range result.Results { env, err := names.ParseEnvironTag(r.EnvironTag) if err != nil { return nil, errors.Annotatef(err, "EnvironTag %q at position %d", r.EnvironTag, i) } owner, err := names.ParseUserTag(r.OwnerTag) if err != nil { return nil, errors.Annotatef(err, "OwnerTag %q at position %d", r.OwnerTag, i) } results[i] = base.EnvironmentStatus{ UUID: env.Id(), Life: r.Life, Owner: owner.Canonical(), HostedMachineCount: r.HostedMachineCount, ServiceCount: r.ServiceCount, } } return results, nil }
// cacheAPIInfo updates the local environment settings (.jenv file) // with the provided apiInfo, assuming we've just successfully // connected to the API server. func cacheAPIInfo(info configstore.EnvironInfo, apiInfo *api.Info) (err error) { defer errors.Contextf(&err, "failed to cache API credentials") environUUID := "" if apiInfo.EnvironTag != "" { tag, err := names.ParseEnvironTag(apiInfo.Tag) if err != nil { return err } environUUID = tag.Id() } info.SetAPIEndpoint(configstore.APIEndpoint{ Addresses: apiInfo.Addrs, CACert: string(apiInfo.CACert), EnvironUUID: environUUID, }) tag, err := names.ParseUserTag(apiInfo.Tag) if err != nil { return err } info.SetAPICredentials(configstore.APICredentials{ User: tag.Id(), Password: apiInfo.Password, }) return info.Write() }
func (s *environSuite) TestParseEnvironTag(c *gc.C) { for i, t := range parseEnvironTagTests { c.Logf("test %d: %s", i, t.tag) got, err := names.ParseEnvironTag(t.tag) if err != nil || t.err != nil { c.Check(err, gc.DeepEquals, t.err) continue } c.Check(got, gc.FitsTypeOf, t.expected) c.Check(got, gc.Equals, t.expected) } }
// EnvironmentUUID returns the environment UUID from the client connection. func (c *Client) EnvironmentUUID() string { value := c.st.EnvironTag() if value != "" { tag, err := names.ParseEnvironTag(value) if err != nil { logger.Warningf("environ tag not an environ: %v", err) return "" } return tag.Id() } return "" }
// EnsureAvailabilitySingle applies a single StateServersSpec specification to the current environment. // Exported so it can be called by the legacy client API in the client package. func EnsureAvailabilitySingle(st *state.State, spec params.StateServersSpec) (params.StateServersChanges, error) { if !st.IsStateServer() { return params.StateServersChanges{}, errors.New("unsupported with hosted environments") } // Check if changes are allowed and the command may proceed. blockChecker := common.NewBlockChecker(st) if err := blockChecker.ChangeAllowed(); err != nil { return params.StateServersChanges{}, errors.Trace(err) } // Validate the environment tag if present. if spec.EnvironTag != "" { tag, err := names.ParseEnvironTag(spec.EnvironTag) if err != nil { return params.StateServersChanges{}, errors.Errorf("invalid environment tag: %v", err) } if _, err := st.FindEntity(tag); err != nil { return params.StateServersChanges{}, err } } series := spec.Series if series == "" { ssi, err := st.StateServerInfo() if err != nil { return params.StateServersChanges{}, err } // We should always have at least one voting machine // If we *really* wanted we could just pick whatever series is // in the majority, but really, if we always copy the value of // the first one, then they'll stay in sync. if len(ssi.VotingMachineIds) == 0 { // Better than a panic()? return params.StateServersChanges{}, fmt.Errorf("internal error, failed to find any voting machines") } templateMachine, err := st.Machine(ssi.VotingMachineIds[0]) if err != nil { return params.StateServersChanges{}, err } series = templateMachine.Series() } changes, err := st.EnsureAvailability(spec.NumStateServers, spec.Constraints, series, spec.Placement) if err != nil { return params.StateServersChanges{}, err } return stateServersChanges(changes), nil }
func (c *ControllerAPI) environStatus(tag string) (params.EnvironmentStatus, error) { var status params.EnvironmentStatus envTag, err := names.ParseEnvironTag(tag) if err != nil { return status, errors.Trace(err) } st, err := c.state.ForEnviron(envTag) if err != nil { return status, errors.Trace(err) } defer st.Close() machines, err := st.AllMachines() if err != nil { return status, errors.Trace(err) } var hostedMachines []*state.Machine for _, m := range machines { if !m.IsManager() { hostedMachines = append(hostedMachines, m) } } services, err := st.AllServices() if err != nil { return status, errors.Trace(err) } env, err := st.Environment() if err != nil { return status, errors.Trace(err) } if err != nil { return status, errors.Trace(err) } return params.EnvironmentStatus{ EnvironTag: tag, OwnerTag: env.Owner().String(), Life: params.Life(env.Life().String()), HostedMachineCount: len(hostedMachines), ServiceCount: len(services), }, nil }
// cacheChangedAPIInfo updates the local environment settings (.jenv file) // with the provided API server addresses if they have changed. It will also // save the environment tag if it is available. func cacheChangedAPIInfo(info configstore.EnvironInfo, hostPorts [][]network.HostPort, newEnvironTag string) error { var addrs []string for _, serverHostPorts := range hostPorts { for _, hostPort := range serverHostPorts { // Only cache addresses that are likely to be usable, // exclude localhost style ones. if hostPort.Scope != network.ScopeMachineLocal && hostPort.Scope != network.ScopeLinkLocal { addrs = append(addrs, hostPort.NetAddr()) } } } endpoint := info.APIEndpoint() changed := false if newEnvironTag != "" { tag, err := names.ParseEnvironTag(newEnvironTag) if err == nil { if environUUID := tag.Id(); endpoint.EnvironUUID != environUUID { changed = true endpoint.EnvironUUID = environUUID } } else { logger.Debugf("cannot parse environ tag: %v", err) } } if len(addrs) != 0 && addrsChanged(endpoint.Addresses, addrs) { logger.Debugf("API addresses changed from %q to %q", endpoint.Addresses, addrs) changed = true endpoint.Addresses = addrs } if !changed { return nil } info.SetAPIEndpoint(endpoint) if err := info.Write(); err != nil { return err } logger.Infof("updated API connection settings cache") return nil }
// ensureAvailabilitySingle applies a single StateServersSpec specification to the current environment. func (c *Client) ensureAvailabilitySingle(spec params.StateServersSpec) (params.StateServersChanges, error) { // Validate the environment tag if present. if spec.EnvironTag != "" { tag, err := names.ParseEnvironTag(spec.EnvironTag) if err != nil { return params.StateServersChanges{}, errors.Errorf("invalid environment tag: %v", err) } if _, err := c.api.state.FindEntity(tag); err != nil { return params.StateServersChanges{}, err } } series := spec.Series if series == "" { ssi, err := c.api.state.StateServerInfo() if err != nil { return params.StateServersChanges{}, err } // We should always have at least one voting machine // If we *really* wanted we could just pick whatever series is // in the majority, but really, if we always copy the value of // the first one, then they'll stay in sync. if len(ssi.VotingMachineIds) == 0 { // Better than a panic()? return params.StateServersChanges{}, fmt.Errorf("internal error, failed to find any voting machines") } templateMachine, err := c.api.state.Machine(ssi.VotingMachineIds[0]) if err != nil { return params.StateServersChanges{}, err } series = templateMachine.Series() } changes, err := c.api.state.EnsureAvailability(spec.NumStateServers, spec.Constraints, series) if err != nil { return params.StateServersChanges{}, err } return stateServersChanges(changes), nil }
// ServerTag returns the tag of the server we are connected to. func (s *State) ServerTag() (names.EnvironTag, error) { return names.ParseEnvironTag(s.serverTag) }
// EnvironTag returns the tag of the environment we are connected to. func (s *State) EnvironTag() (names.EnvironTag, error) { return names.ParseEnvironTag(s.environTag) }
// ControllerTag returns the tag of the server we are connected to. func (s *state) ControllerTag() (names.EnvironTag, error) { return names.ParseEnvironTag(s.controllerTag) }
func (formatter_1_18) unmarshal(data []byte) (*configInternal, error) { // NOTE: this needs to handle the absence of StatePort and get it from the // address var format format_1_18Serialization if err := goyaml.Unmarshal(data, &format); err != nil { return nil, err } if format.UpgradedToVersion == nil || *format.UpgradedToVersion == version.Zero { // Assume we upgrade from 1.16. upgradedToVersion := version.MustParse("1.16.0") format.UpgradedToVersion = &upgradedToVersion } tag, err := names.ParseTag(format.Tag) if err != nil { return nil, err } var envTag names.EnvironTag if format.Environment != "" { envTag, err = names.ParseEnvironTag(format.Environment) if err != nil { return nil, errors.Trace(err) } } config := &configInternal{ tag: tag, paths: NewPathsWithDefaults(Paths{ DataDir: format.DataDir, LogDir: format.LogDir, MetricsSpoolDir: format.MetricsSpoolDir, }), jobs: format.Jobs, upgradedToVersion: *format.UpgradedToVersion, nonce: format.Nonce, environment: envTag, caCert: format.CACert, oldPassword: format.OldPassword, values: format.Values, preferIPv6: format.PreferIPv6, } if len(format.StateAddresses) > 0 { config.stateDetails = &connectionDetails{ format.StateAddresses, format.StatePassword, } } if len(format.APIAddresses) > 0 { config.apiDetails = &connectionDetails{ format.APIAddresses, format.APIPassword, } } if len(format.StateServerKey) != 0 { config.servingInfo = ¶ms.StateServingInfo{ Cert: format.StateServerCert, PrivateKey: format.StateServerKey, CAPrivateKey: format.CAPrivateKey, APIPort: format.APIPort, StatePort: format.StatePort, SharedSecret: format.SharedSecret, SystemIdentity: format.SystemIdentity, } // There's a private key, then we need the state port, // which wasn't always in the 1.18 format. If it's not present // we can infer it from the ports in the state addresses. if config.servingInfo.StatePort == 0 { if len(format.StateAddresses) == 0 { return nil, fmt.Errorf("server key found but no state port") } _, portString, err := net.SplitHostPort(format.StateAddresses[0]) if err != nil { return nil, err } statePort, err := strconv.Atoi(portString) if err != nil { return nil, err } config.servingInfo.StatePort = statePort } } return config, nil }
func (s *fakeHomeSuite) TestEnvironmentTagValid(c *gc.C) { asString := testing.EnvironmentTag.String() tag, err := names.ParseEnvironTag(asString) c.Assert(err, jc.ErrorIsNil) c.Assert(tag, gc.Equals, testing.EnvironmentTag) }
func Open(info *Info, opts DialOpts) (*State, error) { if len(info.Addrs) == 0 { return nil, fmt.Errorf("no API addresses to connect to") } pool := x509.NewCertPool() xcert, err := cert.ParseCert(info.CACert) if err != nil { return nil, err } pool.AddCert(xcert) var environUUID string if info.EnvironTag != "" { tag, err := names.ParseEnvironTag(info.EnvironTag) if err != nil { return nil, err } environUUID = tag.Id() } // Dial all addresses at reasonable intervals. try := parallel.NewTry(0, nil) defer try.Kill() var addrs []string for _, addr := range info.Addrs { if strings.HasPrefix(addr, "localhost:") { addrs = append(addrs, addr) break } } if len(addrs) == 0 { addrs = info.Addrs } for _, addr := range addrs { err := dialWebsocket(addr, environUUID, opts, pool, try) if err == parallel.ErrStopped { break } if err != nil { return nil, err } select { case <-time.After(opts.DialAddressInterval): case <-try.Dead(): } } try.Close() result, err := try.Result() if err != nil { return nil, err } conn := result.(*websocket.Conn) logger.Infof("connection established to %q", conn.RemoteAddr()) client := rpc.NewConn(jsoncodec.NewWebsocket(conn), nil) client.Start() st := &State{ client: client, conn: conn, addr: conn.Config().Location.Host, serverRoot: "https://" + conn.Config().Location.Host, tag: info.Tag, password: info.Password, certPool: pool, } if info.Tag != "" || info.Password != "" { if err := st.Login(info.Tag, info.Password, info.Nonce); err != nil { conn.Close() return nil, err } } st.broken = make(chan struct{}) st.closed = make(chan struct{}) go st.heartbeatMonitor() return st, nil }