func (self *EtcdRouterRegistry) read(key string) (*etcdRouterData, error) { response, err := self.client.Get(key, false, false) if err != nil { etcdError, ok := err.(*etcd.EtcdError) if ok && etcdError.ErrorCode == etcdErrorKeyNotFound { log.Debug("Etcd key not found: %v", key) return nil, nil } log.Warn("Error reading key from etcd: %v", key, err) return nil, err } node := response.Node if node == nil || node.Value == "" { log.Info("No contents for key from etcd: %v", key) return nil, nil } decoded := &etcdRouterData{} err = json.Unmarshal([]byte(node.Value), decoded) if err != nil { log.Warn("Error parsing value from etcd: %v", node.Value, err) return nil, err } return decoded, nil }
func (self *EndpointBundle) HttpGet(huddle *core.Huddle) ([]*model.Instance, error) { tenant := self.Parent.Parent.Tenant bundleType := self.BundleType instances, err := huddle.ListInstances(tenant, bundleType) if err != nil { return nil, err } if instances == nil { return nil, nil } models := []*model.Instance{} for _, instance := range instances { model, err := instance.GetState() if err != nil { return nil, err } if model == nil { log.Debug("Ignoring concurrently deleted (?) instance: %v", instance) continue } models = append(models, model) } return models, nil }
func (self *BundleStore) GetSystemBundle(key string) (*Bundle, error) { log.Debug("Getting system bundle %v", key) template, err := self.GetBundleTemplate(key) if err != nil { return nil, err } if template == nil { return nil, nil } context := &TemplateContext{} config, err := template.executeTemplate(context) if err != nil { return nil, err } bundle, err := parseBundle(config) if err != nil { return nil, err } // bundle, err := getOnly(bundles) // if err != nil { // return nil, err // } return bundle, nil }
func waitReady(instance *core.Instance, timeout int) (bool, error) { ready := false for i := 0; i < timeout; i++ { state, err := instance.GetState() if err != nil { log.Warn("Error while waiting for instance to become ready", err) return false, err } if state == nil { log.Warn("Instance not yet created") continue } status := state.Status if status == "started" { ready = true break } time.Sleep(time.Second) if status == "pending" { log.Debug("Instance not ready; waiting", err) } else { log.Warn("Unknown instance status: %v", status) } } return ready, nil }
func parseReturn(out []reflect.Value) (reflect.Value, error) { var value reflect.Value var err error if len(out) >= 2 { // TODO: Don't assume position 1? errValue := out[1] if !errValue.IsNil() { var ok bool log.Debug("Got error value from rs method: %v", errValue) err, ok = errValue.Interface().(error) if !ok { err = fmt.Errorf("Unable to cast value to error") } } } if err == nil && len(out) > 0 { // TODO: Don't assume position 0 value = out[0] if !value.IsValid() { value = reflect.ValueOf(nil) } } return value, err }
func (self *Bundle) Deploy(jujuPrefix string, apiclient *juju.Client) (*DeployInfo, error) { log.Debug("Deploying bundle: %v", self) info := &DeployInfo{} info.Services = map[string]*DeployServiceInfo{} for key, service := range self.Services { serviceInfo, err := service.deploy(jujuPrefix+key, apiclient) if err != nil { return nil, err } info.Services[key] = serviceInfo } for _, relation := range self.Relations { prefixed := &RelationConfig{} prefixed.From = jujuPrefix + relation.From prefixed.To = jujuPrefix + relation.To err := prefixed.deploy(apiclient) if err != nil { return nil, err } } return info, nil }
func (self *EtcdRouterRegistry) listSubkeys(key string) ([]string, error) { response, err := self.client.Get(key, false, false) if err != nil { etcdError, ok := err.(*etcd.EtcdError) if ok && etcdError.ErrorCode == etcdErrorKeyNotFound { log.Debug("Etcd key not found: %v", key) return []string{}, nil } log.Warn("Error reading key from etcd: %v", key, err) return nil, err } if response == nil || response.Node == nil || response.Node.Nodes == nil { log.Info("No children for key from etcd: %v", key) return []string{}, nil } names := []string{} for _, node := range response.Node.Nodes { nodeKey := node.Key if !strings.HasPrefix(nodeKey, key) { return nil, fmt.Errorf("Key without expected prefix: %v vs %v", nodeKey, key) } suffix := nodeKey[len(key):] names = append(names, suffix) } return names, nil }
func (self *Huddle) ListInstances(tenant string, bundleType bundletype.BundleType) ([]*Instance, error) { prefix := self.jujuPrefix(tenant, bundleType) statuses, err := self.JujuClient.GetServiceStatusList(prefix) if err != nil { return nil, err } if statuses == nil { return nil, rs.HttpError(http.StatusNotFound) } instances := []*Instance{} for key, state := range statuses { _, bundleTypeId, instanceId, module, _, err := ParseUnit(key) if err != nil { log.Debug("Ignoring unparseable service: %v", key) continue } assert.That(bundleTypeId == bundleType.Key()) if module != bundleType.PrimaryJujuService() { continue } i := self.NewInstance(tenant, bundleType, instanceId) i.cacheState(&state) instances = append(instances, i) } return instances, nil }
func (self *RestEndpointHandler) buildArg(req *http.Request, t reflect.Type) (interface{}, error) { v, err := self.server.injector.Get(t) if err == nil && v != nil { return v, nil } // TODO: Fail if two args... // TODO: Only if has content? mediaType, err := getMediaType(req) if err != nil { return nil, err } if mediaType == nil { // Go does have a function to guess the media type, but that seems risky // Instead, use a fixed default mediaType = self.server.defaultMediaType } v, err = self.server.readMessageBody(t, req, mediaType) if err != nil { if err == io.EOF { log.Debug("Error reading message body (EOF)") } else { log.Debug("Error reading message body", err) } err = HttpError(http.StatusBadRequest) return nil, err } if v == nil && err == nil { err = HttpError(http.StatusUnsupportedMediaType) return nil, err } if v != nil { assert.Equal(reflect.TypeOf(v), t) return v, nil } log.Warn("Unable to bind parameter: %v", t) return nil, fmt.Errorf("Unable to bind parameter: %v", t) }
func (self *Scheduler) AddTask(task Runnable, interval time.Duration) *ScheduledTask { scheduledTask := &ScheduledTask{} scheduledTask.task = task scheduledTask.scheduler = self scheduledTask.interval = interval go scheduledTask.run() log.Debug("Scheduled task: %v for interval %v", task, interval) return scheduledTask }
func (self *Huddle) ListAllInstances() ([]*Instance, error) { prefix := "u" statuses, err := self.JujuClient.GetServiceStatusList(prefix) if err != nil { return nil, err } if statuses == nil { return nil, rs.HttpError(http.StatusNotFound) } instances := []*Instance{} for key, state := range statuses { tenant, bundleTypeId, instanceId, module, _, err := ParseUnit(key) if err != nil { log.Debug("Ignoring unparseable service: %v", key) continue } bundleType := self.System.GetBundleType(bundleTypeId) if bundleType == nil { log.Debug("Ignoring unknown bundle type: %v", bundleTypeId) continue } if module != bundleType.PrimaryJujuService() { continue } i := self.NewInstance(tenant, bundleType, instanceId) i.cacheState(&state) instances = append(instances, i) } return instances, nil }
func (self *JujuLogStore) ReadLog(service string, unitId int) (*JujuLog, error) { // TODO: Block path traversal filename := "unit-" + service + "-" + strconv.Itoa(unitId) + ".log" path := path.Join(self.BaseDir, filename) ok, err := files.Exists(path) if err != nil { return nil, err } if !ok { log.Debug("Log file not found: %v", path) return nil, nil } log := &JujuLog{} log.path = path return log, nil }
func (self *baseBundleType) BuildRelationInfo(templateContext *bundle.TemplateContext, bundle *bundle.Bundle, relationKey string) (*model.RelationInfo, error) { log.Info("BuildRelationInfo with %v", templateContext) // Find the properties the juju charm is exposing relationProperties := templateContext.Relations[relationKey] // Map those properties using the definition provideProperties := map[string]string{} if len(bundle.Provides) == 0 { // No explicit provides => derive automatically for k, v := range relationProperties { v = templateContext.GetSpecialProperty(relationKey, k, v) provideProperties[k] = v } // Auto-populate required properties that we generate required := []string{"protocol", "port"} for _, k := range required { v, found := relationProperties[k] if !found { v = templateContext.GetSpecialProperty(relationKey, k, v) } provideProperties[k] = v } } else { definition, found := bundle.Provides[relationKey] if !found { // Explicit provides, but no definition => no relation log.Debug("Request for relation, but no definition found: %v", relationKey) return nil, nil } for k, v := range definition.Properties { provideProperties[k] = v } } relationInfo := &model.RelationInfo{} if templateContext.Proxy != nil { relationInfo.PublicAddresses = []string{templateContext.Proxy.Host} } relationInfo.Properties = provideProperties return relationInfo, nil }
func (self *EndpointInstanceHealth) HttpGet() (*model.Health, error) { instance := self.Parent.getInstance() repair := false // TODO: Use state stored by scheduled health check, rather than running directly? health, err := instance.RunHealthCheck(repair) if err != nil { return nil, err } if health == nil { return nil, rs.ErrNotFound() } log.Debug("Health of %v: %v", instance, health) return health, nil }
func (self *BundleTemplate) executeTemplate(context *TemplateContext) (map[string]interface{}, error) { // t, err := template.New("bundle").Parse(templateString) // if err != nil { // return nil, err // } // log.Debug("Executing bundle template: %v", serviceType) var err error result, err := self.template.Render(context) if err != nil { log.Warn("Error applying template", err) return nil, err } log.Debug("Applied template: %v", result) resultMap, ok := result.(map[string]interface{}) if !ok { log.Warn("Template did not produce map type: %T", result) return nil, fmt.Errorf("Unexpected result from template") } // config := map[string]interface{}{} // err := goyaml.Unmarshal([]byte(yaml), &config) // if err != nil { // return nil, err // } // var buffer bytes.Buffer // err := self.template.Execute(&buffer, &templateContextCopy) // if err != nil { // return nil, err // } // yaml := buffer.String() // log.Debug("Bundle is:\n%v", yaml) return resultMap, nil }
func (self *ServiceHealthCheck) checkService(instance jxaas.Instance, serviceId string, repair bool, dest *model.Health) error { client := instance.GetJujuClient() command := "service " + self.ServiceName + " status" log.Info("Running command on %v: %v", serviceId, command) runResults, err := client.Run(serviceId, nil, command, 5*time.Second) if err != nil { return err } for _, runResult := range runResults { unitId := juju.ParseUnit(runResult.UnitId) code := runResult.Code stdout := string(runResult.Stdout) stderr := string(runResult.Stderr) log.Debug("Result: %v %v %v %v", runResult.UnitId, code, stdout, stderr) healthy := true if !strings.Contains(stdout, "start/running") { log.Info("Service %v not running on %v", serviceId, runResult.UnitId) healthy = false if repair { command := "service " + self.ServiceName + " start" log.Info("Running command on %v: %v", serviceId, command) _, err := client.Run(serviceId, []string{unitId}, command, 5*time.Second) if err != nil { return err } } } dest.Units[unitId] = healthy } return nil }
func (self *HealthCheckAllInstances) Run() error { instances, err := self.huddle.ListAllInstances() if err != nil { log.Warn("Error listing instances", err) return err } for _, instance := range instances { health, err := instance.RunHealthCheck(self.repair) if err != nil { log.Warn("Error running health check on instance: %v", instance, err) continue } // TODO: Check health results and mark instances unhealthy?? log.Debug("Health of %v: %v", instance, health) } return nil }
func (self *Client) PutRelation(from, to string) (*params.AddRelationResults, error) { results, err := self.client.AddRelation(from, to) if err != nil { jujuError, ok := err.(*params.Error) if ok { // There is no code :-( // if jujuError.Code == "relation already exists" { // return nil, nil // } if strings.HasSuffix(jujuError.Message, "relation already exists") { return nil, nil } log.Debug("Error while creating relation from %v to %v: Code=%v Message=%v", from, to, jujuError.Code, jujuError.Message) } return nil, err } return results, nil }
func (self *AutoScaleAllInstances) Run() error { instances, err := self.huddle.ListAllInstances() if err != nil { log.Warn("Error listing instances", err) return err } for _, instance := range instances { scaling, err := instance.RunScaling(true) if err != nil { log.Warn("Error running scaling on instance: %v", instance, err) continue } // TODO: Record this, so we can return scaling info from last poll through API log.Debug("Scaling-state of %v: %v", instance, scaling) } return nil }
// Deletes the instance. // This deletes all Juju services that make up the instance. func (self *Instance) Delete() error { jujuClient := self.GetJujuClient() prefix := self.jujuPrefix statuses, err := jujuClient.GetServiceStatusList(prefix) if err != nil { return err } for serviceId, _ := range statuses { log.Debug("Destroying service %v", serviceId) err = jujuClient.ServiceDestroy(serviceId) if err != nil { log.Warn("Error destroying service: %v", serviceId) return err } } // TODO: Wait for deletion // TODO: Remove machines return nil }
// Retrieves a specific metric-dataset for the instance func (self *Instance) GetMetricValues(key string) (*model.MetricDataset, error) { state, err := self.getState0() if err != nil { return nil, err } primaryServiceId := self.primaryServiceId jujuUnitNames := []string{} units := state.Units[primaryServiceId] for jujuUnitName, _ := range units { unitId := juju.ParseUnit(jujuUnitName) metricUnit := self.jujuPrefix + "metrics" + "/" + unitId jujuUnitNames = append(jujuUnitNames, metricUnit) } log.Debug("Searching with names: %v", jujuUnitNames) return self.readMetrics(jujuUnitNames, key) }
func (self *EndpointXaas) Item(key string, req *http.Request) (*EndpointTenant, error) { child := &EndpointTenant{} tenantId := key tenantName := strings.Replace(key, "-", "", -1) // TODO: Implement authz assert.That(self.Authenticator != nil) authentication := self.Authenticator.Authenticate(tenantId, req) if authentication == nil { log.Debug("Authentication failed") notAuthorized := rs.HttpError(http.StatusUnauthorized) notAuthorized.Headers["WWW-Authenticate"] = "Basic realm=\"jxaas\"" return nil, notAuthorized } else { child.Tenant = tenantName // TODO: Use tenantId? authorization.TenantId return child, nil } }
func NewEtcdRouterRegistry(etcdUrl *url.URL) (*EtcdRouterRegistry, error) { self := &EtcdRouterRegistry{} hosts := []string{} hosts = append(hosts, "http://"+etcdUrl.Host+":4001") log.Debug("Using etcd hosts: %v", hosts) self.client = etcd.NewClient(hosts) path := etcdUrl.Path _, err := self.client.CreateDir(path, 0) if err != nil { etcdError, ok := err.(*etcd.EtcdError) if !ok || etcdError.ErrorCode != etcdErrorAlreadyExists { log.Warn("Error creating path in etcd: %v", path, err) return nil, err } } self.basePath = etcdUrl.Path return self, nil }
func (self *OpenstackTokenAuthenticator) Authenticate(tenantSpec string, req *http.Request) *Authentication { var authorization *Authentication // Becaue the user has authenticated with keystone, we expect a tenant id tenantId := tenantSpec authTokens := req.Header["X-Auth-Token"] if len(authTokens) > 0 { authToken := strings.TrimSpace(authTokens[0]) log.Debug("Request to authenticate with token: %v in tenant: %v", authToken, tenantId) tenants, err := identity.ListTenantsForToken(self.keystoneEndpoint+"tenants", authToken, nil) if err != nil { log.Warn("Error authenticating against Openstack Identity", err) } else if tenants == nil { log.Warn("Tenants returned from Openstack identity was nil") } else { for _, tenant := range tenants { if tenant.Id == tenantId { if !tenant.Enabled { log.Warn("In project, but not enabled for project: %v", tenantId) continue } authorization = &Authentication{TenantId: tenant.Id, TenantName: tenant.Name} break } } if authorization == nil { log.Warn("Valid token, but not authorized for project: %v", tenantId) } } } return authorization }
// Assigns a public port to the serviceId func (self *Huddle) assignPublicPort(serviceId string) (int, bool, error) { self.mutex.Lock() defer self.mutex.Unlock() var port int port, found := self.assignedPublicPorts[serviceId] if found { return port, false, nil } // TODO: Filter? prefix := "" statuses, err := self.JujuClient.GetServiceStatusList(prefix) if err != nil { return 0, false, err } publicPorts := []int{} for _, publicPort := range self.assignedPublicPorts { publicPorts = append(publicPorts, publicPort) } for key, _ := range statuses { var publicPort int publicPort, found := self.assignedPublicPorts[key] if found { assert.That(contains(publicPorts, publicPort)) continue } log.Debug("Looking for public port annotation on: %v", key) annotations, err := self.JujuClient.GetServiceAnnotations(key) if err != nil { return 0, false, err } publicPortString := annotations[ANNOTATION_KEY_PUBLIC_PORT] publicPortString = strings.TrimSpace(publicPortString) if publicPortString == "" { continue } publicPort, err = strconv.Atoi(publicPortString) if err != nil { log.Warn("Error parsing public port on %v: %v", key, publicPortString, err) } self.assignedPublicPorts[key] = publicPort publicPorts = append(publicPorts, publicPort) } // This approach breaks down if the ports are densely assigned if len(publicPorts) > 9000 { return 0, false, fmt.Errorf("Too many ports already assigned") } for { port = 10000 + rand.Intn(10000) if contains(publicPorts, port) { continue } log.Debug("Public ports already assigned: %v", publicPorts) log.Info("Assigned port: %v", port) break } // We can't set the port yet; the service likely doesn't yet exist // err = self.Instance.setPublicPort(port) // if err != nil { // return 0, err // } // Instead we set the port in the map; this map is how we avoid double allocations before // we've created the service self.assignedPublicPorts[serviceId] = port return port, true, nil }
func (self *Instance) readMetrics(jujuUnitNames []string, metricId string) (*model.MetricDataset, error) { instance := self keyValue := metricId keyTimestamp := "Timestamp" keyHostname := "Hostname" keyType := "Type" huddle := instance.huddle es := huddle.SystemServices["elasticsearch"] if es == nil { return nil, rs.ErrNotFound() } if len(jujuUnitNames) == 0 { return nil, nil } // TODO: Inject // TODO: Use an ES client that isn't a singleton elasticgo_api.Domain = es.PublicAddress elasticgo_api.Port = "9200" // TODO: We need to make sure that most fields are _not_ analyzed // That is why we have match below, not term filters := []interface{}{} { match := map[string]string{} match[keyHostname] = jujuUnitNames[0] filter := map[string]interface{}{"query": map[string]interface{}{"match": match}} filters = append(filters, filter) } { // TODO: Hard-coded match := map[string]string{} match[keyType] = "LoadAverage" filter := map[string]interface{}{"query": map[string]interface{}{"match": match}} filters = append(filters, filter) } if len(filters) > 1 { and := map[string]interface{}{"and": filters} filters = []interface{}{and} } match_all := map[string]interface{}{"match_all": map[string]string{}} filtered := map[string]interface{}{"filter": filters[0], "query": match_all} query := map[string]interface{}{"filtered": filtered} body := map[string]interface{}{"query": query} args := map[string]interface{}{} args["size"] = 1000 response, err := elastigo_core.SearchRequest("_all", "message", args, body) if err != nil { log.Warn("Error searching elasticsearch", err) return nil, fmt.Errorf("Error searching elasticsearch") } metrics := &model.MetricDataset{} metrics.Points = []model.MetricDatapoint{} for _, hit := range response.Hits.Hits { // TODO: Are we serializing and deserializing here?? jsonBytes, err := hit.Source.MarshalJSON() if err != nil { log.Warn("Error reading JSON", err) return nil, fmt.Errorf("Error searching elasticsearch") } //log.Info("Found metric: %v", string(jsonBytes)) var value map[string]interface{} err = json.Unmarshal(jsonBytes, &value) if err != nil { log.Warn("Error unmarshalling response", err) return nil, fmt.Errorf("Error searching elasticsearch") } // Post-filter the ES results... // TODO: See if we can persuade ES to filter it correctly hostname, found := value[keyHostname] if !found { log.Debug("No hostname in %v", string(jsonBytes)) continue } hostnameStr, ok := hostname.(string) if !ok { log.Debug("Cannot cast hostname to string: %v", hostname) continue } if hostnameStr != jujuUnitNames[0] { log.Debug("Post-filtering query results from ES") continue } // Grab the timestamp & value t, found := value[keyTimestamp] if !found { log.Debug("No timestamp in %v", string(jsonBytes)) continue } tStr, ok := t.(string) if !ok { log.Debug("Cannot cast timestamp to string: %v", t) continue } timeFormat := time.RFC3339 tVal, err := time.Parse(timeFormat, tStr) if err != nil { log.Debug("Cannot parse timestamp: %v", tStr, err) continue } y, found := value[keyValue] if !found { log.Debug("No value (%v) in %v", keyValue, string(jsonBytes)) continue } yStr, ok := y.(string) if !ok { log.Debug("Cannot cast value to string: %v", y) continue } yVal, err := strconv.ParseFloat(yStr, 32) if err != nil { log.Debug("Error parsing value as float: %v", yStr, err) continue } p := model.MetricDatapoint{} p.T = tVal.Unix() p.V = float32(yVal) metrics.Points = append(metrics.Points, p) } // fmt.Println(values) sort.Sort(metrics.Points) return metrics, nil }
// Runs a health check on the instance func (self *Instance) RunHealthCheck(repair bool) (*model.Health, error) { jujuClient := self.GetJujuClient() state, err := self.getState0() if err != nil { return nil, err } if state == nil { return nil, rs.ErrNotFound() } if state.Model == nil { log.Debug("No model for %v", self) return nil, rs.ErrNotFound() } if state.Model.Status != "started" { log.Info("Skipping health check on not-yet started instance (state %v): %s", state.Model.Status, self) return nil, nil } services, err := jujuClient.GetServiceStatusList(self.jujuPrefix) if err != nil { return nil, err } if services == nil || len(services) == 0 { return nil, rs.ErrNotFound() } bundle, err := self.getCurrentBundle(state) if err != nil { return nil, err } health := &model.Health{} health.Units = map[string]bool{} healthChecks, err := self.bundleType.GetHealthChecks(bundle) if err != nil { return nil, err } // TODO: We can't "juju run" on subordinate charms // charm := self.huddle.getCharmInfo(service.Charm) // // if charm.Subordinate { // continue // } for healthCheckId, healthCheck := range healthChecks { result, err := healthCheck.Run(self, services, repair) if err != nil { log.Info("Health check %v failed", healthCheckId, err) return nil, err } for k, healthy := range result.Units { overall, exists := health.Units[k] if !exists { overall = true } health.Units[k] = overall && healthy } } return health, nil }
func (self *ServiceConfig) deploy(jujuServiceId string, apiclient *juju.Client) (*DeployServiceInfo, error) { serviceInfo := &DeployServiceInfo{} jujuService, err := apiclient.FindService(jujuServiceId) if err != nil { return nil, err } charmUrl := self.Charm charmInfo, err := apiclient.CharmInfo(charmUrl) if err != nil { log.Warn("Error reading charm: %v", charmUrl, err) } if charmInfo == nil { log.Warn("Unable to find charm: %v", charmUrl) } charmUrl = charmInfo.URL if jujuService == nil { // Create new service numUnits := self.NumberUnits if charmInfo.Meta.Subordinate { numUnits = -1 } configYaml, err := makeConfigYaml(jujuServiceId, self.Options) if err != nil { return nil, err } log.Debug("Deploying with YAML: %v", configYaml) err = apiclient.ServiceDeploy( charmUrl, jujuServiceId, numUnits, configYaml) if err != nil { return nil, err } // for retry := 0; retry < 5; retry++ { // status, err := apiclient.GetStatus(jujuServiceId) // if err != nil { // return err // } // if status != nil { // break // } // log.Info("Service was not yet visible; waiting") // time.Sleep(1 * time.Second) // } } else { existingInstance := model.MapToInstance(jujuServiceId, nil, jujuService) existingServiceOptions := existingInstance.Options mergedServiceOptions := map[string]string{} { for key, value := range existingServiceOptions { mergedServiceOptions[key] = value } for key, value := range self.Options { mergedServiceOptions[key] = value } } if !reflect.DeepEqual(existingServiceOptions, mergedServiceOptions) { err = apiclient.SetConfig(jujuServiceId, mergedServiceOptions) if err != nil { return nil, err } } else { log.Debug("Configuration unchanged; won't reconfigure") } } if !charmInfo.Meta.Subordinate { // && self.Exposed != nil { status, err := apiclient.GetServiceStatus(jujuServiceId) if err != nil { return nil, err } if status == nil { return nil, fmt.Errorf("Service not found: %v", jujuServiceId) } serviceInfo.Status = status if status.Exposed != self.Exposed { err = apiclient.SetExposed(jujuServiceId, self.Exposed) if err != nil { log.Warn("Error setting service to Exposed=%v", self.Exposed, err) return nil, err } } actualUnits := len(status.Units) wantUnits := self.NumberUnits if actualUnits != wantUnits { if actualUnits < wantUnits { _, err = apiclient.AddServiceUnits(jujuServiceId, wantUnits-actualUnits) if err != nil { log.Warn("Error adding units", err) } } else { keys := []string{} for key, _ := range status.Units { keys = append(keys, key) } sort.Strings(keys) // TODO: Be more intelligent about which unit to kill? victims := keys[wantUnits:len(keys)] for _, victim := range victims { slash := strings.Index(victim, "/") unitId, err := strconv.Atoi(victim[slash+1:]) if err != nil { log.Warn("Error parsing UnitId: %v", victim) return nil, err } err = apiclient.DestroyUnit(jujuServiceId, unitId) if err != nil { log.Warn("Error removing unit: %v/%v", jujuServiceId, unitId, err) return nil, err } } } } } // for _, openPort := range self.OpenPorts { // apiclient.Run(jujuServiceId, nil, ["open-port", openPort]) // //// err = apiclient.OpenPort(jujuServiceId, openPort) // if err != nil { // log.Warn("Error opening port: %v/%v", jujuServiceId, openPort, err) // return nil, err // } // } return serviceInfo, nil }
// Returns the current state of the instance func (self *Instance) getState0() (*instanceState, error) { jujuClient := self.GetJujuClient() primaryServiceId := self.primaryServiceId status, err := jujuClient.GetServiceStatus(primaryServiceId) // TODO: check err? jujuService, err := jujuClient.FindService(primaryServiceId) if err != nil { return nil, err } if status == nil { log.Warn("No state found for %v", primaryServiceId) return nil, nil } log.Debug("Service state: %v", status) state := &instanceState{} state.Model = model.MapToInstance(self.instanceId, status, jujuService) for k, v := range self.bundleType.GetDefaultOptions() { option, found := state.Model.OptionDescriptions[k] if !found { log.Warn("Option not found in OptionDescriptions %v in %v", k, state.Model.OptionDescriptions) continue } option.Default = v state.Model.OptionDescriptions[k] = option } state.Units = map[string]map[string]api.UnitStatus{} state.Units[primaryServiceId] = status.Units state.PublicAddresses = []string{} for _, unitStatus := range status.Units { if unitStatus.PublicAddress == "" { continue } state.PublicAddresses = append(state.PublicAddresses, unitStatus.PublicAddress) } serviceKeys, err := self.getBundleKeys() if err != nil { return nil, err } if serviceKeys == nil { return nil, rs.ErrNotFound() } // TODO: This is pretty expensive... we could just check to see if properties have been set for serviceId, _ := range serviceKeys { if serviceId == primaryServiceId { continue } status, err := jujuClient.GetServiceStatus(serviceId) if err != nil { log.Warn("Error while fetching status of service: %v", serviceId, err) state.Model.Status = "pending" } else if status == nil { log.Warn("No status for service: %v", serviceId) state.Model.Status = "pending" } else { log.Info("Got state of secondary service: %v => %v", serviceId, status) for _, unitStatus := range status.Units { model.MergeInstanceStatus(state.Model, &unitStatus) } } if status != nil { state.Units[serviceId] = status.Units } } // TODO: This is a bit of a hack also. How should we wait for properties to be set? annotations, err := jujuClient.GetServiceAnnotations(primaryServiceId) if err != nil { log.Warn("Error getting annotations", err) // TODO: Mask error? return nil, err } log.Info("Annotations on %v: %v", primaryServiceId, annotations) state.Model.Options = map[string]string{} state.SystemProperties = map[string]string{} state.RelationMetadata = map[string]string{} relationList := []relationProperty{} for tagName, v := range annotations { if strings.HasPrefix(tagName, ANNOTATION_PREFIX_INSTANCECONFIG) { key := tagName[len(ANNOTATION_PREFIX_INSTANCECONFIG):] state.Model.Options[key] = v continue } if strings.HasPrefix(tagName, ANNOTATION_PREFIX_SYSTEM) { key := tagName[len(ANNOTATION_PREFIX_SYSTEM):] state.SystemProperties[key] = v continue } if strings.HasPrefix(tagName, ANNOTATION_PREFIX_RELATIONINFO) { suffix := tagName[len(ANNOTATION_PREFIX_RELATIONINFO):] tokens := strings.SplitN(suffix, "_", 3) if len(tokens) < 3 { log.Debug("Ignoring unparseable tag: %v", tagName) continue } unitId := tokens[0] relationId := tokens[1] key := tokens[2] if key[0] != '_' { state.RelationMetadata[key] = v continue } relationTokens := strings.SplitN(relationId, ":", 2) if len(relationTokens) != 2 { log.Debug("Ignoring unparseable relation id in tag: %v", tagName) continue } relationProperty := relationProperty{} relationProperty.UnitId = unitId assert.That(key[0] == '_') relationProperty.Key = key[1:] relationProperty.Value = v relationProperty.RelationType = relationTokens[0] relationProperty.RelationKey = relationTokens[1] relationList = append(relationList, relationProperty) continue } } state.Relations = map[string]map[string]string{} for _, relation := range relationList { relationType := relation.RelationType relations, found := state.Relations[relationType] if !found { relations = map[string]string{} state.Relations[relationType] = relations } relations[relation.Key] = relation.Value } // TODO: Only if otherwise ready? annotationsReady := self.bundleType.IsStarted(state.Relations) // For a subordinate charm service (e.g. multimysql), we just watch for the annotation if annotationsReady && state.Model.Status == "" && len(status.SubordinateTo) != 0 { log.Info("Subordinate instance started (per annotations): %v", self) state.Model.Status = "started" } if !annotationsReady { log.Info("Instance not started (per annotations): %v", state.Relations) state.Model.Status = "pending" } log.Info("Status of %v: %v", primaryServiceId, state.Model.Status) // TODO: Fetch inherited properties from primary service and merge return state, nil }
// Authenticate against Openstack using basic auth func (self *OpenstackBasicAuthenticator) Authenticate(tenantSpec string, req *http.Request) *Authentication { var authorization *Authentication // Because the user hasn't authenticated with keystone, we assume tenantSpec is actually a tenant _name_ here, // not a tenant id tenantName := tenantSpec authorizationHeaders := req.Header["Authorization"] if len(authorizationHeaders) > 0 { authorizationHeader := strings.TrimSpace(authorizationHeaders[0]) tokens := strings.SplitN(authorizationHeader, " ", 2) if len(tokens) == 2 && tokens[0] == "Basic" { payload, _ := base64.StdEncoding.DecodeString(tokens[1]) usernameAndPassword := strings.SplitN(string(payload), ":", 2) if len(usernameAndPassword) == 2 { username := usernameAndPassword[0] password := usernameAndPassword[1] log.Debug("Request to authenticate as: %v in tenant %v", username, tenantName) authenticator := identity.NewAuthenticator(identity.AuthUserPass, nil) creds := identity.Credentials{TenantName: tenantName, User: username, URL: self.keystoneEndpoint + "tokens", Secrets: password} auth, err := authenticator.Auth(&creds) if err != nil { if errors.IsUnauthorised(err) { log.Debug("Openstack Identity rejected the authentication request (401)") } else { log.Warn("Error authenticating against Openstack Identity", err) } } else if auth == nil { log.Warn("Auth returned from Openstack identity was nil") } else { if auth.TenantId != "" { authorization = &Authentication{TenantId: auth.TenantId, TenantName: auth.TenantName} } // We don't _need_ to use TenantName based auth; we could retrieve the tenants like this... // tenants, err := identity.ListTenantsForToken(self.keystoneEndpoint+"tenants", auth.Token, nil) // if err != nil { // log.Warn("Unable to fetch tenants for token", err) // } else { // log.Debug("Got tenants: %v", tenants) // for _, tenant := range tenants { // if tenant.Name == tenantName { // authorization = &Authentication{TenantId: tenant.Id, TenantName: tenant.Name} // break // } // } // if authorization == nil { // log.Debug("Authenticated with keystone, but not authorized for project: %v", tenantName) // } // } } } } } return authorization }