コード例 #1
0
ファイル: etcd_router_registry.go プロジェクト: jxaas/jxaas
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
}
コード例 #2
0
ファイル: bundle.go プロジェクト: jxaas/jxaas
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
}
コード例 #3
0
ファイル: bundles.go プロジェクト: jxaas/jxaas
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
}
コード例 #4
0
ファイル: utils.go プロジェクト: jxaas/jxaas
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
}
コード例 #5
0
ファイル: rs.go プロジェクト: justinsb/gova
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
}
コード例 #6
0
ファイル: deploy.go プロジェクト: jxaas/jxaas
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
}
コード例 #7
0
ファイル: etcd_router_registry.go プロジェクト: jxaas/jxaas
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
}
コード例 #8
0
ファイル: huddle.go プロジェクト: jxaas/jxaas
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
}
コード例 #9
0
ファイル: rs.go プロジェクト: justinsb/gova
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)
}
コード例 #10
0
ファイル: scheduler.go プロジェクト: jxaas/jxaas
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
}
コード例 #11
0
ファイル: huddle.go プロジェクト: jxaas/jxaas
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
}
コード例 #12
0
ファイル: logs.go プロジェクト: jxaas/jxaas
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
}
コード例 #13
0
ファイル: bundletype.go プロジェクト: jxaas/jxaas
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
}
コード例 #14
0
ファイル: instance_health.go プロジェクト: jxaas/jxaas
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
}
コード例 #15
0
ファイル: store.go プロジェクト: jxaas/jxaas
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
}
コード例 #16
0
ファイル: servicecheck.go プロジェクト: jxaas/jxaas
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
}
コード例 #17
0
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
}
コード例 #18
0
ファイル: connection.go プロジェクト: jxaas/jxaas
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
}
コード例 #19
0
ファイル: auto_scaling.go プロジェクト: jxaas/jxaas
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
}
コード例 #20
0
ファイル: instance.go プロジェクト: jxaas/jxaas
// 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
}
コード例 #21
0
ファイル: metrics.go プロジェクト: jxaas/jxaas
// 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)
}
コード例 #22
0
ファイル: root.go プロジェクト: jxaas/jxaas
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
	}
}
コード例 #23
0
ファイル: etcd_router_registry.go プロジェクト: jxaas/jxaas
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
}
コード例 #24
0
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
}
コード例 #25
0
ファイル: huddle.go プロジェクト: jxaas/jxaas
// 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
}
コード例 #26
0
ファイル: metrics.go プロジェクト: jxaas/jxaas
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
}
コード例 #27
0
ファイル: instance.go プロジェクト: jxaas/jxaas
// 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
}
コード例 #28
0
ファイル: deploy.go プロジェクト: jxaas/jxaas
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
}
コード例 #29
0
ファイル: instance.go プロジェクト: jxaas/jxaas
// 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
}
コード例 #30
0
// 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
}