Skip to content

Commit d6a51f1

Browse files
authored
Merge pull request #52 from bidhan-a/issue-30
Modify, Delete Pipeline feature (#30)
2 parents 17b7873 + d08125e commit d6a51f1

File tree

8 files changed

+678
-9
lines changed

8 files changed

+678
-9
lines changed

frontend/client/views/settings/index.vue

+183-4
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,51 @@
4444
</div>
4545
</div>
4646
</tab-pane>
47-
<!--<tab-pane label="Manage Pipelines" icon="fa fa-wrench"></tab-pane>-->
47+
<tab-pane label="Manage Pipelines" icon="fa fa-wrench">
48+
<div class="tile is-ancestor">
49+
<div class="tile is-vertical">
50+
<div class="tile is-parent">
51+
<a class="button is-primary" v-on:click="createPipeline" style="margin-bottom: -10px;">
52+
<span class="icon">
53+
<i class="fa fa-plus"></i>
54+
</span>
55+
<span>Create Pipeline</span>
56+
</a>
57+
</div>
58+
<div class="tile is-parent">
59+
<article class="tile is-child notification content-article box">
60+
<vue-good-table
61+
:columns="pipelineColumns"
62+
:rows="pipelineRows"
63+
:paginate="true"
64+
:global-search="true"
65+
:defaultSortBy="{field: 'id', type: 'desc'}"
66+
globalSearchPlaceholder="Search ..."
67+
styleClass="table table-own-bordered">
68+
<template slot="table-row" slot-scope="props">
69+
<td>
70+
<span>{{ props.row.name }}</span>
71+
</td>
72+
<td>
73+
<span>{{ props.row.type }}</span>
74+
</td>
75+
<td>
76+
<span>{{ convertTime(props.row.created) }}</span>
77+
</td>
78+
<td>
79+
<a v-on:click="editPipelineModal(props.row)"><i class="fa fa-edit" style="color: whitesmoke;"></i></a>
80+
<a v-on:click="deletePipelineModal(props.row)"><i class="fa fa-trash" style="color: whitesmoke;"></i></a>
81+
</td>
82+
</template>
83+
<div slot="emptystate" class="empty-table-text">
84+
No active pipelines.
85+
</div>
86+
</vue-good-table>
87+
</article>
88+
</div>
89+
</div>
90+
</div>
91+
</tab-pane>
4892
</tabs>
4993
</div>
5094

@@ -157,6 +201,57 @@
157201
</div>
158202
</div>
159203
</modal>
204+
205+
<!-- edit pipeline modal -->
206+
<modal :visible="showEditPipelineModal" class="modal-z-index" @close="close">
207+
<div class="box pipeline-modal">
208+
<div class="block pipeline-modal-content">
209+
<collapse accordion is-fullwidth>
210+
<collapse-item title="Change Pipeline Name" selected>
211+
<div class="pipeline-modal-content">
212+
<p class="control has-icons-left" style="padding-bottom: 5px;">
213+
<input class="input is-medium input-bar" v-focus v-model="selectPipeline.name" placeholder="Pipeline Name">
214+
<span class="icon is-small is-left">
215+
<i class="fa fa-book"></i>
216+
</span>
217+
</p>
218+
</div>
219+
</collapse-item>
220+
</collapse>
221+
<div class="modal-footer">
222+
<div style="float: left;">
223+
<button class="button is-primary" v-on:click="changePipelineName">Change Name</button>
224+
</div>
225+
<div style="float: right;">
226+
<button class="button is-danger" v-on:click="close">Cancel</button>
227+
</div>
228+
</div>
229+
</div>
230+
</div>
231+
</modal>
232+
233+
<!-- delete pipeline modal -->
234+
<modal :visible="showDeletePipelineModal" class="modal-z-index" @close="close">
235+
<div class="box pipeline-modal">
236+
<article class="media">
237+
<div class="media-content">
238+
<div class="content">
239+
<p>
240+
<span style="color: whitesmoke;">Do you really want to delete the pipeline "{{ selectPipeline.name }}"?</span>
241+
</p>
242+
</div>
243+
<div class="modal-footer">
244+
<div style="float: left;">
245+
<button class="button is-primary" v-on:click="deletePipeline" style="width:150px;">Yes</button>
246+
</div>
247+
<div style="float: right;">
248+
<button class="button is-danger" v-on:click="close" style="width:130px;">No</button>
249+
</div>
250+
</div>
251+
</div>
252+
</article>
253+
</div>
254+
</modal>
160255
</div>
161256
</template>
162257

@@ -214,10 +309,31 @@ export default {
214309
}
215310
],
216311
userRows: [],
312+
pipelineColumns: [
313+
{
314+
label: 'Name',
315+
field: 'name'
316+
},
317+
{
318+
label: 'Type',
319+
field: 'type'
320+
},
321+
{
322+
label: 'Created',
323+
field: 'created'
324+
},
325+
{
326+
label: ''
327+
}
328+
],
329+
pipelineRows: [],
217330
selectUser: {},
331+
selectPipeline: {},
218332
showEditUserModal: false,
219333
showDeleteUserModal: false,
220-
showAddUserModal: false
334+
showAddUserModal: false,
335+
showEditPipelineModal: false,
336+
showDeletePipelineModal: false
221337
}
222338
},
223339
@@ -245,6 +361,17 @@ export default {
245361
.catch((error) => {
246362
this.$onError(error)
247363
})
364+
this.$http
365+
.get('/api/v1/pipeline', { showProgressBar: false })
366+
.then(response => {
367+
if (response.data) {
368+
this.pipelineRows = response.data;
369+
} else {
370+
this.pipelineRows = [];
371+
}
372+
}).catch((error) => {
373+
this.$onError(error)
374+
})
248375
},
249376
250377
convertTime (time) {
@@ -266,11 +393,24 @@ export default {
266393
this.showAddUserModal = true
267394
},
268395
396+
editPipelineModal (pipeline) {
397+
this.selectPipeline = pipeline
398+
this.showEditPipelineModal = true
399+
},
400+
401+
deletePipelineModal (pipeline) {
402+
this.selectPipeline = pipeline
403+
this.showDeletePipelineModal = true
404+
},
405+
269406
close () {
270407
this.showEditUserModal = false
271408
this.showDeleteUserModal = false
272409
this.showAddUserModal = false
273410
this.selectUser = {}
411+
this.showEditPipelineModal = false
412+
this.showDeletePipelineModal = false
413+
this.selectPipeline = {}
274414
this.$emit('close')
275415
},
276416
@@ -372,6 +512,45 @@ export default {
372512
.catch((error) => {
373513
this.$onError(error)
374514
})
515+
},
516+
517+
createPipeline () {
518+
this.$router.push('/pipeline/create')
519+
},
520+
521+
changePipelineName () {
522+
this.$http
523+
.put('/api/v1/pipeline/' + this.selectPipeline.id, this.selectPipeline)
524+
.then(response => {
525+
openNotification({
526+
title: 'Pipeline updated!',
527+
message: 'Pipeline has been successfully updated.',
528+
type: 'success'
529+
})
530+
this.fetchData()
531+
this.close()
532+
})
533+
.catch((error) => {
534+
this.$onError(error)
535+
})
536+
this.close()
537+
},
538+
539+
deletePipeline () {
540+
this.$http
541+
.delete('/api/v1/pipeline/' + this.selectPipeline.id)
542+
.then(response => {
543+
openNotification({
544+
title: 'Pipeline deleted!',
545+
message: 'Pipeline ' + this.selectPipeline.name + ' has been successfully deleted.',
546+
type: 'success'
547+
})
548+
this.fetchData()
549+
this.close()
550+
})
551+
.catch((error) => {
552+
this.$onError(error)
553+
})
375554
}
376555
}
377556
}
@@ -393,12 +572,12 @@ export default {
393572
border-bottom-color: #4da2fc !important;
394573
}
395574
396-
.user-modal {
575+
.user-modal, .pipeline-modal {
397576
text-align: center;
398577
background-color: #2a2735;
399578
}
400579
401-
.user-modal-content {
580+
.user-modal-content, .pipeline-modal-content {
402581
margin: auto;
403582
padding: 10px;
404583
}

handlers/handler.go

+8
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ var (
4141

4242
// errLogNotFound is thrown when a job log file was not found
4343
errLogNotFound = errors.New("job log file not found")
44+
45+
// errPipelineDelete is thrown when a pipeline binary could not be deleted
46+
errPipelineDelete = errors.New("pipeline could not be deleted. Perhaps you don't have the right permissions")
47+
48+
// errPipelineRename is thrown when a pipeline binary could not be renamed
49+
errPipelineRename = errors.New("pipeline could not be renamed")
4450
)
4551

4652
// storeService is an instance of store.
@@ -74,6 +80,8 @@ func InitHandlers(e *echo.Echo, store *store.Store, scheduler *scheduler.Schedul
7480
e.GET(p+"pipeline/name", PipelineNameAvailable)
7581
e.GET(p+"pipeline", PipelineGetAll)
7682
e.GET(p+"pipeline/:pipelineid", PipelineGet)
83+
e.PUT(p+"pipeline/:pipelineid", PipelineUpdate)
84+
e.DELETE(p+"pipeline/:pipelineid", PipelineDelete)
7785
e.POST(p+"pipeline/:pipelineid/start", PipelineStart)
7886
e.GET(p+"pipeline/latest", PipelineGetAllWithLatestRun)
7987

handlers/pipeline.go

+93
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,99 @@ func PipelineGet(c echo.Context) error {
136136
return c.String(http.StatusNotFound, errPipelineNotFound.Error())
137137
}
138138

139+
// PipelineUpdate updates the given pipeline.
140+
func PipelineUpdate(c echo.Context) error {
141+
p := gaia.Pipeline{}
142+
if err := c.Bind(&p); err != nil {
143+
return c.String(http.StatusBadRequest, err.Error())
144+
}
145+
146+
// Look up pipeline for the given id
147+
var foundPipeline gaia.Pipeline
148+
for pipeline := range pipeline.GlobalActivePipelines.Iter() {
149+
if pipeline.ID == p.ID {
150+
foundPipeline = pipeline
151+
}
152+
}
153+
154+
if foundPipeline.Name == "" {
155+
return c.String(http.StatusNotFound, errPipelineNotFound.Error())
156+
}
157+
158+
// We're only handling pipeline name updates for now.
159+
if foundPipeline.Name != p.Name {
160+
// Pipeline name has been changed
161+
162+
currentName := foundPipeline.Name
163+
164+
// Rename binary
165+
err := pipeline.RenameBinary(foundPipeline, p.Name)
166+
if err != nil {
167+
return c.String(http.StatusInternalServerError, errPipelineRename.Error())
168+
}
169+
170+
// Update name and exec path
171+
foundPipeline.Name = p.Name
172+
foundPipeline.ExecPath = pipeline.GetExecPath(p)
173+
174+
// Update pipeline in store
175+
err = storeService.PipelinePut(&foundPipeline)
176+
if err != nil {
177+
return c.String(http.StatusInternalServerError, err.Error())
178+
}
179+
180+
// Update active pipelines
181+
pipeline.GlobalActivePipelines.ReplaceByName(currentName, foundPipeline)
182+
183+
}
184+
185+
return c.String(http.StatusOK, "Pipeline has been updated")
186+
}
187+
188+
// PipelineDelete accepts a pipeline id and deletes it from the
189+
// store. It also removes the binary inside the pipeline folder.
190+
func PipelineDelete(c echo.Context) error {
191+
pipelineIDStr := c.Param("pipelineid")
192+
193+
pipelineID, err := strconv.Atoi(pipelineIDStr)
194+
if err != nil {
195+
return c.String(http.StatusBadRequest, errInvalidPipelineID.Error())
196+
}
197+
198+
// Look up pipeline for the given id
199+
var foundPipeline gaia.Pipeline
200+
var index int
201+
var deletedPipelineIndex int
202+
for pipeline := range pipeline.GlobalActivePipelines.Iter() {
203+
if pipeline.ID == pipelineID {
204+
foundPipeline = pipeline
205+
deletedPipelineIndex = index
206+
}
207+
index++
208+
}
209+
210+
if foundPipeline.Name == "" {
211+
return c.String(http.StatusNotFound, errPipelineNotFound.Error())
212+
}
213+
214+
// Delete pipeline binary
215+
err = pipeline.DeleteBinary(foundPipeline)
216+
if err != nil {
217+
return c.String(http.StatusInternalServerError, errPipelineDelete.Error())
218+
}
219+
220+
// Delete pipeline from store
221+
err = storeService.PipelineDelete(pipelineID)
222+
if err != nil {
223+
return c.String(http.StatusInternalServerError, err.Error())
224+
}
225+
226+
// Remove from active pipelines
227+
pipeline.GlobalActivePipelines.Remove(deletedPipelineIndex)
228+
229+
return c.String(http.StatusOK, "Pipeline has been deleted")
230+
}
231+
139232
// PipelineStart starts a pipeline by the given id.
140233
// Afterwards it returns the created/scheduled pipeline run.
141234
func PipelineStart(c echo.Context) error {

0 commit comments

Comments
 (0)