Skip to content

Commit

Permalink
Merge pull request #52 from bidhan-a/issue-30
Browse files Browse the repository at this point in the history
Modify, Delete Pipeline feature (#30)
  • Loading branch information
michelvocks committed Jul 25, 2018
2 parents 17b7873 + d08125e commit d6a51f1
Show file tree
Hide file tree
Showing 8 changed files with 678 additions and 9 deletions.
187 changes: 183 additions & 4 deletions frontend/client/views/settings/index.vue
Expand Up @@ -44,7 +44,51 @@
</div>
</div>
</tab-pane>
<!--<tab-pane label="Manage Pipelines" icon="fa fa-wrench"></tab-pane>-->
<tab-pane label="Manage Pipelines" icon="fa fa-wrench">
<div class="tile is-ancestor">
<div class="tile is-vertical">
<div class="tile is-parent">
<a class="button is-primary" v-on:click="createPipeline" style="margin-bottom: -10px;">
<span class="icon">
<i class="fa fa-plus"></i>
</span>
<span>Create Pipeline</span>
</a>
</div>
<div class="tile is-parent">
<article class="tile is-child notification content-article box">
<vue-good-table
:columns="pipelineColumns"
:rows="pipelineRows"
:paginate="true"
:global-search="true"
:defaultSortBy="{field: 'id', type: 'desc'}"
globalSearchPlaceholder="Search ..."
styleClass="table table-own-bordered">
<template slot="table-row" slot-scope="props">
<td>
<span>{{ props.row.name }}</span>
</td>
<td>
<span>{{ props.row.type }}</span>
</td>
<td>
<span>{{ convertTime(props.row.created) }}</span>
</td>
<td>
<a v-on:click="editPipelineModal(props.row)"><i class="fa fa-edit" style="color: whitesmoke;"></i></a>
<a v-on:click="deletePipelineModal(props.row)"><i class="fa fa-trash" style="color: whitesmoke;"></i></a>
</td>
</template>
<div slot="emptystate" class="empty-table-text">
No active pipelines.
</div>
</vue-good-table>
</article>
</div>
</div>
</div>
</tab-pane>
</tabs>
</div>

Expand Down Expand Up @@ -157,6 +201,57 @@
</div>
</div>
</modal>

<!-- edit pipeline modal -->
<modal :visible="showEditPipelineModal" class="modal-z-index" @close="close">
<div class="box pipeline-modal">
<div class="block pipeline-modal-content">
<collapse accordion is-fullwidth>
<collapse-item title="Change Pipeline Name" selected>
<div class="pipeline-modal-content">
<p class="control has-icons-left" style="padding-bottom: 5px;">
<input class="input is-medium input-bar" v-focus v-model="selectPipeline.name" placeholder="Pipeline Name">
<span class="icon is-small is-left">
<i class="fa fa-book"></i>
</span>
</p>
</div>
</collapse-item>
</collapse>
<div class="modal-footer">
<div style="float: left;">
<button class="button is-primary" v-on:click="changePipelineName">Change Name</button>
</div>
<div style="float: right;">
<button class="button is-danger" v-on:click="close">Cancel</button>
</div>
</div>
</div>
</div>
</modal>

<!-- delete pipeline modal -->
<modal :visible="showDeletePipelineModal" class="modal-z-index" @close="close">
<div class="box pipeline-modal">
<article class="media">
<div class="media-content">
<div class="content">
<p>
<span style="color: whitesmoke;">Do you really want to delete the pipeline "{{ selectPipeline.name }}"?</span>
</p>
</div>
<div class="modal-footer">
<div style="float: left;">
<button class="button is-primary" v-on:click="deletePipeline" style="width:150px;">Yes</button>
</div>
<div style="float: right;">
<button class="button is-danger" v-on:click="close" style="width:130px;">No</button>
</div>
</div>
</div>
</article>
</div>
</modal>
</div>
</template>

Expand Down Expand Up @@ -214,10 +309,31 @@ export default {
}
],
userRows: [],
pipelineColumns: [
{
label: 'Name',
field: 'name'
},
{
label: 'Type',
field: 'type'
},
{
label: 'Created',
field: 'created'
},
{
label: ''
}
],
pipelineRows: [],
selectUser: {},
selectPipeline: {},
showEditUserModal: false,
showDeleteUserModal: false,
showAddUserModal: false
showAddUserModal: false,
showEditPipelineModal: false,
showDeletePipelineModal: false
}
},
Expand Down Expand Up @@ -245,6 +361,17 @@ export default {
.catch((error) => {
this.$onError(error)
})
this.$http
.get('/api/v1/pipeline', { showProgressBar: false })
.then(response => {
if (response.data) {
this.pipelineRows = response.data;
} else {
this.pipelineRows = [];
}
}).catch((error) => {
this.$onError(error)
})
},
convertTime (time) {
Expand All @@ -266,11 +393,24 @@ export default {
this.showAddUserModal = true
},
editPipelineModal (pipeline) {
this.selectPipeline = pipeline
this.showEditPipelineModal = true
},
deletePipelineModal (pipeline) {
this.selectPipeline = pipeline
this.showDeletePipelineModal = true
},
close () {
this.showEditUserModal = false
this.showDeleteUserModal = false
this.showAddUserModal = false
this.selectUser = {}
this.showEditPipelineModal = false
this.showDeletePipelineModal = false
this.selectPipeline = {}
this.$emit('close')
},
Expand Down Expand Up @@ -372,6 +512,45 @@ export default {
.catch((error) => {
this.$onError(error)
})
},
createPipeline () {
this.$router.push('/pipeline/create')
},
changePipelineName () {
this.$http
.put('/api/v1/pipeline/' + this.selectPipeline.id, this.selectPipeline)
.then(response => {
openNotification({
title: 'Pipeline updated!',
message: 'Pipeline has been successfully updated.',
type: 'success'
})
this.fetchData()
this.close()
})
.catch((error) => {
this.$onError(error)
})
this.close()
},
deletePipeline () {
this.$http
.delete('/api/v1/pipeline/' + this.selectPipeline.id)
.then(response => {
openNotification({
title: 'Pipeline deleted!',
message: 'Pipeline ' + this.selectPipeline.name + ' has been successfully deleted.',
type: 'success'
})
this.fetchData()
this.close()
})
.catch((error) => {
this.$onError(error)
})
}
}
}
Expand All @@ -393,12 +572,12 @@ export default {
border-bottom-color: #4da2fc !important;
}
.user-modal {
.user-modal, .pipeline-modal {
text-align: center;
background-color: #2a2735;
}
.user-modal-content {
.user-modal-content, .pipeline-modal-content {
margin: auto;
padding: 10px;
}
Expand Down
8 changes: 8 additions & 0 deletions handlers/handler.go
Expand Up @@ -41,6 +41,12 @@ var (

// errLogNotFound is thrown when a job log file was not found
errLogNotFound = errors.New("job log file not found")

// errPipelineDelete is thrown when a pipeline binary could not be deleted
errPipelineDelete = errors.New("pipeline could not be deleted. Perhaps you don't have the right permissions")

// errPipelineRename is thrown when a pipeline binary could not be renamed
errPipelineRename = errors.New("pipeline could not be renamed")
)

// storeService is an instance of store.
Expand Down Expand Up @@ -74,6 +80,8 @@ func InitHandlers(e *echo.Echo, store *store.Store, scheduler *scheduler.Schedul
e.GET(p+"pipeline/name", PipelineNameAvailable)
e.GET(p+"pipeline", PipelineGetAll)
e.GET(p+"pipeline/:pipelineid", PipelineGet)
e.PUT(p+"pipeline/:pipelineid", PipelineUpdate)
e.DELETE(p+"pipeline/:pipelineid", PipelineDelete)
e.POST(p+"pipeline/:pipelineid/start", PipelineStart)
e.GET(p+"pipeline/latest", PipelineGetAllWithLatestRun)

Expand Down
93 changes: 93 additions & 0 deletions handlers/pipeline.go
Expand Up @@ -136,6 +136,99 @@ func PipelineGet(c echo.Context) error {
return c.String(http.StatusNotFound, errPipelineNotFound.Error())
}

// PipelineUpdate updates the given pipeline.
func PipelineUpdate(c echo.Context) error {
p := gaia.Pipeline{}
if err := c.Bind(&p); err != nil {
return c.String(http.StatusBadRequest, err.Error())
}

// Look up pipeline for the given id
var foundPipeline gaia.Pipeline
for pipeline := range pipeline.GlobalActivePipelines.Iter() {
if pipeline.ID == p.ID {
foundPipeline = pipeline
}
}

if foundPipeline.Name == "" {
return c.String(http.StatusNotFound, errPipelineNotFound.Error())
}

// We're only handling pipeline name updates for now.
if foundPipeline.Name != p.Name {
// Pipeline name has been changed

currentName := foundPipeline.Name

// Rename binary
err := pipeline.RenameBinary(foundPipeline, p.Name)
if err != nil {
return c.String(http.StatusInternalServerError, errPipelineRename.Error())
}

// Update name and exec path
foundPipeline.Name = p.Name
foundPipeline.ExecPath = pipeline.GetExecPath(p)

// Update pipeline in store
err = storeService.PipelinePut(&foundPipeline)
if err != nil {
return c.String(http.StatusInternalServerError, err.Error())
}

// Update active pipelines
pipeline.GlobalActivePipelines.ReplaceByName(currentName, foundPipeline)

}

return c.String(http.StatusOK, "Pipeline has been updated")
}

// PipelineDelete accepts a pipeline id and deletes it from the
// store. It also removes the binary inside the pipeline folder.
func PipelineDelete(c echo.Context) error {
pipelineIDStr := c.Param("pipelineid")

pipelineID, err := strconv.Atoi(pipelineIDStr)
if err != nil {
return c.String(http.StatusBadRequest, errInvalidPipelineID.Error())
}

// Look up pipeline for the given id
var foundPipeline gaia.Pipeline
var index int
var deletedPipelineIndex int
for pipeline := range pipeline.GlobalActivePipelines.Iter() {
if pipeline.ID == pipelineID {
foundPipeline = pipeline
deletedPipelineIndex = index
}
index++
}

if foundPipeline.Name == "" {
return c.String(http.StatusNotFound, errPipelineNotFound.Error())
}

// Delete pipeline binary
err = pipeline.DeleteBinary(foundPipeline)
if err != nil {
return c.String(http.StatusInternalServerError, errPipelineDelete.Error())
}

// Delete pipeline from store
err = storeService.PipelineDelete(pipelineID)
if err != nil {
return c.String(http.StatusInternalServerError, err.Error())
}

// Remove from active pipelines
pipeline.GlobalActivePipelines.Remove(deletedPipelineIndex)

return c.String(http.StatusOK, "Pipeline has been deleted")
}

// PipelineStart starts a pipeline by the given id.
// Afterwards it returns the created/scheduled pipeline run.
func PipelineStart(c echo.Context) error {
Expand Down

0 comments on commit d6a51f1

Please sign in to comment.