diff --git a/api/api.go b/api/api.go index bacb7c3c9..9f4c8ebc5 100644 --- a/api/api.go +++ b/api/api.go @@ -592,10 +592,11 @@ func (c *Client) newRequest(method, path string) (*request, error) { config: &c.config, method: method, url: &url.URL{ - Scheme: base.Scheme, - User: base.User, - Host: base.Host, - Path: u.Path, + Scheme: base.Scheme, + User: base.User, + Host: base.Host, + Path: u.Path, + RawPath: u.RawPath, }, params: make(map[string][]string), } diff --git a/api/jobs.go b/api/jobs.go index b240b394f..e12709143 100644 --- a/api/jobs.go +++ b/api/jobs.go @@ -146,7 +146,7 @@ func (j *Jobs) PrefixList(prefix string) ([]*JobListStub, *QueryMeta, error) { // job given its unique ID. func (j *Jobs) Info(jobID string, q *QueryOptions) (*Job, *QueryMeta, error) { var resp Job - qm, err := j.client.query("/v1/job/"+jobID, &resp, q) + qm, err := j.client.query("/v1/job/"+url.PathEscape(jobID), &resp, q) if err != nil { return nil, nil, err } @@ -157,7 +157,7 @@ func (j *Jobs) Info(jobID string, q *QueryOptions) (*Job, *QueryMeta, error) { // unique ID. func (j *Jobs) Versions(jobID string, diffs bool, q *QueryOptions) ([]*Job, []*JobDiff, *QueryMeta, error) { var resp JobVersionsResponse - qm, err := j.client.query(fmt.Sprintf("/v1/job/%s/versions?diffs=%v", jobID, diffs), &resp, q) + qm, err := j.client.query(fmt.Sprintf("/v1/job/%s/versions?diffs=%v", url.PathEscape(jobID), diffs), &resp, q) if err != nil { return nil, nil, nil, err } @@ -167,7 +167,7 @@ func (j *Jobs) Versions(jobID string, diffs bool, q *QueryOptions) ([]*Job, []*J // Allocations is used to return the allocs for a given job ID. func (j *Jobs) Allocations(jobID string, allAllocs bool, q *QueryOptions) ([]*AllocationListStub, *QueryMeta, error) { var resp []*AllocationListStub - u, err := url.Parse("/v1/job/" + jobID + "/allocations") + u, err := url.Parse("/v1/job/" + url.PathEscape(jobID) + "/allocations") if err != nil { return nil, nil, err } @@ -188,7 +188,7 @@ func (j *Jobs) Allocations(jobID string, allAllocs bool, q *QueryOptions) ([]*Al // ID. func (j *Jobs) Deployments(jobID string, all bool, q *QueryOptions) ([]*Deployment, *QueryMeta, error) { var resp []*Deployment - u, err := url.Parse("/v1/job/" + jobID + "/deployments") + u, err := url.Parse("/v1/job/" + url.PathEscape(jobID) + "/deployments") if err != nil { return nil, nil, err } @@ -208,7 +208,7 @@ func (j *Jobs) Deployments(jobID string, all bool, q *QueryOptions) ([]*Deployme // the given job ID. func (j *Jobs) LatestDeployment(jobID string, q *QueryOptions) (*Deployment, *QueryMeta, error) { var resp *Deployment - qm, err := j.client.query("/v1/job/"+jobID+"/deployment", &resp, q) + qm, err := j.client.query("/v1/job/"+url.PathEscape(jobID)+"/deployment", &resp, q) if err != nil { return nil, nil, err } @@ -219,7 +219,7 @@ func (j *Jobs) LatestDeployment(jobID string, q *QueryOptions) (*Deployment, *Qu // ID. func (j *Jobs) Evaluations(jobID string, q *QueryOptions) ([]*Evaluation, *QueryMeta, error) { var resp []*Evaluation - qm, err := j.client.query("/v1/job/"+jobID+"/evaluations", &resp, q) + qm, err := j.client.query("/v1/job/"+url.PathEscape(jobID)+"/evaluations", &resp, q) if err != nil { return nil, nil, err } @@ -232,7 +232,7 @@ func (j *Jobs) Evaluations(jobID string, q *QueryOptions) ([]*Evaluation, *Query // eventually GC'ed from the system. Most callers should not specify purge. func (j *Jobs) Deregister(jobID string, purge bool, q *WriteOptions) (string, *WriteMeta, error) { var resp JobDeregisterResponse - wm, err := j.client.delete(fmt.Sprintf("/v1/job/%v?purge=%t", jobID, purge), &resp, q) + wm, err := j.client.delete(fmt.Sprintf("/v1/job/%v?purge=%t", url.PathEscape(jobID), purge), &resp, q) if err != nil { return "", nil, err } @@ -242,7 +242,7 @@ func (j *Jobs) Deregister(jobID string, purge bool, q *WriteOptions) (string, *W // ForceEvaluate is used to force-evaluate an existing job. func (j *Jobs) ForceEvaluate(jobID string, q *WriteOptions) (string, *WriteMeta, error) { var resp JobRegisterResponse - wm, err := j.client.write("/v1/job/"+jobID+"/evaluate", nil, &resp, q) + wm, err := j.client.write("/v1/job/"+url.PathEscape(jobID)+"/evaluate", nil, &resp, q) if err != nil { return "", nil, err } @@ -258,7 +258,7 @@ func (j *Jobs) EvaluateWithOpts(jobID string, opts EvalOptions, q *WriteOptions) } var resp JobRegisterResponse - wm, err := j.client.write("/v1/job/"+jobID+"/evaluate", req, &resp, q) + wm, err := j.client.write("/v1/job/"+url.PathEscape(jobID)+"/evaluate", req, &resp, q) if err != nil { return "", nil, err } @@ -268,7 +268,7 @@ func (j *Jobs) EvaluateWithOpts(jobID string, opts EvalOptions, q *WriteOptions) // PeriodicForce spawns a new instance of the periodic job and returns the eval ID func (j *Jobs) PeriodicForce(jobID string, q *WriteOptions) (string, *WriteMeta, error) { var resp periodicForceResponse - wm, err := j.client.write("/v1/job/"+jobID+"/periodic/force", nil, &resp, q) + wm, err := j.client.write("/v1/job/"+url.PathEscape(jobID)+"/periodic/force", nil, &resp, q) if err != nil { return "", nil, err } @@ -301,7 +301,7 @@ func (j *Jobs) PlanOpts(job *Job, opts *PlanOptions, q *WriteOptions) (*JobPlanR } var resp JobPlanResponse - wm, err := j.client.write("/v1/job/"+*job.ID+"/plan", req, &resp, q) + wm, err := j.client.write("/v1/job/"+url.PathEscape(*job.ID)+"/plan", req, &resp, q) if err != nil { return nil, nil, err } @@ -310,7 +310,7 @@ func (j *Jobs) PlanOpts(job *Job, opts *PlanOptions, q *WriteOptions) (*JobPlanR func (j *Jobs) Summary(jobID string, q *QueryOptions) (*JobSummary, *QueryMeta, error) { var resp JobSummary - qm, err := j.client.query("/v1/job/"+jobID+"/summary", &resp, q) + qm, err := j.client.query("/v1/job/"+url.PathEscape(jobID)+"/summary", &resp, q) if err != nil { return nil, nil, err } @@ -325,7 +325,7 @@ func (j *Jobs) Dispatch(jobID string, meta map[string]string, Meta: meta, Payload: payload, } - wm, err := j.client.write("/v1/job/"+jobID+"/dispatch", req, &resp, q) + wm, err := j.client.write("/v1/job/"+url.PathEscape(jobID)+"/dispatch", req, &resp, q) if err != nil { return nil, nil, err } @@ -345,7 +345,7 @@ func (j *Jobs) Revert(jobID string, version uint64, enforcePriorVersion *uint64, EnforcePriorVersion: enforcePriorVersion, VaultToken: vaultToken, } - wm, err := j.client.write("/v1/job/"+jobID+"/revert", req, &resp, q) + wm, err := j.client.write("/v1/job/"+url.PathEscape(jobID)+"/revert", req, &resp, q) if err != nil { return nil, nil, err } @@ -362,7 +362,7 @@ func (j *Jobs) Stable(jobID string, version uint64, stable bool, JobVersion: version, Stable: stable, } - wm, err := j.client.write("/v1/job/"+jobID+"/stable", req, &resp, q) + wm, err := j.client.write("/v1/job/"+url.PathEscape(jobID)+"/stable", req, &resp, q) if err != nil { return nil, nil, err } diff --git a/api/jobs_test.go b/api/jobs_test.go index 34de8e8d0..9ec503954 100644 --- a/api/jobs_test.go +++ b/api/jobs_test.go @@ -876,13 +876,15 @@ func TestJobs_Info(t *testing.T) { // Trying to retrieve a job by ID before it exists // returns an error - _, _, err := jobs.Info("job1", nil) + id := "job-id/with\\troublesome:characters\n?&字\000" + _, _, err := jobs.Info(id, nil) if err == nil || !strings.Contains(err.Error(), "not found") { t.Fatalf("expected not found error, got: %#v", err) } // Register the job job := testJob() + job.ID = &id _, wm, err := jobs.Register(job, nil) if err != nil { t.Fatalf("err: %s", err) @@ -890,7 +892,7 @@ func TestJobs_Info(t *testing.T) { assertWriteMeta(t, wm) // Query the job again and ensure it exists - result, qm, err := jobs.Info("job1", nil) + result, qm, err := jobs.Info(id, nil) if err != nil { t.Fatalf("err: %s", err) }