feat: sless_job terraform resource + jobs REST API
Operator (v0.1.4):
- internal/api/handler/jobs.go: CreateJob/GetJob/DeleteJob handlers
- internal/api/router.go: POST/GET/DELETE /v1/namespaces/{ns}/jobs/{name}
Terraform provider (v0.1.2):
- client.go: JobRequest/JobResponse + CreateJob/GetJob/DeleteJob/WaitJobDone
- resources/job_resource.go: sless_job resource (Create blocks until Succeeded/Failed)
- provider.go: register NewJobResource
Deploy:
- operator:v0.1.4 deployed and running in cluster
- provider v0.1.2 published to terra.k8c.ru
This commit is contained in:
@@ -290,3 +290,102 @@ func (c *Client) DeleteTrigger(ctx context.Context, ns, name string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Job CRUD ---
|
||||
|
||||
// JobRequest — тело POST /v1/namespaces/{ns}/jobs
|
||||
type JobRequest struct {
|
||||
Name string `json:"name"`
|
||||
FunctionRef string `json:"function"`
|
||||
EventJSON string `json:"event_json,omitempty"`
|
||||
}
|
||||
|
||||
// JobResponse — ответ GET /v1/namespaces/{ns}/jobs/{name}
|
||||
type JobResponse struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
FunctionRef string `json:"function"`
|
||||
EventJSON string `json:"event_json"`
|
||||
Phase string `json:"phase"`
|
||||
JobName string `json:"job_name"`
|
||||
StartTime string `json:"start_time"`
|
||||
CompletionTime string `json:"completion_time"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// CreateJob — POST /v1/namespaces/{ns}/jobs → 201
|
||||
func (c *Client) CreateJob(ctx context.Context, ns string, req JobRequest) (*JobResponse, error) {
|
||||
url := fmt.Sprintf("%s/v1/namespaces/%s/jobs", c.endpoint, ns)
|
||||
resp, err := c.doJSON(ctx, http.MethodPost, url, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("create job: status %d: %s", resp.StatusCode, body)
|
||||
}
|
||||
var j JobResponse
|
||||
return &j, json.NewDecoder(resp.Body).Decode(&j)
|
||||
}
|
||||
|
||||
// GetJob — GET /v1/namespaces/{ns}/jobs/{name} → nil если 404
|
||||
func (c *Client) GetJob(ctx context.Context, ns, name string) (*JobResponse, error) {
|
||||
url := fmt.Sprintf("%s/v1/namespaces/%s/jobs/%s", c.endpoint, ns, name)
|
||||
resp, err := c.doJSON(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("get job: status %d: %s", resp.StatusCode, body)
|
||||
}
|
||||
var j JobResponse
|
||||
return &j, json.NewDecoder(resp.Body).Decode(&j)
|
||||
}
|
||||
|
||||
// DeleteJob — DELETE /v1/namespaces/{ns}/jobs/{name} → 204
|
||||
func (c *Client) DeleteJob(ctx context.Context, ns, name string) error {
|
||||
url := fmt.Sprintf("%s/v1/namespaces/%s/jobs/%s", c.endpoint, ns, name)
|
||||
resp, err := c.doJSON(ctx, http.MethodDelete, url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusNotFound {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("delete job: status %d: %s", resp.StatusCode, body)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitJobDone опрашивает job каждые 5 секунд пока phase не Succeeded или Failed.
|
||||
// Блокирует terraform apply до завершения джоба.
|
||||
func (c *Client) WaitJobDone(ctx context.Context, ns, name string, timeout time.Duration) (*JobResponse, error) {
|
||||
deadline := time.Now().Add(timeout)
|
||||
for time.Now().Before(deadline) {
|
||||
j, err := c.GetJob(ctx, ns, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if j == nil {
|
||||
return nil, fmt.Errorf("job %s/%s not found while waiting", ns, name)
|
||||
}
|
||||
switch j.Phase {
|
||||
case "Succeeded":
|
||||
return j, nil
|
||||
case "Failed":
|
||||
return nil, fmt.Errorf("job failed: %s", j.Message)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-time.After(5 * time.Second):
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("timeout waiting for job %s/%s to complete", ns, name)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user