// title: event list // path: /events // method: GET // produce: application/json // responses: // 200: OK // 204: No content func eventList(w http.ResponseWriter, r *http.Request, t auth.Token) error { r.ParseForm() filter := &event.Filter{} dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) dec.IgnoreCase(true) err := dec.DecodeValues(&filter, r.Form) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: fmt.Sprintf("unable to parse event filters: %s", err)} } filter.PruneUserValues() filter.Permissions, err = t.Permissions() if err != nil { return err } events, err := event.List(filter) if err != nil { return err } if len(events) == 0 { w.WriteHeader(http.StatusNoContent) return nil } w.Header().Add("Content-Type", "application/json") return json.NewEncoder(w).Encode(events) }
// title: template create // path: /iaas/templates // method: POST // consume: application/x-www-form-urlencoded // responses: // 201: Template created // 400: Invalid data // 401: Unauthorized func templateCreate(w http.ResponseWriter, r *http.Request, token auth.Token) (err error) { err = r.ParseForm() if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } var paramTemplate iaas.Template dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(¶mTemplate, r.Form) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } iaasCtx := permission.Context(permission.CtxIaaS, paramTemplate.IaaSName) allowed := permission.Check(token, permission.PermMachineTemplateCreate, iaasCtx) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeIaas, Value: paramTemplate.IaaSName}, Kind: permission.PermMachineTemplateCreate, Owner: token, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermMachineReadEvents, iaasCtx), }) if err != nil { return err } defer func() { evt.Done(err) }() err = paramTemplate.Save() if err != nil { return err } w.WriteHeader(http.StatusCreated) return nil }
// title: set node status // path: /node/status // method: POST // consume: application/x-www-form-urlencoded // produce: application/json // responses: // 200: Ok // 400: Invalid data // 401: Unauthorized // 404: App or unit not found func setNodeStatus(w http.ResponseWriter, r *http.Request, t auth.Token) error { if t.GetAppName() != app.InternalAppName { return &errors.HTTP{Code: http.StatusForbidden, Message: "this token is not allowed to execute this action"} } err := r.ParseForm() if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } var hostInput provision.NodeStatusData dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(&hostInput, r.Form) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } result, err := app.UpdateNodeStatus(hostInput) if err != nil { if err == provision.ErrNodeNotFound { return &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } return err } w.Header().Add("Content-Type", "application/json") return json.NewEncoder(w).Encode(result) }
func (s *S) TestRebalanceContainersRunAskingForConfirmation(c *check.C) { var stdout, stderr bytes.Buffer context := cmd.Context{ Stdout: &stdout, Stderr: &stderr, Stdin: bytes.NewBufferString("y"), } msg, _ := json.Marshal(tsuruIo.SimpleJsonMessage{Message: "progress msg"}) result := string(msg) trans := &cmdtest.ConditionalTransport{ Transport: cmdtest.Transport{Message: result, Status: http.StatusOK}, CondFunc: func(req *http.Request) bool { req.ParseForm() var params rebalanceOptions dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err := dec.DecodeValues(¶ms, req.Form) c.Assert(err, check.IsNil) c.Assert(params.Dry, check.Equals, false) path := req.URL.Path == "/1.0/docker/containers/rebalance" method := req.Method == "POST" return path && method }, } manager := cmd.NewManager("admin", "0.1", "admin-ver", &stdout, &stderr, nil, nil) client := cmd.NewClient(&http.Client{Transport: trans}, nil, manager) cmd := rebalanceContainersCmd{} err := cmd.Run(&context, client) c.Assert(err, check.IsNil) c.Assert(stdout.String(), check.Equals, "Are you sure you want to rebalance containers? (y/n) progress msg") cmd2 := rebalanceContainersCmd{} err = cmd2.Run(&context, client) c.Assert(err, check.IsNil) }
// title: autoscale set rule // path: /docker/autoscale/rules // method: POST // consume: application/x-www-form-urlencoded // responses: // 200: Ok // 400: Invalid data // 401: Unauthorized func autoScaleSetRule(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { allowedSetRule := permission.Check(t, permission.PermNodeAutoscaleUpdate) if !allowedSetRule { return permission.ErrUnauthorized } err = r.ParseForm() if err != nil { return &tsuruErrors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } var rule autoScaleRule dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(&rule, r.Form) if err != nil { return &tsuruErrors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } var ctxs []permission.PermissionContext if rule.MetadataFilter != "" { ctxs = append(ctxs, permission.Context(permission.CtxPool, rule.MetadataFilter)) } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypePool, Value: rule.MetadataFilter}, Kind: permission.PermNodeAutoscaleUpdate, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermPoolReadEvents, ctxs...), }) if err != nil { return err } defer func() { evt.Done(err) }() return rule.update() }
// title: logs config set // path: /docker/logs // method: POST // consume: application/x-www-form-urlencoded // produce: application/x-json-stream // responses: // 200: Ok // 400: Invalid data // 401: Unauthorized func logsConfigSetHandler(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { err = r.ParseForm() if err != nil { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: fmt.Sprintf("unable to parse form values: %s", err), } } pool := r.FormValue("pool") restart, _ := strconv.ParseBool(r.FormValue("restart")) delete(r.Form, "pool") delete(r.Form, "restart") var conf container.DockerLogConfig dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(&conf, r.Form) if err != nil { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: fmt.Sprintf("unable to parse fields in docker log config: %s", err), } } var ctxs []permission.PermissionContext if pool != "" { ctxs = append(ctxs, permission.Context(permission.CtxPool, pool)) } hasPermission := permission.Check(t, permission.PermPoolUpdateLogs, ctxs...) if !hasPermission { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypePool, Value: pool}, Kind: permission.PermPoolUpdateLogs, Owner: t, CustomData: event.FormToCustomData(r.Form), DisableLock: true, Allowed: event.Allowed(permission.PermPoolReadEvents, ctxs...), }) if err != nil { return err } defer func() { evt.Done(err) }() err = conf.Save(pool) if err != nil { return err } w.Header().Set("Content-Type", "application/x-json-stream") keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "") defer keepAliveWriter.Stop() writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} fmt.Fprintln(writer, "Log config successfully updated.") if restart { filter := &app.Filter{} if pool != "" { filter.Pools = []string{pool} } return tryRestartAppsByFilter(filter, writer) } return nil }
// title: node healing update // path: /healing/node // method: POST // consume: application/x-www-form-urlencoded // responses: // 200: Ok // 401: Unauthorized func nodeHealingUpdate(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { err = r.ParseForm() if err != nil { return err } poolName := r.FormValue("pool") var ctxs []permission.PermissionContext if poolName != "" { ctxs = append(ctxs, permission.Context(permission.CtxPool, poolName)) } if !permission.Check(t, permission.PermHealingUpdate, ctxs...) { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypePool, Value: poolName}, Kind: permission.PermHealingUpdate, Owner: t, CustomData: event.FormToCustomData(r.Form), DisableLock: true, Allowed: event.Allowed(permission.PermPoolReadEvents, ctxs...), }) if err != nil { return err } defer func() { evt.Done(err) }() var config healer.NodeHealerConfig delete(r.Form, "pool") dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(&config, r.Form) if err != nil { return err } return healer.UpdateConfig(poolName, config) }
// title: set envs // path: /apps/{app}/env // method: POST // consume: application/x-www-form-urlencoded // produce: application/x-json-stream // responses: // 200: Envs updated // 400: Invalid data // 401: Unauthorized // 404: App not found func setEnv(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { err = r.ParseForm() if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } var e Envs dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(&e, r.Form) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } if len(e.Envs) == 0 { msg := "You must provide the list of environment variables" return &errors.HTTP{Code: http.StatusBadRequest, Message: msg} } appName := r.URL.Query().Get(":app") a, err := getAppFromContext(appName, r) if err != nil { return err } allowed := permission.Check(t, permission.PermAppUpdateEnvSet, contextsForApp(&a)..., ) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: appTarget(appName), Kind: permission.PermAppUpdateEnvSet, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...), }) if err != nil { return err } defer func() { evt.Done(err) }() envs := map[string]string{} variables := []bind.EnvVar{} for _, v := range e.Envs { envs[v.Name] = v.Value variables = append(variables, bind.EnvVar{Name: v.Name, Value: v.Value, Public: !e.Private}) } w.Header().Set("Content-Type", "application/x-json-stream") keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 30*time.Second, "") defer keepAliveWriter.Stop() writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} return a.SetEnvs( bind.SetEnvApp{ Envs: variables, PublicOnly: true, ShouldRestart: !e.NoRestart, }, writer, ) }
// title: pool create // path: /pools // method: POST // consume: application/x-www-form-urlencoded // responses: // 201: Pool created // 400: Invalid data // 401: Unauthorized // 409: Pool already exists func addPoolHandler(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { allowed := permission.Check(t, permission.PermPoolCreate) if !allowed { return permission.ErrUnauthorized } dec := form.NewDecoder(nil) dec.IgnoreCase(true) dec.IgnoreUnknownKeys(true) var addOpts provision.AddPoolOptions err = r.ParseForm() if err == nil { err = dec.DecodeValues(&addOpts, r.Form) } if err != nil { return &terrors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } if addOpts.Name == "" { return &terrors.HTTP{ Code: http.StatusBadRequest, Message: provision.ErrPoolNameIsRequired.Error(), } } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypePool, Value: addOpts.Name}, Kind: permission.PermPoolCreate, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermPoolReadEvents, permission.Context(permission.CtxPool, addOpts.Name)), }) if err != nil { return err } defer func() { evt.Done(err) }() err = provision.AddPool(addOpts) if err == provision.ErrDefaultPoolAlreadyExists { return &terrors.HTTP{ Code: http.StatusConflict, Message: err.Error(), } } if err == provision.ErrPoolNameIsRequired { return &terrors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } if err == nil { w.WriteHeader(http.StatusCreated) } return err }
// title: node container update // path: /docker/nodecontainers/{name} // method: POST // consume: application/x-www-form-urlencoded // responses: // 200: Ok // 400: Invald data // 401: Unauthorized // 404: Not found func nodeContainerUpdate(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { err = r.ParseForm() if err != nil { return err } dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) dec.IgnoreCase(true) var config nodecontainer.NodeContainerConfig err = dec.DecodeValues(&config, r.Form) if err != nil { return err } poolName := r.FormValue("pool") var ctxs []permission.PermissionContext if poolName != "" { ctxs = append(ctxs, permission.Context(permission.CtxPool, poolName)) } if !permission.Check(t, permission.PermNodecontainerUpdate, ctxs...) { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeNodeContainer, Value: config.Name}, Kind: permission.PermNodecontainerUpdate, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermPoolReadEvents, ctxs...), }) if err != nil { return err } defer func() { evt.Done(err) }() config.Name = r.URL.Query().Get(":name") err = nodecontainer.UpdateContainer(poolName, &config) if err != nil { if err == nodecontainer.ErrNodeContainerNotFound { return &tsuruErrors.HTTP{ Code: http.StatusNotFound, Message: err.Error(), } } if _, ok := err.(nodecontainer.ValidationErr); ok { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } return err } return nil }
// title: move container // path: /docker/container/{id}/move // method: POST // consume: application/x-www-form-urlencoded // produce: application/x-json-stream // responses: // 200: Ok // 400: Invalid data // 401: Unauthorized // 404: Not found func moveContainerHandler(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { err = r.ParseForm() if err != nil { return &tsuruErrors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } params := map[string]string{} dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(¶ms, r.Form) if err != nil { return &tsuruErrors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } contId := r.URL.Query().Get(":id") to := params["to"] if to == "" { return errors.Errorf("Invalid params: id: %s - to: %s", contId, to) } cont, err := mainDockerProvisioner.GetContainer(contId) if err != nil { return &tsuruErrors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } permContexts, err := moveContainersPermissionContexts(cont.HostAddr, to) if err != nil { return err } if !permission.Check(t, permission.PermNodeUpdateMoveContainer, permContexts...) { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeContainer, Value: contId}, Kind: permission.PermNodeUpdateMoveContainer, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermPoolReadEvents, permContexts...), }) if err != nil { return err } defer func() { evt.Done(err) }() w.Header().Set("Content-Type", "application/x-json-stream") keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "") defer keepAliveWriter.Stop() writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} _, err = mainDockerProvisioner.moveContainer(contId, to, writer) if err != nil { return errors.Wrap(err, "Error trying to move container") } fmt.Fprintf(writer, "Containers moved successfully!\n") return nil }
func (r *Response) getForm() map[string]interface{} { if r.chain.failed() { return nil } if !r.checkContentType("application/x-www-form-urlencoded", "") { return nil } decoder := form.NewDecoder(bytes.NewReader(r.content)) var object map[string]interface{} if err := decoder.Decode(&object); err != nil { r.chain.fail(err.Error()) return nil } return object }
// title: rebalance containers // path: /docker/containers/rebalance // method: POST // consume: application/x-www-form-urlencoded // produce: application/x-json-stream // responses: // 200: Ok // 204: No content // 400: Invalid data // 401: Unauthorized func rebalanceContainersHandler(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() var params rebalanceOptions dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(¶ms, r.Form) if err != nil { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } var permContexts []permission.PermissionContext pool, ok := params.MetadataFilter["pool"] if ok { permContexts = append(permContexts, permission.Context(permission.CtxPool, pool)) } if !permission.Check(t, permission.PermNodeUpdateRebalance, permContexts...) { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypePool, Value: pool}, Kind: permission.PermNodeUpdateRebalance, Owner: t, CustomData: event.FormToCustomData(r.Form), DisableLock: true, Allowed: event.Allowed(permission.PermPoolReadEvents, permContexts...), }) if err != nil { return err } defer func() { evt.Done(err) }() w.Header().Set("Content-Type", "application/x-json-stream") keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "") defer keepAliveWriter.Stop() writer := &tsuruIo.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)} _, err = mainDockerProvisioner.rebalanceContainersByFilter(writer, params.AppFilter, params.MetadataFilter, params.Dry) if err != nil { return errors.Wrap(err, "Error trying to rebalance containers") } fmt.Fprintf(writer, "Containers successfully rebalanced!\n") return nil }
func (s *S) TestRebalanceContainersRun(c *check.C) { var stdout, stderr bytes.Buffer context := cmd.Context{ Stdout: &stdout, Stderr: &stderr, } msg, _ := json.Marshal(tsuruIo.SimpleJsonMessage{Message: "progress msg"}) result := string(msg) expectedRebalance := rebalanceOptions{ Dry: true, } trans := &cmdtest.ConditionalTransport{ Transport: cmdtest.Transport{Message: result, Status: http.StatusOK}, CondFunc: func(req *http.Request) bool { req.ParseForm() var params rebalanceOptions dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err := dec.DecodeValues(¶ms, req.Form) c.Assert(err, check.IsNil) c.Assert(params, check.DeepEquals, expectedRebalance) path := req.URL.Path == "/1.0/docker/containers/rebalance" method := req.Method == "POST" return path && method }, } manager := cmd.NewManager("admin", "0.1", "admin-ver", &stdout, &stderr, nil, nil) client := cmd.NewClient(&http.Client{Transport: trans}, nil, manager) cmd := rebalanceContainersCmd{} err := cmd.Flags().Parse(true, []string{"--dry", "-y"}) c.Assert(err, check.IsNil) err = cmd.Run(&context, client) c.Assert(err, check.IsNil) expected := "progress msg" c.Assert(stdout.String(), check.Equals, expected) expectedRebalance = rebalanceOptions{ Dry: false, } cmd2 := rebalanceContainersCmd{} cmd2.Flags().Parse(true, []string{"-y"}) err = cmd2.Run(&context, client) c.Assert(err, check.IsNil) }
// title: pool update // path: /pools/{name} // method: PUT // consume: application/x-www-form-urlencoded // responses: // 200: Pool updated // 401: Unauthorized // 404: Pool not found // 409: Default pool already defined func poolUpdateHandler(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { r.ParseForm() allowed := permission.Check(t, permission.PermPoolUpdate) if !allowed { return permission.ErrUnauthorized } poolName := r.URL.Query().Get(":name") evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypePool, Value: poolName}, Kind: permission.PermPoolUpdate, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermPoolReadEvents, permission.Context(permission.CtxPool, poolName)), }) if err != nil { return err } defer func() { evt.Done(err) }() dec := form.NewDecoder(nil) dec.IgnoreCase(true) dec.IgnoreUnknownKeys(true) var updateOpts provision.UpdatePoolOptions err = dec.DecodeValues(&updateOpts, r.Form) if err != nil { return &terrors.HTTP{ Code: http.StatusBadRequest, Message: err.Error(), } } err = provision.PoolUpdate(poolName, updateOpts) if err == provision.ErrPoolNotFound { return &terrors.HTTP{Code: http.StatusNotFound, Message: err.Error()} } if err == provision.ErrDefaultPoolAlreadyExists { return &terrors.HTTP{ Code: http.StatusConflict, Message: err.Error(), } } return err }
// title: add install host // path: /install/hosts // method: POST // consume: application/x-www-form-urlencoded // produce: application/json // responses: // 201: Host added // 401: Unauthorized func installHostAdd(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { allowed := permission.Check(t, permission.PermInstallManage) if !allowed { return permission.ErrUnauthorized } err = r.ParseForm() if err != nil { return err } dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) dec.IgnoreCase(true) var host *install.Host err = dec.DecodeValues(&host, r.Form) if err != nil { return err } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeInstallHost, Value: host.Name}, Kind: permission.PermInstallManage, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermInstallManage), }) if err != nil { return err } defer func() { evt.Done(err) }() var rawDriver map[string]interface{} err = json.Unmarshal([]byte(r.Form.Get("driver")), &rawDriver) if err != nil { return err } host.Driver = rawDriver err = install.AddHost(host) if err != nil { return err } w.WriteHeader(http.StatusCreated) return nil }
// title: template update // path: /iaas/templates/{template_name} // method: PUT // consume: application/x-www-form-urlencoded // responses: // 200: OK // 400: Invalid data // 401: Unauthorized // 404: Not found func templateUpdate(w http.ResponseWriter, r *http.Request, token auth.Token) (err error) { err = r.ParseForm() if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } var paramTemplate iaas.Template dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(¶mTemplate, r.Form) if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } templateName := r.URL.Query().Get(":template_name") dbTpl, err := iaas.FindTemplate(templateName) if err != nil { if err == mgo.ErrNotFound { return &errors.HTTP{Code: http.StatusNotFound, Message: "template not found"} } return err } iaasCtx := permission.Context(permission.CtxIaaS, dbTpl.IaaSName) allowed := permission.Check(token, permission.PermMachineTemplateUpdate, iaasCtx) if !allowed { return permission.ErrUnauthorized } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeIaas, Value: dbTpl.IaaSName}, Kind: permission.PermMachineTemplateUpdate, Owner: token, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermMachineReadEvents, iaasCtx), }) if err != nil { return err } defer func() { evt.Done(err) }() return dbTpl.Update(¶mTemplate) }
// NewDecoder returns a form decoder that reads from r. func NewDecoder(r io.Reader) goa.Decoder { return form.NewDecoder(r) }
// title: add node // path: /{provisioner}/node // method: POST // consume: application/x-www-form-urlencoded // produce: application/x-json-stream // responses: // 201: Ok // 401: Unauthorized // 404: Not found func addNodeHandler(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { err = r.ParseForm() if err != nil { return &tsuruErrors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } var params provision.AddNodeOptions dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(¶ms, r.Form) if err != nil { return &tsuruErrors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } if templateName, ok := params.Metadata["template"]; ok { params.Metadata, err = iaas.ExpandTemplate(templateName, params.Metadata) if err != nil { return &tsuruErrors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } } poolName := params.Metadata["pool"] if poolName == "" { return &tsuruErrors.HTTP{Code: http.StatusBadRequest, Message: "pool is required"} } if !permission.Check(t, permission.PermNodeCreate, permission.Context(permission.CtxPool, poolName)) { return permission.ErrUnauthorized } if !params.Register { canCreateMachine := permission.Check(t, permission.PermMachineCreate, permission.Context(permission.CtxIaaS, params.Metadata["iaas"])) if !canCreateMachine { return permission.ErrUnauthorized } } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeNode}, Kind: permission.PermNodeCreate, Owner: t, CustomData: event.FormToCustomData(r.Form), DisableLock: true, Allowed: event.Allowed(permission.PermPoolReadEvents, permission.Context(permission.CtxPool, poolName)), }) if err != nil { return err } defer func() { evt.Done(err) }() pool, err := provision.GetPoolByName(poolName) if err != nil { return err } prov, err := pool.GetProvisioner() if err != nil { return err } nodeProv, ok := prov.(provision.NodeProvisioner) if !ok { return provision.ProvisionerNotSupported{Prov: prov, Action: "node operations"} } w.Header().Set("Content-Type", "application/x-json-stream") w.WriteHeader(http.StatusCreated) keepAliveWriter := tsuruIo.NewKeepAliveWriter(w, 15*time.Second, "") defer keepAliveWriter.Stop() addr, response, err := addNodeForParams(nodeProv, params) evt.Target.Value = addr if err != nil { if desc := response["description"]; desc != "" { return errors.Wrapf(err, "Instructions:\n%s", desc) } return err } return nil }
func formC(r io.Reader) Decoder { return form.NewDecoder(r) }
// title: app create // path: /apps // method: POST // consume: application/x-www-form-urlencoded // produce: application/json // responses: // 201: App created // 400: Invalid data // 401: Unauthorized // 403: Quota exceeded // 409: App already exists func createApp(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { err = r.ParseForm() if err != nil { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } var ia inputApp dec := form.NewDecoder(nil) dec.IgnoreCase(true) dec.IgnoreUnknownKeys(true) dec.DecodeValues(&ia, r.Form) a := app.App{ TeamOwner: ia.TeamOwner, Platform: ia.Platform, Plan: app.Plan{Name: ia.Plan}, Name: ia.Name, Description: ia.Description, Pool: ia.Pool, RouterOpts: ia.RouterOpts, } if a.TeamOwner == "" { a.TeamOwner, err = permission.TeamForPermission(t, permission.PermAppCreate) if err != nil { if err != permission.ErrTooManyTeams { return err } teams, listErr := auth.ListTeams() if listErr != nil { return listErr } if len(teams) != 1 { return err } a.TeamOwner = teams[0].Name } } canCreate := permission.Check(t, permission.PermAppCreate, permission.Context(permission.CtxTeam, a.TeamOwner), ) if !canCreate { return permission.ErrUnauthorized } u, err := t.User() if err != nil { return err } platform, err := app.GetPlatform(a.Platform) if err != nil { return err } if platform.Disabled { canUsePlat := permission.Check(t, permission.PermPlatformUpdate) || permission.Check(t, permission.PermPlatformCreate) if !canUsePlat { return &errors.HTTP{Code: http.StatusBadRequest, Message: app.InvalidPlatformError.Error()} } } evt, err := event.New(&event.Opts{ Target: appTarget(a.Name), Kind: permission.PermAppCreate, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermAppReadEvents, contextsForApp(&a)...), }) if err != nil { return err } defer func() { evt.Done(err) }() err = app.CreateApp(&a, u) if err != nil { log.Errorf("Got error while creating app: %s", err) if e, ok := err.(*errors.ValidationError); ok { return &errors.HTTP{Code: http.StatusBadRequest, Message: e.Message} } if _, ok := err.(app.NoTeamsError); ok { return &errors.HTTP{ Code: http.StatusBadRequest, Message: "In order to create an app, you should be member of at least one team", } } if e, ok := err.(*app.AppCreationError); ok { if e.Err == app.ErrAppAlreadyExists { return &errors.HTTP{Code: http.StatusConflict, Message: e.Error()} } if _, ok := e.Err.(*quota.QuotaExceededError); ok { return &errors.HTTP{ Code: http.StatusForbidden, Message: "Quota exceeded", } } } if err == app.InvalidPlatformError { return &errors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } return err } repo, err := repository.Manager().GetRepository(a.Name) if err != nil { return err } msg := map[string]string{ "status": "success", "repository_url": repo.ReadWriteURL, "ip": a.Ip, } jsonMsg, err := json.Marshal(msg) if err != nil { return err } w.WriteHeader(http.StatusCreated) w.Header().Set("Content-Type", "application/json") w.Write(jsonMsg) return nil }
// title: update nodes // path: /{provisioner}/node // method: PUT // consume: application/x-www-form-urlencoded // responses: // 200: Ok // 400: Invalid data // 401: Unauthorized // 404: Not found func updateNodeHandler(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) { err = r.ParseForm() if err != nil { return &tsuruErrors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } var params provision.UpdateNodeOptions dec := form.NewDecoder(nil) dec.IgnoreUnknownKeys(true) err = dec.DecodeValues(¶ms, r.Form) if err != nil { return &tsuruErrors.HTTP{Code: http.StatusBadRequest, Message: err.Error()} } if params.Disable && params.Enable { return &tsuruErrors.HTTP{ Code: http.StatusBadRequest, Message: "A node can't be enabled and disabled simultaneously.", } } if params.Address == "" { return &tsuruErrors.HTTP{Code: http.StatusBadRequest, Message: "address is required"} } prov, node, err := provision.FindNode(params.Address) if err != nil { if err == provision.ErrNodeNotFound { return &tsuruErrors.HTTP{ Code: http.StatusNotFound, Message: err.Error(), } } return err } nodeProv := prov.(provision.NodeProvisioner) oldPool := node.Pool() allowedOldPool := permission.Check(t, permission.PermNodeUpdate, permission.Context(permission.CtxPool, oldPool), ) if !allowedOldPool { return permission.ErrUnauthorized } newPool, ok := params.Metadata["pool"] if ok { allowedNewPool := permission.Check(t, permission.PermNodeUpdate, permission.Context(permission.CtxPool, newPool), ) if !allowedNewPool { return permission.ErrUnauthorized } } evt, err := event.New(&event.Opts{ Target: event.Target{Type: event.TargetTypeNode, Value: node.Address()}, Kind: permission.PermNodeUpdate, Owner: t, CustomData: event.FormToCustomData(r.Form), Allowed: event.Allowed(permission.PermPoolReadEvents, permission.Context(permission.CtxPool, oldPool), permission.Context(permission.CtxPool, newPool), ), }) if err != nil { return err } defer func() { evt.Done(err) }() return nodeProv.UpdateNode(params) }