Exemple #1
0
func (e *Command) repoCreate(c *cobra.Command, args []string) {
	if len(args) < 1 {
		Fail(1, "Valid arguments: <id> [<clone repo url>]\n")
	}

	t := e.Transport.Get()

	id, err := NewResourceLocator(t, git.ResourceTypeRepository, args[0])
	if err != nil {
		Fail(1, "You must pass one valid repository name: %s\n", err.Error())
	}
	if id.(*ResourceLocator).Type != git.ResourceTypeRepository {
		Fail(1, "You must pass one valid repository name: %s\n", err.Error())
	}

	cloneUrl := ""
	if len(args) == 2 {
		cloneUrl = args[1]
	}

	Executor{
		On: Locators{id},
		Serial: func(on Locator) JobRequest {
			return &gitjobs.CreateRepositoryRequest{
				Id:        git.RepoIdentifier(on.(*ResourceLocator).Id),
				CloneUrl:  cloneUrl,
				RequestId: jobs.NewRequestIdentifier(),
			}
		},
		Output:    os.Stdout,
		Transport: t,
	}.StreamAndExit()
}
Exemple #2
0
func repoCreate(c *cobra.Command, args []string) {
	if len(args) < 1 {
		Fail(1, "Valid arguments: <id> [<clone repo url>]\n")
	}

	t := c.Flags().Lookup("transport").Value.(*transport.TransportFlag).Get()

	id, err := NewResourceLocator(t, git.ResourceTypeRepository, args[0])
	if err != nil {
		Fail(1, "You must pass one valid repository name: %s\n", err.Error())
	}
	if id.(*ResourceLocator).Type != git.ResourceTypeRepository {
		Fail(1, "You must pass one valid repository name: %s\n", err.Error())
	}

	cloneUrl := ""
	if len(args) == 2 {
		cloneUrl = args[1]
	}

	Executor{
		On: Locators{id},
		Serial: func(on Locator) jobs.Job {
			return &gitjobs.CreateRepositoryRequest{
				Id:        git.RepoIdentifier(on.(*ResourceLocator).Id),
				CloneUrl:  cloneUrl,
				RequestId: jobs.NewRequestIdentifier(),
			}
		},
		Output:    os.Stdout,
		LocalInit: LocalInitializers(systemd.Start, containers.InitializeData),
		Transport: t,
	}.StreamAndExit()
}
Exemple #3
0
func (ctx *CommandContext) installImage(c *cobra.Command, args []string) {
	if err := ctx.environment.ExtractVariablesFrom(&args, true); err != nil {
		cmd.Fail(1, err.Error())
	}

	if len(args) < 2 {
		cmd.Fail(1, "Valid arguments: <image_name> <id> ...")
	}

	t := ctx.Transport.Get()

	imageId := args[0]
	if imageId == "" {
		cmd.Fail(1, "Argument 1 must be a Docker image to base the service on")
	}
	ids, err := cloc.NewContainerLocators(t, args[1:]...)
	if err != nil {
		cmd.Fail(1, "You must pass one or more valid service names: %s", err.Error())
	}

	for _, locator := range ids {
		if imageId == string(cloc.AsIdentifier(locator)) {
			cmd.Fail(1, "Image name and container id must not be the same: %s", imageId)
		}
	}

	cmd.Executor{
		On: ids,
		Serial: func(on cmd.Locator) cmd.JobRequest {
			r := cjobs.InstallContainerRequest{
				RequestIdentifier: jobs.NewRequestIdentifier(),

				Id:               cloc.AsIdentifier(on),
				Image:            imageId,
				Started:          ctx.start,
				Isolate:          ctx.isolate,
				SocketActivation: ctx.sockAct,

				Ports:        *ctx.portPairs.Get().(*port.PortPairs),
				Environment:  &ctx.environment.Description,
				NetworkLinks: ctx.networkLinks.NetworkLinks,
				VolumeConfig: ctx.volumeConfig.VolumeConfig,
				SystemdSlice: ctx.systemdSlice,
			}
			return &r
		},
		Output:    os.Stdout,
		Transport: t,
	}.StreamAndExit()
}
Exemple #4
0
func installImage(cmd *cobra.Command, args []string) {
	if err := environment.ExtractVariablesFrom(&args, true); err != nil {
		Fail(1, err.Error())
	}

	if len(args) < 2 {
		Fail(1, "Valid arguments: <image_name> <id> ...\n")
	}

	imageId := args[0]
	if imageId == "" {
		Fail(1, "Argument 1 must be a Docker image to base the service on\n")
	}
	ids, err := NewContainerLocators(args[1:]...)
	if err != nil {
		Fail(1, "You must pass one or more valid service names: %s\n", err.Error())
	}

	suffix := "/" + imageId
	for _, locator := range ids {
		if strings.HasSuffix(locator.Identity(), suffix) {
			Fail(1, "Image name and container id must not be the same: %s\n", imageId)
		}
	}

	Executor{
		On: ids,
		Serial: func(on Locator) jobs.Job {
			return &http.HttpInstallContainerRequest{
				InstallContainerRequest: jobs.InstallContainerRequest{
					RequestIdentifier: jobs.NewRequestIdentifier(),

					Id:               on.(ResourceLocator).Identifier(),
					Image:            imageId,
					Started:          start,
					Isolate:          isolate,
					SocketActivation: sockAct,

					Ports:        *portPairs.Get().(*port.PortPairs),
					Environment:  &environment.Description,
					NetworkLinks: networkLinks.NetworkLinks,
				},
			}
		},
		Output:    os.Stdout,
		LocalInit: needsSystemdAndData,
	}.StreamAndExit()
}
Exemple #5
0
func (t *TokenConfiguration) Sign(job *cjobs.ContentRequest, keyId string, expiration int64) (string, error) {
	source := &TokenData{
		Identifier:     jobs.NewRequestIdentifier().String(),
		Locator:        job.Locator,
		Type:           job.Type,
		ExpirationDate: expiration,
	}

	buf := &bytes.Buffer{}
	encoder := json.NewEncoder(buf)
	if err := encoder.Encode(source); err != nil {
		return "", err
	}

	cipher, err := rsa.EncryptPKCS1v15(rand.Reader, t.publicKey, buf.Bytes())
	if err != nil {
		return "", err
	}

	hash := crypto.SHA256.New()
	if _, err := hash.Write(cipher); err != nil {
		return "", err
	}

	hashed := hash.Sum(nil)
	sig, err := rsa.SignPKCS1v15(rand.Reader, t.privateKey, crypto.SHA256, hashed)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf(
		"%s/%s/%s",
		utils.EncodeUrlPath(keyId),
		base64.URLEncoding.EncodeToString(sig),
		base64.URLEncoding.EncodeToString(cipher),
	), nil
}
Exemple #6
0
func (ctx *CommandContext) deployContainers(c *cobra.Command, args []string) {
	if len(args) < 1 {
		cmd.Fail(1, "Valid arguments: <deployment_file|URL> <host> ...")
	}

	t := ctx.Transport.Get()

	path := args[0]
	if path == "" {
		cmd.Fail(1, "Argument 1 must be deployment file or URL describing how the containers are related")
	}

	u, err := url.Parse(path)
	if nil != err {
		cmd.Fail(1, "Cannot Parse Argument 1: %s", err.Error())
	}

	var deploy *deployment.Deployment
	switch u.Scheme {
	case "":
		deploy, err = deployment.NewDeploymentFromFile(u.Path)
	case "file":
		deploy, err = deployment.NewDeploymentFromFile(u.Path)
	case "http", "https":
		deploy, err = deployment.NewDeploymentFromURL(u.String(), *ctx.Insecure, time.Duration(ctx.timeout))
	default:
		cmd.Fail(1, "Unsupported URL Scheme '%s' for deployment", u.Scheme)
	}

	if nil != err {
		cmd.Fail(1, "Unable to load deployment from %s: %s", path, err.Error())
	}

	if len(args) == 1 {
		args = append(args, transport.Local.String())
	}
	servers, err := transport.NewTransportLocators(t, args[1:]...)
	if err != nil {
		cmd.Fail(1, "You must pass zero or more valid host names (use '%s' or pass no arguments for the current server): %s", transport.Local.String(), err.Error())
	}

	re := regexp.MustCompile("\\.\\d{8}\\-\\d{6}\\z")
	now := time.Now().Format(".20060102-150405")
	base := filepath.Base(path)
	base = re.ReplaceAllString(base, "")
	newPath := base + now

	fmt.Printf("==> Deploying %s\n", path)
	changes, removed, err := deploy.Describe(deployment.SimplePlacement(servers), t)
	if err != nil {
		cmd.Fail(1, "Deployment is not valid: %s", err.Error())
	}

	if len(removed) > 0 {
		removedIds, err := LocatorsForDeploymentInstances(t, removed)
		if err != nil {
			cmd.Fail(1, "Unable to generate deployment info: %s", err.Error())
		}

		failures := cmd.Executor{
			On: removedIds,
			Serial: func(on cmd.Locator) cmd.JobRequest {
				return &cjobs.DeleteContainerRequest{
					Id: cloc.AsIdentifier(on),
				}
			},
			Output: os.Stdout,
			OnSuccess: func(r *cmd.CliJobResponse, w io.Writer, job cmd.RequestedJob) {
				fmt.Fprintf(w, "==> Deleted %s", string(job.Request.(*cjobs.DeleteContainerRequest).Id))
			},
			Transport: t,
		}.Stream()
		for i := range failures {
			fmt.Fprintf(os.Stderr, failures[i].Error())
		}
	}

	addedIds, err := LocatorsForDeploymentInstances(t, changes.Instances.Added())
	if err != nil {
		cmd.Fail(1, "Unable to generate deployment info: %s", err.Error())
	}

	errors := cmd.Executor{
		On: addedIds,
		Serial: func(on cmd.Locator) cmd.JobRequest {
			instance, _ := changes.Instances.Find(cloc.AsIdentifier(on))
			links := instance.NetworkLinks()

			return &cjobs.InstallContainerRequest{
				RequestIdentifier: jobs.NewRequestIdentifier(),

				Id:          instance.Id,
				Image:       instance.Image,
				Environment: instance.EnvironmentVariables(),
				Isolate:     ctx.isolate,

				Ports:        instance.Ports.PortPairs(),
				NetworkLinks: &links,
			}
		},
		OnSuccess: func(r *cmd.CliJobResponse, w io.Writer, job cmd.RequestedJob) {
			installJob := job.Request.(*cjobs.InstallContainerRequest)
			instance, _ := changes.Instances.Find(installJob.Id)
			if pairs, ok := installJob.PortMappingsFrom(r.Pending); ok {
				if !instance.Ports.Update(pairs) {
					fmt.Fprintf(os.Stderr, "Not all ports listed %+v were returned by the server %+v", instance.Ports, pairs)
				}
			}
		},
		Output:    os.Stdout,
		Transport: t,
	}.Stream()

	changes.UpdateLinks()

	for _, c := range changes.Containers {
		instances := c.Instances()
		if len(instances) > 0 {
			for _, link := range instances[0].NetworkLinks() {
				fmt.Printf("==> Linking %s: %s:%d -> %s:%d\n", c.Name, link.FromHost, link.FromPort, link.ToHost, link.ToPort)
			}
		}
	}

	contents, _ := json.MarshalIndent(changes, "", "  ")
	contents = append(contents, []byte("\n")...)
	if err := ioutil.WriteFile(newPath, contents, 0664); err != nil {
		fmt.Fprintf(os.Stderr, "Unable to write %s: %s\n", newPath, err.Error())
	}

	linkedIds, err := LocatorsForDeploymentInstances(t, changes.Instances.Linked())
	if err != nil {
		cmd.Fail(1, "Unable to generate deployment info: %s", err.Error())
	}

	cmd.Executor{
		On: linkedIds,
		Group: func(on ...cmd.Locator) cmd.JobRequest {
			links := []containers.ContainerLink{}
			for i := range on {
				instance, _ := changes.Instances.Find(cloc.AsIdentifier(on[i]))
				network := instance.NetworkLinks()
				if len(network) > 0 {
					links = append(links, containers.ContainerLink{instance.Id, network})
				}
			}

			return &cjobs.LinkContainersRequest{&containers.ContainerLinks{links}}
		},
		Output:    os.Stdout,
		Transport: t,
	}.Stream()

	cmd.Executor{
		On: addedIds,
		Serial: func(on cmd.Locator) cmd.JobRequest {
			return &cjobs.StartedContainerStateRequest{
				Id: cloc.AsIdentifier(on),
			}
		},
		Output:    os.Stdout,
		Transport: t,
	}.Stream()

	fmt.Printf("==> Deployed as %s\n", newPath)
	if len(errors) > 0 {
		for i := range errors {
			fmt.Fprintf(os.Stderr, "Error: %s\n", errors[i])
		}
		os.Exit(1)
	}
}
Exemple #7
0
func (conf *HttpConfiguration) handleWithMethod(method JobHandler) func(*rest.ResponseWriter, *rest.Request) {
	return func(w *rest.ResponseWriter, r *rest.Request) {
		match := r.Header.Get("If-Match")
		segments := strings.Split(match, ",")
		for i := range segments {
			if strings.HasPrefix(segments[i], "api=") {
				if segments[i][4:] != ApiVersion() {
					http.Error(w, fmt.Sprintf("Current API version %s does not match requested %s", ApiVersion(), segments[i][4:]), http.StatusPreconditionFailed)
					return
				}
			}
		}

		context := &jobs.JobContext{}

		requestId := r.Header.Get("X-Request-Id")
		if requestId == "" {
			context.Id = jobs.NewRequestIdentifier()
		} else {
			id, err := jobs.NewRequestIdentifierFromString(requestId)
			if err != nil {
				http.Error(w, "X-Request-Id must be a 32 character hexadecimal string", http.StatusBadRequest)
				return
			}
			context.Id = id
		}

		/*token, id, errt := extractToken(r.PathParam("token"), r.Request)
		if errt != nil {
			log.Println(errt)
			http.Error(w, "Token is required - pass /token/<token>/<path>", http.StatusForbidden)
			return
		}

		if token.D == 0 {
			log.Println("http: Recommend passing 'd' as an argument for the current date")
		}
		if token.U == "" {
			log.Println("http: Recommend passing 'u' as an argument for the associated user")
		}*/

		job, errh := method(context, r)
		if errh != nil {
			if errh != ErrHandledResponse {
				http.Error(w, "Invalid request: "+errh.Error()+"\n", http.StatusBadRequest)
			}
			return
		}

		mode := ResponseJson
		if r.Header.Get("Accept") == "text/plain" {
			mode = ResponseTable
		}

		acceptHeader := r.Header.Get("Accept")
		overrideAcceptHeader := r.Header.Get("X-Accept")
		if overrideAcceptHeader != "" {
			acceptHeader = overrideAcceptHeader
		}
		canStream := didClientRequestStreamableResponse(acceptHeader)
		if streaming, ok := job.(HttpStreamable); ok {
			canStream = streaming.Streamable()
		}
		response := NewHttpJobResponse(w.ResponseWriter, !canStream, mode)

		wait, errd := conf.Dispatcher.Dispatch(context.Id, job, response)
		if errd == jobs.ErrRanToCompletion {
			http.Error(w, errd.Error(), http.StatusNoContent)
			return
		} else if errd != nil {
			serveRequestError(w, apiRequestError{errd, errd.Error(), http.StatusServiceUnavailable})
			return
		}
		<-wait
	}
}
Exemple #8
0
func (h *HttpTransport) ExecuteRemote(baseUrl *url.URL, job RemoteExecutable, res jobs.Response) error {
	reader, writer := io.Pipe()
	httpreq, errn := http.NewRequest(job.HttpMethod(), baseUrl.String(), reader)
	if errn != nil {
		return errn
	}

	id := job.MarshalRequestIdentifier()
	if len(id) == 0 {
		id = jobs.NewRequestIdentifier()
	}

	query := &url.Values{}
	job.MarshalUrlQuery(query)

	req := httpreq
	req.Header.Set("X-Request-Id", id.String())
	req.Header.Set("If-Match", "api="+ApiVersion())
	req.Header.Set("Content-Type", "application/json")
	//TODO: introduce API version per job
	//TODO: content request signing for GETs
	req.URL.Path = job.HttpPath()
	req.URL.RawQuery = query.Encode()
	go func() {
		if err := job.MarshalHttpRequestBody(writer); err != nil {
			log.Printf("http_remote: Error when writing to http: %v", err)
			writer.CloseWithError(err)
		} else {
			writer.Close()
		}
	}()

	resp, err := h.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	isJson := resp.Header.Get("Content-Type") == "application/json"

	switch code := resp.StatusCode; {
	case code == 202:
		if isJson {
			return errors.New("Decoding of streaming JSON has not been implemented")
		}
		data, err := job.UnmarshalHttpResponse(resp.Header, nil, ResponseTable)
		if err != nil {
			return err
		}
		if pending, ok := data.(map[string]interface{}); ok {
			for k := range pending {
				res.WritePendingSuccess(k, pending[k])
			}
		}
		w := res.SuccessWithWrite(jobs.ResponseOk, false, false)
		if _, err := io.Copy(w, resp.Body); err != nil {
			return err
		}
	case code == 204:
		data, err := job.UnmarshalHttpResponse(resp.Header, nil, ResponseTable)
		if err != nil {
			return err
		}
		if pending, ok := data.(map[string]interface{}); ok {
			for k := range pending {
				res.WritePendingSuccess(k, pending[k])
			}
		}
		res.Success(jobs.ResponseOk)
	case code >= 200 && code < 300:
		if !isJson {
			return errors.New(fmt.Sprintf("remote: Response with %d status code had content type %s (should be application/json)", code, resp.Header.Get("Content-Type")))
		}
		data, err := job.UnmarshalHttpResponse(nil, resp.Body, ResponseJson)
		if err != nil {
			return err
		}
		res.SuccessWithData(jobs.ResponseOk, data)
	default:
		if isJson {
			decoder := json.NewDecoder(resp.Body)
			data := httpFailureResponse{}
			if err := decoder.Decode(&data); err != nil {
				return err
			}
			res.Failure(jobs.SimpleError{jobs.ResponseError, data.Message})
			return nil
		}
		io.Copy(os.Stderr, resp.Body)
		res.Failure(jobs.SimpleError{jobs.ResponseError, "Unable to decode response."})
	}
	return nil
}
Exemple #9
0
func deployContainers(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		Fail(1, "Valid arguments: <deployment_file> <host> ...")
	}

	path := args[0]
	if path == "" {
		Fail(1, "Argument 1 must be deployment file describing how the containers are related")
	}
	deploy, err := deployment.NewDeploymentFromFile(path)
	if err != nil {
		Fail(1, "Unable to load deployment file: %s", err.Error())
	}
	if len(args) == 1 {
		args = append(args, transport.Local.String())
	}
	servers, err := transport.NewTransportLocators(defaultTransport.Get(), args[1:]...)
	if err != nil {
		Fail(1, "You must pass zero or more valid host names (use '%s' or pass no arguments for the current server): %s", transport.Local.String(), err.Error())
	}

	re := regexp.MustCompile("\\.\\d{8}\\-\\d{6}\\z")
	now := time.Now().Format(".20060102-150405")
	base := filepath.Base(path)
	base = re.ReplaceAllString(base, "")
	newPath := base + now

	fmt.Printf("==> Deploying %s\n", path)
	changes, removed, err := deploy.Describe(deployment.SimplePlacement(servers), defaultTransport.Get())
	if err != nil {
		Fail(1, "Deployment is not valid: %s", err.Error())
	}

	if len(removed) > 0 {
		removedIds, err := LocatorsForDeploymentInstances(defaultTransport.Get(), removed)
		if err != nil {
			Fail(1, "Unable to generate deployment info: %s", err.Error())
		}

		failures := Executor{
			On: removedIds,
			Serial: func(on Locator) jobs.Job {
				return &cjobs.DeleteContainerRequest{
					Id:    AsIdentifier(on),
					Label: on.Identity(),
				}
			},
			Output: os.Stdout,
			OnSuccess: func(r *CliJobResponse, w io.Writer, job interface{}) {
				fmt.Fprintf(w, "==> Deleted %s", job.(jobs.LabeledJob).JobLabel())
			},
			LocalInit: needsSystemdAndData,
			Transport: defaultTransport.Get(),
		}.Stream()
		for i := range failures {
			fmt.Fprintf(os.Stderr, failures[i].Error())
		}
	}

	addedIds, err := LocatorsForDeploymentInstances(defaultTransport.Get(), changes.Instances.Added())
	if err != nil {
		Fail(1, "Unable to generate deployment info: %s", err.Error())
	}

	errors := Executor{
		On: addedIds,
		Serial: func(on Locator) jobs.Job {
			instance, _ := changes.Instances.Find(AsIdentifier(on))
			links := instance.NetworkLinks()
			return &cjobs.InstallContainerRequest{
				RequestIdentifier: jobs.NewRequestIdentifier(),

				Id:      instance.Id,
				Image:   instance.Image,
				Isolate: isolate,

				Ports:        instance.Ports.PortPairs(),
				NetworkLinks: &links,
			}
		},
		OnSuccess: func(r *CliJobResponse, w io.Writer, job interface{}) {
			installJob := job.(*cjobs.InstallContainerRequest)
			instance, _ := changes.Instances.Find(installJob.Id)
			if pairs, ok := installJob.PortMappingsFrom(r.Pending); ok {
				if !instance.Ports.Update(pairs) {
					fmt.Fprintf(os.Stderr, "Not all ports listed %+v were returned by the server %+v", instance.Ports, pairs)
				}
			}
		},
		Output:    os.Stdout,
		LocalInit: needsSystemdAndData,
		Transport: defaultTransport.Get(),
	}.Stream()

	changes.UpdateLinks()

	for _, c := range changes.Containers {
		instances := c.Instances()
		if len(instances) > 0 {
			for _, link := range instances[0].NetworkLinks() {
				fmt.Printf("==> Linking %s: %s:%d -> %s:%d\n", c.Name, link.FromHost, link.FromPort, link.ToHost, link.ToPort)
			}
		}
	}

	contents, _ := json.Marshal(changes)
	if err := ioutil.WriteFile(newPath, contents, 0664); err != nil {
		fmt.Fprintf(os.Stderr, "Unable to write %s: %s\n", newPath, err.Error())
	}

	linkedIds, err := LocatorsForDeploymentInstances(defaultTransport.Get(), changes.Instances.Linked())
	if err != nil {
		Fail(1, "Unable to generate deployment info: %s", err.Error())
	}

	Executor{
		On: linkedIds,
		Group: func(on ...Locator) jobs.Job {
			links := []cjobs.ContainerLink{}
			for i := range on {
				instance, _ := changes.Instances.Find(AsIdentifier(on[i]))
				network := instance.NetworkLinks()
				if len(network) > 0 {
					links = append(links, cjobs.ContainerLink{instance.Id, network})
				}
			}

			return &cjobs.LinkContainersRequest{&cjobs.ContainerLinks{links}, on[0].TransportLocator().String()}
		},
		Output:    os.Stdout,
		Transport: defaultTransport.Get(),
	}.Stream()

	Executor{
		On: addedIds,
		Serial: func(on Locator) jobs.Job {
			return &cjobs.StartedContainerStateRequest{
				Id: AsIdentifier(on),
			}
		},
		Output:    os.Stdout,
		Transport: defaultTransport.Get(),
	}.Stream()

	fmt.Printf("==> Deployed as %s\n", newPath)
	if len(errors) > 0 {
		for i := range errors {
			fmt.Fprintf(os.Stderr, "Error: %s\n", errors[i])
		}
		os.Exit(1)
	}
}
Exemple #10
0
func (conf *HttpConfiguration) handleWithMethod(method JobHandler) func(*rest.ResponseWriter, *rest.Request) {
	return func(w *rest.ResponseWriter, r *rest.Request) {
		match := r.Header.Get("If-Match")
		segments := strings.Split(match, ",")
		for i := range segments {
			if strings.HasPrefix(segments[i], "api=") {
				if segments[i][4:] != ApiVersion() {
					http.Error(w, fmt.Sprintf("Current API version %s does not match requested %s", ApiVersion(), segments[i][4:]), http.StatusPreconditionFailed)
					return
				}
			}
		}

		context := &jobs.JobContext{}

		requestId := r.Header.Get("X-Request-Id")
		if requestId == "" {
			context.Id = jobs.NewRequestIdentifier()
		} else {
			id, err := jobs.NewRequestIdentifierFromString(requestId)
			if err != nil {
				http.Error(w, "X-Request-Id must be a 32 character hexadecimal string", http.StatusBadRequest)
				return
			}
			context.Id = id
		}

		// parse the incoming request into an object
		jobRequest, errh := method(context, r)
		if errh != nil {
			serveRequestError(w, apiRequestError{errh, errh.Error(), http.StatusBadRequest})
			return
		}

		// find the job implementation for that request
		job, errj := jobs.JobFor(jobRequest)
		if errj != nil {
			serveRequestError(w, apiRequestError{errj, errj.Error(), http.StatusBadRequest})
			return
		}

		// determine the type of the request
		acceptHeader := r.Header.Get("Accept")
		overrideAcceptHeader := r.Header.Get("X-Accept")
		if overrideAcceptHeader != "" {
			acceptHeader = overrideAcceptHeader
		}

		// setup the appropriate mode
		mode := ResponseJson
		if acceptHeader == "text/plain" {
			mode = ResponseTable
		}
		canStream := didClientRequestStreamableResponse(acceptHeader)
		response := NewHttpJobResponse(w.ResponseWriter, !canStream, mode)

		// queue / handle the request
		wait, errd := conf.Dispatcher.Dispatch(context.Id, job, response)
		if errd == jobs.ErrRanToCompletion {
			http.Error(w, errd.Error(), http.StatusNoContent)
			return
		} else if errd != nil {
			serveRequestError(w, apiRequestError{errd, errd.Error(), http.StatusServiceUnavailable})
			return
		}
		<-wait
	}
}
Exemple #11
0
func (conf *HttpConfiguration) handleWithMethod(method JobHandler) func(*rest.ResponseWriter, *rest.Request) {
	return func(w *rest.ResponseWriter, r *rest.Request) {
		context := &HttpContext{}

		context.ApiVersion = r.Header.Get("X-Api-Version")

		requestId := r.Header.Get("X-Request-Id")
		if requestId == "" {
			context.Id = jobs.NewRequestIdentifier()
		} else {
			id, err := jobs.NewRequestIdentifierFromString(requestId)
			if err != nil {
				http.Error(w, "X-Request-Id must be a 32 character hexadecimal string", http.StatusBadRequest)
				return
			}
			context.Id = id
		}

		// parse the incoming request into an object
		jobRequest, errh := method(conf, context, r)
		if errh != nil {
			serveRequestError(w, apiRequestError{errh, errh.Error(), http.StatusBadRequest})
			return
		}

		// find the job implementation for that request
		job, errj := jobs.JobFor(jobRequest)
		if errj != nil {
			if errj == jobs.ErrNoJobForRequest {
				serveRequestError(w, apiRequestError{errj, fmt.Sprintf("The requested job %s has no registered implementation", reflect.TypeOf(jobRequest)), http.StatusBadRequest})
			}
			serveRequestError(w, apiRequestError{errj, errj.Error(), http.StatusBadRequest})
			return
		}

		// determine the type of the request
		acceptHeader := r.Header.Get("Accept")
		overrideAcceptHeader := r.Header.Get("X-Accept")
		if overrideAcceptHeader != "" {
			acceptHeader = overrideAcceptHeader
		}

		// setup the appropriate mode
		mode := client.ResponseJson
		if acceptHeader == "text/plain" {
			mode = client.ResponseTable
		}
		canStream := didClientRequestStreamableResponse(acceptHeader)
		response := NewHttpJobResponse(w.ResponseWriter, !canStream, mode)

		// queue / handle the request
		wait, errd := conf.Dispatcher.Dispatch(context.Id, job, response)
		if errd == jobs.ErrRanToCompletion {
			http.Error(w, errd.Error(), http.StatusNoContent)
			return
		} else if errd != nil {
			serveRequestError(w, apiRequestError{errd, errd.Error(), http.StatusServiceUnavailable})
			return
		}
		<-wait
	}
}