// servePutLeader sets the leader for a service. func (h *Handler) servePutLeader(w http.ResponseWriter, r *http.Request, params httprouter.Params) { // Retrieve path parameters. service := params.ByName("service") // Check if the service allows manual leader election. config := h.Store.Config(service) if config == nil || config.LeaderType != discoverd.LeaderTypeManual { hh.ValidationError(w, "", "service leader election type is not manual") return } // Read instance from the request. inst := &discoverd.Instance{} if err := hh.DecodeJSON(r, inst); err != nil { hh.Error(w, err) return } // Manually set the leader on the service. if err := h.Store.SetServiceLeader(service, inst.ID); err == ErrNotLeader { h.redirectToLeader(w, r) return } else if err != nil { hh.Error(w, err) return } }
func (h *jobAPI) Update(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { log := h.host.log.New("fn", "Update") log.Info("decoding command") var cmd host.Command if err := httphelper.DecodeJSON(req, &cmd); err != nil { log.Error("error decoding command", "err", err) httphelper.Error(w, err) return } log.Info("updating host") err := h.host.Update(&cmd) if err != nil { httphelper.Error(w, err) return } // send an ok response and then shutdown after 1s to give the response // chance to reach the client. httphelper.JSON(w, http.StatusOK, cmd) log.Info("shutting down in 1s") time.AfterFunc(time.Second, func() { log.Info("exiting") os.Exit(0) }) }
func (h *jobAPI) ResourceCheck(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var req host.ResourceCheck if err := httphelper.DecodeJSON(r, &req); err != nil { httphelper.Error(w, err) return } var conflicts []host.Port for _, p := range req.Ports { if p.Proto == "" { p.Proto = "tcp" } if !checkPort(p) { conflicts = append(conflicts, p) } } if len(conflicts) > 0 { resp := host.ResourceCheck{Ports: conflicts} detail, err := json.Marshal(resp) if err != nil { httphelper.Error(w, err) return } httphelper.JSON(w, 409, &httphelper.JSONError{ Code: httphelper.ConflictErrorCode, Message: "Conflicting resources found", Detail: detail, }) return } httphelper.JSON(w, 200, struct{}{}) }
func (api *HTTPAPI) Pull(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { volumeID := ps.ByName("volume_id") pull := &volume.PullCoordinate{} if err := httphelper.DecodeJSON(r, &pull); err != nil { httphelper.Error(w, err) return } hostClient, err := api.cluster.Host(pull.HostID) if err != nil { httphelper.Error(w, err) return } haves, err := api.vman.ListHaves(volumeID) if err != nil { httphelper.Error(w, err) return } reader, err := hostClient.SendSnapshot(pull.SnapshotID, haves) if err != nil { httphelper.Error(w, err) return } snap, err := api.vman.ReceiveSnapshot(volumeID, reader) if err != nil { httphelper.Error(w, err) return } httphelper.JSON(w, 200, snap.Info()) }
// servePutService creates a service. func (h *Handler) servePutService(w http.ResponseWriter, r *http.Request, params httprouter.Params) { // Retrieve the path parameter. service := params.ByName("service") if err := ValidServiceName(service); err != nil { hh.ValidationError(w, "", err.Error()) return } // Read config from the request. config := &discoverd.ServiceConfig{} if err := hh.DecodeJSON(r, config); err != nil { hh.Error(w, err) return } // Add the service to the store. if err := h.Store.AddService(service, config); err == ErrNotLeader { h.redirectToLeader(w, r) return } else if IsServiceExists(err) { hh.ObjectExistsError(w, err.Error()) return } else if err != nil { hh.Error(w, err) return } }
func (c *controllerAPI) PutResource(ctx context.Context, w http.ResponseWriter, req *http.Request) { params, _ := ctxhelper.ParamsFromContext(ctx) p, err := c.getProvider(ctx) if err != nil { respondWithError(w, err) return } var resource ct.Resource if err = httphelper.DecodeJSON(req, &resource); err != nil { respondWithError(w, err) return } resource.ID = params.ByName("resources_id") resource.ProviderID = p.ID if err := schema.Validate(resource); err != nil { respondWithError(w, err) return } if err := c.resourceRepo.Add(&resource); err != nil { respondWithError(w, err) return } httphelper.JSON(w, 200, &resource) }
func (api *httpAPI) NewCredential(w http.ResponseWriter, req *http.Request, params httprouter.Params) { creds := &Credential{} if err := httphelper.DecodeJSON(req, &creds); err != nil { httphelper.Error(w, err) return } if creds.Type == "azure" { oauthCreds := make([]*OAuthCredential, 0, 2) for _, resource := range []string{azure.JSONAPIResource, azure.XMLAPIResource} { token, err := azure.OAuth2Config(creds.ID, creds.Endpoint, resource).Exchange(oauth2.NoContext, creds.Secret) if err != nil { httphelper.Error(w, err) return } oauthCreds = append(oauthCreds, &OAuthCredential{ ClientID: creds.ID, AccessToken: token.AccessToken, RefreshToken: token.RefreshToken, ExpiresAt: &token.Expiry, Scope: resource, }) } creds.Secret = "" creds.OAuthCreds = oauthCreds } if err := api.Installer.SaveCredentials(creds); err != nil { if err == credentialExistsError { httphelper.ObjectExistsError(w, err.Error()) return } httphelper.Error(w, err) return } w.WriteHeader(200) }
func (c *controllerAPI) PutFormation(ctx context.Context, w http.ResponseWriter, req *http.Request) { app := c.getApp(ctx) release, err := c.getRelease(ctx) if err != nil { respondWithError(w, err) return } var formation ct.Formation if err = httphelper.DecodeJSON(req, &formation); err != nil { respondWithError(w, err) return } if release.ArtifactID == "" { respondWithError(w, ct.ValidationError{Message: "release is not deployable"}) return } formation.AppID = app.ID formation.ReleaseID = release.ID if err = schema.Validate(formation); err != nil { respondWithError(w, err) return } if err = c.formationRepo.Add(&formation); err != nil { respondWithError(w, err) return } httphelper.JSON(w, 200, &formation) }
func (h *jobAPI) ConfigureNetworking(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { log := h.host.log.New("fn", "ConfigureNetworking") log.Info("decoding config") config := &host.NetworkConfig{} if err := httphelper.DecodeJSON(r, config); err != nil { log.Error("error decoding config", "err", err) shutdown.Fatal(err) } // configure the network before returning a response in case the // network coordinator requires the bridge to be created (e.g. // when using flannel with the "alloc" backend) h.host.networkOnce.Do(func() { log.Info("configuring network", "subnet", config.Subnet, "mtu", config.MTU, "resolvers", config.Resolvers) if err := h.host.backend.ConfigureNetworking(config); err != nil { log.Error("error configuring network", "err", err) shutdown.Fatal(err) } h.host.statusMtx.Lock() h.host.status.Network = config h.host.statusMtx.Unlock() }) }
func (api *HTTPAPI) CreateProvider(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { pspec := &volume.ProviderSpec{} if err := httphelper.DecodeJSON(r, &pspec); err != nil { httphelper.Error(w, err) return } if pspec.ID == "" { pspec.ID = random.UUID() } if pspec.Kind == "" { httphelper.ValidationError(w, "kind", "must not be blank") return } var provider volume.Provider provider, err := volumemanager.NewProvider(pspec) if err == volume.UnknownProviderKind { httphelper.ValidationError(w, "kind", fmt.Sprintf("%q is not known", pspec.Kind)) return } if err := api.vman.AddProvider(pspec.ID, provider); err != nil { switch err { case volumemanager.ErrProviderExists: httphelper.ObjectExistsError(w, fmt.Sprintf("provider %q already exists", pspec.ID)) return default: httphelper.Error(w, err) return } } httphelper.JSON(w, 200, pspec) }
func (api *HTTPAPI) Send(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { volumeID := ps.ByName("volume_id") if !strings.Contains(r.Header.Get("Accept"), snapshotContentType) { httphelper.ValidationError(w, "", fmt.Sprintf("must be prepared to accept a content type of %q", snapshotContentType)) return } w.Header().Set("Content-Type", snapshotContentType) var haves []json.RawMessage if err := httphelper.DecodeJSON(r, &haves); err != nil { httphelper.Error(w, err) return } err := api.vman.SendSnapshot(volumeID, haves, w) if err != nil { switch err { case volume.ErrNoSuchVolume: httphelper.ObjectNotFoundError(w, fmt.Sprintf("no volume with id %q", volumeID)) return default: httphelper.Error(w, err) return } } }
func (c *controllerAPI) SetAppRelease(ctx context.Context, w http.ResponseWriter, req *http.Request) { var rid releaseID if err := httphelper.DecodeJSON(req, &rid); err != nil { respondWithError(w, err) return } rel, err := c.releaseRepo.Get(rid.ID) if err != nil { if err == ErrNotFound { err = ct.ValidationError{ Message: fmt.Sprintf("could not find release with ID %s", rid.ID), } } respondWithError(w, err) return } release := rel.(*ct.Release) if err := schema.Validate(release); err != nil { respondWithError(w, err) return } app := c.getApp(ctx) c.appRepo.SetRelease(app, release.ID) httphelper.JSON(w, 200, release) }
func (c *controllerAPI) UpdateApp(ctx context.Context, rw http.ResponseWriter, req *http.Request) { params, _ := ctxhelper.ParamsFromContext(ctx) var data appUpdate if err := httphelper.DecodeJSON(req, &data); err != nil { respondWithError(rw, err) return } if v, ok := data["meta"]; ok && v == nil { // handle {"meta": null} delete(data, "meta") } if err := schema.Validate(data); err != nil { respondWithError(rw, err) return } app, err := c.appRepo.Update(params.ByName("apps_id"), data) if err != nil { respondWithError(rw, err) return } httphelper.JSON(rw, 200, app) }
func (c *controllerAPI) UpdateAppMeta(ctx context.Context, rw http.ResponseWriter, req *http.Request) { params, _ := ctxhelper.ParamsFromContext(ctx) var data appUpdate if err := httphelper.DecodeJSON(req, &data); err != nil { respondWithError(rw, err) return } if err := schema.Validate(data); err != nil { respondWithError(rw, err) return } if data["meta"] == nil { data["meta"] = make(map[string]interface{}) } app, err := c.appRepo.Update(params.ByName("apps_id"), data) if err != nil { respondWithError(rw, err) return } httphelper.JSON(rw, 200, app) }
func (h *jobAPI) ConfigureDiscoverd(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { log := h.host.log.New("fn", "ConfigureDiscoverd") log.Info("decoding config") var config host.DiscoverdConfig if err := httphelper.DecodeJSON(r, &config); err != nil { log.Error("error decoding config", "err", err) httphelper.Error(w, err) return } log.Info("config decoded", "url", config.URL, "dns", config.DNS) h.host.statusMtx.Lock() h.host.status.Discoverd = &config h.host.statusMtx.Unlock() if config.URL != "" && config.DNS != "" { go h.host.discoverdOnce.Do(func() { log.Info("connecting to service discovery", "url", config.URL) if err := h.host.discMan.ConnectLocal(config.URL); err != nil { log.Error("error connecting to service discovery", "err", err) shutdown.Fatal(err) } }) } }
func (h *jobAPI) UpdateTags(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var tags map[string]string if err := httphelper.DecodeJSON(r, &tags); err != nil { httphelper.Error(w, err) return } if err := h.host.UpdateTags(tags); err != nil { httphelper.Error(w, err) return } w.WriteHeader(200) }
func (api *httpAPI) InstallHandler(w http.ResponseWriter, req *http.Request, params httprouter.Params) { var input *jsonInput if err := httphelper.DecodeJSON(req, &input); err != nil { httphelper.Error(w, err) return } api.InstallerStackMtx.Lock() defer api.InstallerStackMtx.Unlock() if len(api.InstallerStacks) > 0 { httphelper.ObjectExistsError(w, "install already started") return } var id = random.Hex(16) var creds aws.CredentialsProvider if input.Creds.AccessKeyID != "" && input.Creds.SecretAccessKey != "" { creds = aws.Creds(input.Creds.AccessKeyID, input.Creds.SecretAccessKey, "") } else { var err error creds, err = aws.EnvCreds() if err != nil { httphelper.ValidationError(w, "", err.Error()) return } } s := &httpInstaller{ ID: id, PromptOutChan: make(chan *httpPrompt), PromptInChan: make(chan *httpPrompt), logger: log.New(), api: api, } s.Stack = &Stack{ Creds: creds, Region: input.Region, InstanceType: input.InstanceType, NumInstances: input.NumInstances, VpcCidr: input.VpcCidr, SubnetCidr: input.SubnetCidr, PromptInput: s.PromptInput, YesNoPrompt: s.YesNoPrompt, } if err := s.Stack.RunAWS(); err != nil { httphelper.Error(w, err) return } api.InstallerStacks[id] = s go s.handleEvents() httphelper.JSON(w, 200, s) }
func (c *controllerAPI) CreateRoute(ctx context.Context, w http.ResponseWriter, req *http.Request) { var route router.Route if err := httphelper.DecodeJSON(req, &route); err != nil { respondWithError(w, err) return } if err := createRoute(c.appRepo.db, c.routerc, c.getApp(ctx).ID, &route); err != nil { respondWithError(w, err) return } httphelper.JSON(w, 200, &route) }
func (h *jobAPI) ConfigureDiscoverd(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { log := h.host.log.New("fn", "ConfigureDiscoverd") log.Info("decoding config") config := &host.DiscoverdConfig{} if err := httphelper.DecodeJSON(r, config); err != nil { log.Error("error decoding config", "err", err) httphelper.Error(w, err) return } log.Info("config decoded", "url", config.URL, "dns", config.DNS) h.host.ConfigureDiscoverd(config) }
func (h *jobAPI) ConfigureNetworking(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { log := h.host.log.New("fn", "ConfigureNetworking") log.Info("decoding config") config := &host.NetworkConfig{} if err := httphelper.DecodeJSON(r, config); err != nil { log.Error("error decoding config", "err", err) shutdown.Fatal(err) } // configure the network before returning a response in case the // network coordinator requires the bridge to be created (e.g. // when using flannel with the "alloc" backend) h.host.ConfigureNetworking(config) }
func (h *jobAPI) ConfigureNetworking(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { config := &host.NetworkConfig{} if err := httphelper.DecodeJSON(r, config); err != nil { shutdown.Fatal(err) } go h.networkOnce.Do(func() { if err := h.host.backend.ConfigureNetworking(config); err != nil { shutdown.Fatal(err) } h.statusMtx.Lock() h.status.Network = config h.statusMtx.Unlock() }) }
func (api *httpAPI) PromptHandler(w http.ResponseWriter, req *http.Request, params httprouter.Params) { api.InstallerPromptsMtx.Lock() prompt := api.InstallerPrompts[params.ByName("id")] api.InstallerPromptsMtx.Unlock() if prompt == nil { httphelper.ObjectNotFoundError(w, "prompt not found") return } var input *httpPrompt if err := httphelper.DecodeJSON(req, &input); err != nil { httphelper.Error(w, err) return } prompt.Resolve(input) w.WriteHeader(200) }
func (c *controllerAPI) UpdateRoute(ctx context.Context, w http.ResponseWriter, req *http.Request) { var route *router.Route if err := httphelper.DecodeJSON(req, &route); err != nil { respondWithError(w, err) return } err := c.routerc.UpdateRoute(route) if err == routerc.ErrNotFound { err = ErrNotFound } if err != nil { respondWithError(w, err) return } httphelper.JSON(w, 200, route) }
func (h *httpAPI) SetServiceMeta(w http.ResponseWriter, r *http.Request, params httprouter.Params) { meta := &discoverd.ServiceMeta{} if err := hh.DecodeJSON(r, meta); err != nil { hh.Error(w, err) return } if err := h.Store.SetServiceMeta(params.ByName("service"), meta); err != nil { if IsNotFound(err) { hh.ObjectNotFoundError(w, err.Error()) } else { hh.Error(w, err) } return } hh.JSON(w, 200, meta) }
func (c *controllerAPI) ProvisionResource(ctx context.Context, w http.ResponseWriter, req *http.Request) { p, err := c.getProvider(ctx) if err != nil { respondWithError(w, err) return } var rr ct.ResourceReq if err = httphelper.DecodeJSON(req, &rr); err != nil { respondWithError(w, err) return } var config []byte if rr.Config != nil { config = *rr.Config } else { config = []byte(`{}`) } data, err := resource.Provision(p.URL, config) if err != nil { respondWithError(w, err) return } res := &ct.Resource{ ProviderID: p.ID, ExternalID: data.ID, Env: data.Env, Apps: rr.Apps, } if err := schema.Validate(res); err != nil { respondWithError(w, err) return } if err := c.resourceRepo.Add(res); err != nil { // TODO: attempt to "rollback" provisioning respondWithError(w, err) return } httphelper.JSON(w, 200, res) }
func (h *httpAPI) SetLeader(w http.ResponseWriter, r *http.Request, params httprouter.Params) { service := params.ByName("service") config := h.Store.GetConfig(service) if config == nil || config.LeaderType != discoverd.LeaderTypeManual { hh.ValidationError(w, "", "service leader election type is not manual") return } inst := &discoverd.Instance{} if err := hh.DecodeJSON(r, inst); err != nil { hh.Error(w, err) return } if err := h.Store.SetLeader(service, inst.ID); err != nil { hh.Error(w, err) return } }
func (h *jobAPI) ConfigureDiscoverd(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var config struct { URL string `json:"url"` } if err := httphelper.DecodeJSON(r, &config); err != nil { httphelper.Error(w, err) return } go h.discoverdOnce.Do(func() { if err := h.connectDiscoverd(config.URL); err != nil { shutdown.Fatal(err) } h.statusMtx.Lock() h.status.Discoverd = &host.DiscoverdConfig{URL: config.URL} h.statusMtx.Unlock() }) }
func (h *jobAPI) ConfigureNetworking(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { config := &host.NetworkConfig{} if err := httphelper.DecodeJSON(r, config); err != nil { shutdown.Fatal(err) } // configure the network before returning a response in case the // network coordinator requires the bridge to be created (e.g. // when using flannel with the "alloc" backend) h.networkOnce.Do(func() { if err := h.host.backend.ConfigureNetworking(config); err != nil { shutdown.Fatal(err) } h.host.statusMtx.Lock() h.host.status.Network = config h.host.statusMtx.Unlock() }) }
func (h *jobAPI) AddJob(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { // TODO(titanous): validate UUID id := ps.ByName("id") log := h.host.log.New("fn", "AddJob", "job.id", id) if shutdown.IsActive() { log.Warn("refusing to start job due to active shutdown") httphelper.JSON(w, 500, struct{}{}) return } log.Info("decoding job") job := &host.Job{ID: id} if err := httphelper.DecodeJSON(r, job); err != nil { log.Error("error decoding job", "err", err) httphelper.Error(w, err) return } log.Info("acquiring state database") if err := h.host.state.Acquire(); err != nil { log.Error("error acquiring state database", "err", err) httphelper.Error(w, err) return } h.host.state.AddJob(job) go func() { // TODO(titanous): ratelimit this goroutine? log.Info("running job") err := h.host.backend.Run(job, nil) h.host.state.Release() if err != nil { log.Error("error running job", "err", err) h.host.state.SetStatusFailed(job.ID, err) } }() // TODO(titanous): return 201 Accepted httphelper.JSON(w, 200, struct{}{}) }
func (h *jobAPI) ConfigureDiscoverd(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { var config host.DiscoverdConfig if err := httphelper.DecodeJSON(r, &config); err != nil { httphelper.Error(w, err) return } h.host.statusMtx.Lock() h.host.status.Discoverd = &config h.host.statusMtx.Unlock() if config.URL != "" && config.DNS != "" { go h.discoverdOnce.Do(func() { if err := h.connectDiscoverd(config.URL); err != nil { shutdown.Fatal(err) } }) } }