Skip to content

Commit a508eac

Browse files
author
Jiří Suchomel
authored
Merge pull request #48 from flaviodsr/assign_list
Implement workflow list assignments
2 parents 4687e8c + 9795c67 commit a508eac

File tree

6 files changed

+347
-81
lines changed

6 files changed

+347
-81
lines changed

design/workflow.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ var _ = Service("workflow", func() {
145145
Field(4, "status", String, "status of the workflow runs to list", func() {
146146
Enum("Started", "Running", "Cancelled", "Succeeded", "Failed", "Completed", "Timeout")
147147
Example("Succeeded")
148+
148149
})
149150
})
150151

@@ -168,6 +169,29 @@ var _ = Service("workflow", func() {
168169
Response(CodeOK)
169170
Response("NotFound", CodeNotFound)
170171
})
172+
173+
})
174+
175+
Method("listAssignments", func() {
176+
Description("List workflow assignments to codesets")
177+
178+
Payload(func() {
179+
Field(1, "name", String, "Name of the workflow to list assignments", func() {
180+
Example("mlflow-sklearn-e2e")
181+
})
182+
})
183+
184+
Result(ArrayOf(WorkflowAssignment), "Return a list of workflow assignments.")
185+
186+
HTTP(func() {
187+
GET("/workflows/assignments")
188+
Param("name")
189+
Response(StatusOK)
190+
})
191+
192+
GRPC(func() {
193+
Response(CodeOK)
194+
})
171195
})
172196
})
173197

@@ -316,3 +340,17 @@ var WorkflowRunOutput = Type("WorkflowRunOutput", func() {
316340
Field(1, "output", WorkflowOutput, "The workflow output")
317341
Field(2, "value", String, "The output value set by the Workflow run")
318342
})
343+
344+
// WorkflowAssignment describes the assignment between a workflow and codesets
345+
var WorkflowAssignment = Type("WorkflowAssignment", func() {
346+
Field(1, "workflow", String, "Workflow assigned to the codeset")
347+
Field(2, "status", WorkflowAssignmentStatus, "The status of the assignment")
348+
Field(3, "codesets", ArrayOf(Codeset), "Codesets assigned to the workflow")
349+
})
350+
351+
// WorkflowAssignmentStatus describes the status of the resource responsible for the
352+
// assignment between a workflow and codesets
353+
var WorkflowAssignmentStatus = Type("WorkflowAssignmentStatus", func() {
354+
Field(1, "available", Boolean, "The state of the assignment")
355+
Field(2, "URL", String, "Dashboard URL to the resource responsible for the assignment")
356+
})

pkg/core/tekton/tekton.go

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func (w *WorkflowBackend) CreateWorkflow(ctx context.Context, logger *log.Logger
7878
}
7979

