Skip to content

Commit 7478733

Browse files
michelvocksSkarlso
authored andcommitted
Implemented pipeline name already in use check (#153)
* Implemented pipeline name already in use check * Removed break from iterate * Implemented pipeline name validation also in create pipeline API * Corrected one line to short if
1 parent 3a95a11 commit 7478733

File tree

4 files changed

+157
-22
lines changed

4 files changed

+157
-22
lines changed

handlers/handler.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"net/http"
88
"strings"
99

10-
"github.com/GeertJohan/go.rice"
10+
rice "github.com/GeertJohan/go.rice"
1111
jwt "github.com/dgrijalva/jwt-go"
1212
"github.com/gaia-pipeline/gaia"
1313
"github.com/labstack/echo"
@@ -18,9 +18,6 @@ var (
1818
// errNotAuthorized is thrown when user wants to access resource which is protected
1919
errNotAuthorized = errors.New("no or invalid jwt token provided. You are not authorized")
2020

21-
// errPathLength is a validation error during pipeline name input
22-
errPathLength = errors.New("name of pipeline is empty or one of the path elements length exceeds 50 characters")
23-
2421
// errPipelineNotFound is thrown when a pipeline was not found with the given id
2522
errPipelineNotFound = errors.New("pipeline not found with the given id")
2623

handlers/pipeline.go

+7-18
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package handlers
33
import (
44
"net/http"
55
"strconv"
6-
"strings"
76
"time"
87

98
"github.com/gaia-pipeline/gaia"
@@ -14,11 +13,6 @@ import (
1413
uuid "github.com/satori/go.uuid"
1514
)
1615

17-
const (
18-
// Split char to separate path from pipeline and name
19-
pipelinePathSplitChar = "/"
20-
)
21-
2216
// PipelineGitLSRemote checks for available git remote branches.
2317
// This is the perfect way to check if we have access to a given repo.
2418
func PipelineGitLSRemote(c echo.Context) error {
@@ -46,6 +40,11 @@ func CreatePipeline(c echo.Context) error {
4640
return c.String(http.StatusBadRequest, err.Error())
4741
}
4842

43+
// Validate pipeline name
44+
if err := pipeline.ValidatePipelineName(p.Pipeline.Name); err != nil {
45+
return c.String(http.StatusBadRequest, err.Error())
46+
}
47+
4948
// Set initial value
5049
p.Created = time.Now()
5150
p.StatusType = gaia.CreatePipelineRunning
@@ -84,18 +83,8 @@ func CreatePipelineGetAll(c echo.Context) error {
8483
// available and valid.
8584
func PipelineNameAvailable(c echo.Context) error {
8685
pName := c.QueryParam("name")
87-
88-
// The name could contain a path. Split it up
89-
path := strings.Split(pName, pipelinePathSplitChar)
90-
91-
// Iterate all objects
92-
for _, s := range path {
93-
// Length should be correct
94-
if len(s) < 1 || len(s) > 50 {
95-
return c.String(http.StatusBadRequest, errPathLength.Error())
96-
}
97-
98-
// TODO check if pipeline name is already in use
86+
if err := pipeline.ValidatePipelineName(pName); err != nil {
87+
return c.String(http.StatusBadRequest, err.Error())
9988
}
10089

10190
return nil

handlers/pipeline_test.go

+107
Original file line numberDiff line numberDiff line change
@@ -468,3 +468,110 @@ func TestPipelineCheckPeriodicSchedules(t *testing.T) {
468468
}
469469
})
470470
}
471+
472+
func TestPipelineNameAvailable(t *testing.T) {
473+
tmp, _ := ioutil.TempDir("", "TestPipelineNameAvailable")
474+
dataDir := tmp
475+
476+
gaia.Cfg = &gaia.Config{
477+
Logger: hclog.NewNullLogger(),
478+
HomePath: dataDir,
479+
DataPath: dataDir,
480+
PipelinePath: dataDir,
481+
}
482+
483+
// Initialize store
484+
dataStore, _ := services.StorageService()
485+
dataStore.Init()
486+
defer func() { services.MockStorageService(nil) }()
487+
488+
// Initialize global active pipelines
489+
ap := pipeline.NewActivePipelines()
490+
pipeline.GlobalActivePipelines = ap
491+
492+
// Initialize echo
493+
e := echo.New()
494+
InitHandlers(e)
495+
496+
p := gaia.Pipeline{
497+
ID: 1,
498+
Name: "Pipeline A",
499+
Type: gaia.PTypeGolang,
500+
Created: time.Now(),
501+
}
502+
503+
// Add to store
504+
err := dataStore.PipelinePut(&p)
505+
if err != nil {
506+
t.Fatal(err)
507+
}
508+
// Add to active pipelines
509+
ap.Append(p)
510+
511+
t.Run("fails for pipeline name already in use", func(t *testing.T) {
512+
req := httptest.NewRequest(echo.GET, "/", nil)
513+
req.Header.Set("Content-Type", "application/json")
514+
rec := httptest.NewRecorder()
515+
c := e.NewContext(req, rec)
516+
c.SetPath("/api/" + gaia.APIVersion + "/pipeline/name")
517+
q := req.URL.Query()
518+
q.Add("name", "pipeline a")
519+
req.URL.RawQuery = q.Encode()
520+
521+
PipelineNameAvailable(c)
522+
523+
if rec.Code != http.StatusBadRequest {
524+
t.Fatalf("expected response code %v got %v", http.StatusBadRequest, rec.Code)
525+
}
526+
bodyBytes, err := ioutil.ReadAll(rec.Body)
527+
if err != nil {
528+
t.Fatalf("cannot read response body: %s", err.Error())
529+
}
530+
nameAlreadyInUseMessage := "pipeline name is already in use"
531+
if string(bodyBytes[:]) != nameAlreadyInUseMessage {
532+
t.Fatalf("error message should be '%s' but was '%s'", nameAlreadyInUseMessage, string(bodyBytes[:]))
533+
}
534+
})
535+
536+
t.Run("fails for pipeline name is too long", func(t *testing.T) {
537+
req := httptest.NewRequest(echo.GET, "/", nil)
538+
req.Header.Set("Content-Type", "application/json")
539+
rec := httptest.NewRecorder()
540+
c := e.NewContext(req, rec)
541+
c.SetPath("/api/" + gaia.APIVersion + "/pipeline/name")
542+
q := req.URL.Query()
543+
q.Add("name", "pipeline a pipeline a pipeline a pipeline a pipeline a pipeline a pipeline a pipeline a pipeline a pipeline a")
544+
req.URL.RawQuery = q.Encode()
545+
546+
PipelineNameAvailable(c)
547+
548+
if rec.Code != http.StatusBadRequest {
549+
t.Fatalf("expected response code %v got %v", http.StatusBadRequest, rec.Code)
550+
}
551+
bodyBytes, err := ioutil.ReadAll(rec.Body)
552+
if err != nil {
553+
t.Fatalf("cannot read response body: %s", err.Error())
554+
}
555+
nameTooLongMessage := "name of pipeline is empty or one of the path elements length exceeds 50 characters"
556+
if string(bodyBytes[:]) != nameTooLongMessage {
557+
t.Fatalf("error message should be '%s' but was '%s'", nameTooLongMessage, string(bodyBytes[:]))
558+
}
559+
})
560+
561+
t.Run("works for pipeline with different name", func(t *testing.T) {
562+
req := httptest.NewRequest(echo.GET, "/", nil)
563+
req.Header.Set("Content-Type", "application/json")
564+
rec := httptest.NewRecorder()
565+
c := e.NewContext(req, rec)
566+
c.SetPath("/api/" + gaia.APIVersion + "/pipeline/name")
567+
q := req.URL.Query()
568+
q.Add("name", "pipeline b")
569+
req.URL.RawQuery = q.Encode()
570+
571+
PipelineNameAvailable(c)
572+
573+
if rec.Code != http.StatusOK {
574+
t.Fatalf("expected response code %v got %v", http.StatusOK, rec.Code)
575+
}
576+
})
577+
}

workers/pipeline/create_pipeline.go

+42
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package pipeline
22

33
import (
4+
"errors"
45
"fmt"
6+
"strings"
57

68
"github.com/gaia-pipeline/gaia"
79
"github.com/gaia-pipeline/gaia/services"
@@ -19,6 +21,17 @@ const (
1921

2022
// Completed percent progress
2123
pipelineCompleteStatus = 100
24+
25+
// Split char to separate path from pipeline and name
26+
pipelinePathSplitChar = "/"
27+
)
28+
29+
var (
30+
// errPathLength is a validation error during pipeline name input
31+
errPathLength = errors.New("name of pipeline is empty or one of the path elements length exceeds 50 characters")
32+
33+
// errPipelineNameInUse is thrown when a pipelines name is already in use
34+
errPipelineNameInUse = errors.New("pipeline name is already in use")
2235
)
2336

2437
// CreatePipeline is the main function which executes step by step the creation
@@ -145,3 +158,32 @@ func CreatePipeline(p *gaia.CreatePipeline) {
145158
}
146159
}
147160
}
161+
162+
// ValidatePipelineName validates a given pipeline name and
163+
// returns the correct error back.
164+
func ValidatePipelineName(pName string) error {
165+
// The name could contain a path. Split it up.
166+
path := strings.Split(pName, pipelinePathSplitChar)
167+
168+
// Iterate all objects.
169+
for _, s := range path {
170+
// Length should be correct.
171+
if len(s) < 1 || len(s) > 50 {
172+
return errPathLength
173+
}
174+
175+
// Check if pipeline name is already in use.
176+
alreadyInUse := false
177+
for activePipeline := range GlobalActivePipelines.Iter() {
178+
if strings.ToLower(s) == strings.ToLower(activePipeline.Name) {
179+
alreadyInUse = true
180+
}
181+
}
182+
183+
// Throw error because it's already in use.
184+
if alreadyInUse {
185+
return errPipelineNameInUse
186+
}
187+
}
188+
return nil
189+
}

0 commit comments

Comments
 (0)