8080
// CreateWorkflowRun creates a PipelineRun with its default values for received workflow and codeset
81-
func (w *WorkflowBackend) CreateWorkflowRun(ctx context.Context, workflowName string, codeset domain.Codeset) error {
81+
func (w *WorkflowBackend) CreateWorkflowRun(ctx context.Context, workflowName string, codeset *domain.Codeset) error {
8282
pipeline, err := w.tektonClients.PipelineClient.Get(ctx, workflowName, metav1.GetOptions{})
8383
if err != nil {
8484
return fmt.Errorf("error getting tekton pipeline %q: %w", workflowName, err)
@@ -124,63 +124,84 @@ func (w *WorkflowBackend) ListWorkflowRuns(ctx context.Context, wf workflow.Work
124124
return workflowRuns, nil
125125
}
126126

127-
// CreateListener creates tekton resources required to have a listener ready for triggering the pipeline
128-
func (w *WorkflowBackend) CreateListener(ctx context.Context, logger *log.Logger, workflowName string, wait bool) (string, error) {
127+
// CreateWorkflowListener creates tekton resources required to have a listener ready for triggering the pipeline
128+
func (w *WorkflowBackend) CreateWorkflowListener(ctx context.Context, logger *log.Logger, workflowName string, wait bool) (*domain.WorkflowListener, error) {
129129
pipeline, err := w.tektonClients.PipelineClient.Get(ctx, workflowName, metav1.GetOptions{})
130130
if err != nil {
131-
return "", fmt.Errorf("error getting tekton pipeline %q: %w", workflowName, err)
131+
return nil, fmt.Errorf("error getting tekton pipeline %q: %w", workflowName, err)
132132
}
133133

134134
triggerTemplate := generateTriggerTemplate(pipeline)
135135
_, err = w.tektonClients.TriggerTemplateClient.Get(ctx, workflowName, metav1.GetOptions{})
136136
if err != nil {
137137
if !k8serr.IsNotFound(err) {
138-
return "", fmt.Errorf("error getting tekton trigger template %q: %w", workflowName, err)
138+
return nil, fmt.Errorf("error getting tekton trigger template %q: %w", workflowName, err)
139139
}
140140
logger.Printf("Creating tekton trigger template for workflow: %s...", workflowName)
141141
_, err := w.tektonClients.TriggerTemplateClient.Create(ctx, triggerTemplate, metav1.CreateOptions{})
142142
if err != nil {
143-
return "", fmt.Errorf("error creating tekton trigger template %q: %w", workflowName, err)
143+
return nil, fmt.Errorf("error creating tekton trigger template %q: %w", workflowName, err)
144144
}
145145
}
146146

147147
triggerBinding := generateTriggerBinding(triggerTemplate)
148148
_, err = w.tektonClients.TriggerBindingClient.Get(ctx, workflowName, metav1.GetOptions{})
149149
if err != nil {
150150
if !k8serr.IsNotFound(err) {
151-
return "", fmt.Errorf("error getting tekton trigger binding %q: %w", workflowName, err)
151+
return nil, fmt.Errorf("error getting tekton trigger binding %q: %w", workflowName, err)
152152
}
153153
logger.Printf("Creating tekton trigger binding for workflow: %s...", workflowName)
154154
_, err := w.tektonClients.TriggerBindingClient.Create(ctx, triggerBinding, metav1.CreateOptions{})
155155
if err != nil {
156-
return "", fmt.Errorf("error creating tekton trigger binding %q: %w", workflowName, err)
156+
return nil, fmt.Errorf("error creating tekton trigger binding %q: %w", workflowName, err)
157157
}
158158
}
159159

160160
eventListener := generateEventListener(triggerTemplate, triggerBinding)
161161
el, err := w.tektonClients.EventListenerClient.Get(ctx, workflowName, metav1.GetOptions{})
162162
if err != nil {
163163
if !k8serr.IsNotFound(err) {
164-
return "", fmt.Errorf("error getting tekton event listener %q: %w", workflowName, err)
164+
return nil, fmt.Errorf("error getting tekton event listener %q: %w", workflowName, err)
165165
}
166166
logger.Printf("Creating tekton event listener for workflow: %s...", workflowName)
167167
el, err = w.tektonClients.EventListenerClient.Create(ctx, eventListener, metav1.CreateOptions{})
168168
if err != nil {
169-
return "", fmt.Errorf("error creating tekton event listener %q: %w", workflowName, err)
169+
return nil, fmt.Errorf("error creating tekton event listener %q: %w", workflowName, err)
170170
}
171171
}
172172

173+
url := fmt.Sprintf("http://el-%s.%s.svc.cluster.local:8080", workflowName, w.namespace)
173174
if wait {
174175
interval := 1 * time.Second
175176
timeout := 1 * time.Minute
176177
if err := waitFor(w.eventListenerReady(ctx, el.Name), interval, timeout); err != nil {
177-
return "", fmt.Errorf("event listener %q did not get ready in the expected time: %w", el.Name, err)
178+
return nil, fmt.Errorf("event listener %q did not get ready in the expected time: %w", el.Name, err)
178179
}
180+
el, err = w.tektonClients.EventListenerClient.Get(ctx, workflowName, metav1.GetOptions{})
181+
if err != nil {
182+
return nil, fmt.Errorf("error getting tekton event listener state %q: %w", workflowName, err)
183+
}
184+
url = el.Status.Address.URL.String()
185+
}
186+
available := listenerIsAvailable(el.Status)
187+
return &domain.WorkflowListener{Name: el.Name, URL: url, Available: available,
188+
DashboardURL: fmt.Sprintf("%s/#/namespaces/%s/eventlisteners/%s", w.dashboardURL, w.namespace, el.Name)}, nil
189+
}
179190

180-
el, _ = w.tektonClients.EventListenerClient.Get(ctx, workflowName, metav1.GetOptions{})
181-
return el.Status.Address.URL.String(), nil
191+
// GetWorkflowListener returns the listener for a given workflow
192+
func (w *WorkflowBackend) GetWorkflowListener(ctx context.Context, logger *log.Logger, workflowName string) (wl *domain.WorkflowListener, err error) {
193+
el, err := w.tektonClients.EventListenerClient.Get(ctx, workflowName, metav1.GetOptions{})
194+
if err != nil {
195+
return nil, fmt.Errorf("error getting tekton event listener %q: %w", workflowName, err)
196+
}
197+
available := listenerIsAvailable(el.Status)
198+
wl = &domain.WorkflowListener{Name: el.Name, Available: available,
199+
DashboardURL: fmt.Sprintf("%s/#/namespaces/%s/eventlisteners/%s", w.dashboardURL, w.namespace, el.Name)}
200+
if available {
201+
url := el.Status.Address.URL.String()
202+
wl.URL = url
182203
}
183-
return fmt.Sprintf("http://el-%s.%s.svc.cluster.local:8080", workflowName, w.namespace), nil
204+
return
184205
}
185206

186207
func (e WorkflowBackendErr) Error() string {
@@ -197,23 +218,24 @@ func (w *WorkflowBackend) eventListenerReady(ctx context.Context, name string) w
197218
if err != nil {
198219
return false, nil
199220
}
200-
// No conditions have been set yet
201-
if len(el.Status.Conditions) == 0 {
202-
return false, nil
203-
}
204-
if el.Status.GetCondition(apis.ConditionType(appsv1.DeploymentAvailable)) == nil {
205-
return false, nil
206-
}
207-
for _, cond := range el.Status.Conditions {
208-
if cond.Status != corev1.ConditionTrue {
209-
return false, nil
210-
}
211-
}
212-
if el.Status.Address.URL == nil {
213-
return false, nil
221+
return listenerIsAvailable(el.Status), nil
222+
}
223+
}
224+
225+
func listenerIsAvailable(status v1alpha1.EventListenerStatus) bool {
226+
// No conditions have been set yet
227+
if len(status.Conditions) == 0 {
228+
return false
229+
}
230+
if status.GetCondition(apis.ConditionType(appsv1.DeploymentAvailable)) == nil {
231+
return false
232+
}
233+
for _, cond := range status.Conditions {
234+
if cond.Status != corev1.ConditionTrue {
235+
return false
214236
}
215-
return true, nil
216237
}
238+
return status.Address.URL != nil
217239
}
218240

219241
func generatePipeline(w workflow.Workflow, namespace string) *v1beta1.Pipeline {
@@ -314,7 +336,7 @@ STEPS:
314336
return &pb.Pipeline
315337
}
316338

317-
func generatePipelineRun(p *v1beta1.Pipeline, codeset domain.Codeset) (*v1beta1.PipelineRun, error) {
339+
func generatePipelineRun(p *v1beta1.Pipeline, codeset *domain.Codeset) (*v1beta1.PipelineRun, error) {
318340
codesetVersion := "main"
319341
prb := builder.NewPipelineRunBuilder(fmt.Sprintf("%s%s-%s-", pipelineRunPrefix, codeset.Project, codeset.Name))
320342

0 commit comments

Comments
 (0)