diff --git a/.travis.yml b/.travis.yml index aff9e1f81..979f7dd06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,5 @@ +sudo: true + language: go go: @@ -5,10 +7,32 @@ go: go_import_path: github.com/vmware/harbor -service: - - mysql +services: + - docker + - mysql -env: DB_HOST=127.0.0.1 DB_PORT=3306 DB_USR=root DB_PWD= +dist: trusty +addons: + apt: + packages: + - mysql-server-5.6 + - mysql-client-core-5.6 + - mysql-client-5.6 + +env: + DB_HOST: 127.0.0.1 + DB_PORT: 3306 + DB_USR: root + DB_PWD: + DOCKER_COMPOSE_VERSION: 1.7.1 + HARBOR_ADMIN: admin + HARBOR_ADMIN_PASSWD: Harbor12345 + +before_install: + - ./tests/hostcfg.sh + - cd Deploy + - ./prepare + - cd .. install: - sudo apt-get update && sudo apt-get install -y libldap2-dev @@ -17,12 +41,37 @@ install: - go get -d github.com/go-sql-driver/mysql - go get github.com/golang/lint/golint - go get github.com/GeertJohan/fgt - + - sudo apt-get install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" docker-engine=1.11.1-0~trusty + - sudo rm /usr/local/bin/docker-compose + - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose + - chmod +x docker-compose + - sudo mv docker-compose /usr/local/bin + - go get github.com/dghubble/sling + - go get github.com/stretchr/testify + - go get github.com/yhua123/harbor/tests/apitests/apilib + before_script: # create tables and load data - mysql < ./Deploy/db/registry.sql -uroot --verbose script: - - go list ./... | grep -v /vendor/ | xargs -L1 fgt golint - - go list ./... | grep -v 'vendor' | xargs -L1 go vet - - go list ./... | grep -v 'vendor' | xargs -L1 go test -v + - go list ./... | grep -v 'tests' | grep -v /vendor/ | xargs -L1 fgt golint + - go list ./... | grep -v 'tests' | grep -v 'vendor' | xargs -L1 go vet + - go list ./... | grep -v 'tests' | grep -v 'vendor' | xargs -L1 go test -v + + - docker-compose -f Deploy/docker-compose.yml up -d + + - docker ps + - docker version + - docker-compose version + - go run tests/startuptest.go http://localhost/ + - go run tests/userlogintest.go -name ${HARBOR_ADMIN} -passwd ${HARBOR_ADMIN_PASSWD} + + + # test for API + - #IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'` + - #echo $IP + - #docker login -u admin -p Harbor12345 $IP + - #./tests/testprepare.sh + - docker ps + - go test -v ./tests/apitests diff --git a/tests/apitests/.harborAPISearch_test.go.swp b/tests/apitests/.harborAPISearch_test.go.swp new file mode 100644 index 000000000..3a572392e Binary files /dev/null and b/tests/apitests/.harborAPISearch_test.go.swp differ diff --git a/tests/apitests/apilib/.harborapi.go.swp b/tests/apitests/apilib/.harborapi.go.swp new file mode 100644 index 000000000..75ed84400 Binary files /dev/null and b/tests/apitests/apilib/.harborapi.go.swp differ diff --git a/tests/apitests/apilib/accesslog.go b/tests/apitests/apilib/accesslog.go new file mode 100644 index 000000000..0d6318102 --- /dev/null +++ b/tests/apitests/apilib/accesslog.go @@ -0,0 +1,23 @@ +/* + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package HarborAPI + +type AccessLog struct { + Username string `json:"username,omitempty"` + Keywords string `json:"keywords,omitempty"` + BeginTimestamp int32 `json:"beginTimestamp,omitempty"` + EndTimestamp int32 `json:"endTimestamp,omitempty"` +} diff --git a/tests/apitests/apilib/harborapi.go b/tests/apitests/apilib/harborapi.go new file mode 100644 index 000000000..ee0d0d9e1 --- /dev/null +++ b/tests/apitests/apilib/harborapi.go @@ -0,0 +1,113 @@ +//Package HarborAPI +//These APIs provide services for manipulating Harbor project. +package HarborAPI + +import ( + "encoding/json" + //"fmt" + "io/ioutil" + "net/http" + + "github.com/dghubble/sling" +) + +type HarborAPI struct { + basePath string +} + +func NewHarborAPI() *HarborAPI { + return &HarborAPI{ + basePath: "http://localhost", + } +} + +func NewHarborAPIWithBasePath(basePath string) *HarborAPI { + return &HarborAPI{ + basePath: basePath, + } +} + +type UsrInfo struct { + Name string + Passwd string +} + +//Search for projects and repositories +//Implementation Notes +//The Search endpoint returns information about the projects and repositories +//offered at public status or related to the current logged in user. +//The response includes the project and repository list in a proper display order. +//@param q Search parameter for project and repository name. +//@return []Search +//func (a HarborAPI) SearchGet (q string) (Search, error) { +func (a HarborAPI) SearchGet(q string) (Search, error) { + + _sling := sling.New().Get(a.basePath) + + // create path and map variables + path := "/api/search" + + _sling = _sling.Path(path) + + type QueryParams struct { + Query string `url:"q"` + } + + _sling = _sling.QueryStruct(&QueryParams{q}) + + // accept header + accepts := []string{"application/json", "text/plain"} + for key := range accepts { + _sling = _sling.Set("Accept", accepts[key]) + break // only use the first Accept + } + + req, err := _sling.Request() + client := &http.Client{} + httpResponse, err := client.Do(req) + defer httpResponse.Body.Close() + + body, err := ioutil.ReadAll(httpResponse.Body) + if err != nil { + // handle error + } + + var successPayload = new(Search) + err = json.Unmarshal(body, &successPayload) + return *successPayload, err +} + +//Create a new project. +//Implementation Notes +//This endpoint is for user to create a new project. +//@param project New created project. +//@return void +//func (a HarborAPI) ProjectsPost (prjUsr UsrInfo, project Project) (int, error) { +func (a HarborAPI) ProjectsPost(prjUsr UsrInfo, project Project) (int, error) { + + _sling := sling.New().Post(a.basePath) + + // create path and map variables + path := "/api/projects" + + _sling = _sling.Path(path) + + // accept header + accepts := []string{"application/json", "text/plain"} + for key := range accepts { + _sling = _sling.Set("Accept", accepts[key]) + break // only use the first Accept + } + + // body params + _sling = _sling.BodyJSON(project) + + req, err := _sling.Request() + req.SetBasicAuth(prjUsr.Name, prjUsr.Passwd) + + client := &http.Client{} + httpResponse, err := client.Do(req) + defer httpResponse.Body.Close() + + return httpResponse.StatusCode, err +} diff --git a/tests/apitests/apilib/harborlogout.go b/tests/apitests/apilib/harborlogout.go new file mode 100644 index 000000000..fa59ee2bb --- /dev/null +++ b/tests/apitests/apilib/harborlogout.go @@ -0,0 +1,15 @@ +// HarborLogout.go +package HarborAPI + +import ( + "net/http" +) + +func (a HarborAPI) HarborLogout() (int, error) { + + response, err := http.Get(a.basePath + "/logout") + + defer response.Body.Close() + + return response.StatusCode, err +} diff --git a/tests/apitests/apilib/harlogin.go b/tests/apitests/apilib/harlogin.go new file mode 100644 index 000000000..d711103bb --- /dev/null +++ b/tests/apitests/apilib/harlogin.go @@ -0,0 +1,28 @@ +// HarborLogon.go +package HarborAPI + +import ( + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +func (a HarborAPI) HarborLogin(user UsrInfo) (int, error) { + + v := url.Values{} + v.Set("principal", user.Name) + v.Set("password", user.Passwd) + + body := ioutil.NopCloser(strings.NewReader(v.Encode())) //endode v:[body struce] + + client := &http.Client{} + reqest, err := http.NewRequest("POST", a.basePath+"/login", body) + + reqest.Header.Set("Content-Type", "application/x-www-form-urlencoded;param=value") //setting post head + + resp, err := client.Do(reqest) + defer resp.Body.Close() //close resp.Body + + return resp.StatusCode, err +} diff --git a/tests/apitests/apilib/project.go b/tests/apitests/apilib/project.go new file mode 100644 index 000000000..19444957b --- /dev/null +++ b/tests/apitests/apilib/project.go @@ -0,0 +1,15 @@ +package HarborAPI + +import () + +type Project struct { + ProjectId int32 `json:"id,omitempty"` + OwnerId int32 `json:"owner_id,omitempty"` + ProjectName string `json:"project_name,omitempty"` + CreationTime string `json:"creation_time,omitempty"` + Deleted int32 `json:"deleted,omitempty"` + UserId int32 `json:"user_id,omitempty"` + OwnerName string `json:"owner_name,omitempty"` + Public bool `json:"public,omitempty"` + Togglable bool `json:"togglable,omitempty"` +} diff --git a/tests/apitests/apilib/projecttemp4search.go b/tests/apitests/apilib/projecttemp4search.go new file mode 100644 index 000000000..c7219cc97 --- /dev/null +++ b/tests/apitests/apilib/projecttemp4search.go @@ -0,0 +1,9 @@ +package HarborAPI + +import () + +type Project4Search struct { + ProjectId int32 `json:"id,omitempty"` + ProjectName string `json:"name,omitempty"` + Public int32 `json:"public,omitempty"` +} diff --git a/tests/apitests/apilib/repository.go b/tests/apitests/apilib/repository.go new file mode 100644 index 000000000..5de9302df --- /dev/null +++ b/tests/apitests/apilib/repository.go @@ -0,0 +1,16 @@ +package HarborAPI + +import ( + "time" +) + +type Repository struct { + Id string `json:"id,omitempty"` + Parent string `json:"parent,omitempty"` + Created time.Time `json:"created,omitempty"` + DurationDays string `json:"duration_days,omitempty"` + Author string `json:"author,omitempty"` + Architecture string `json:"architecture,omitempty"` + DockerVersion string `json:"docker_version,omitempty"` + Os string `json:"os,omitempty"` +} diff --git a/tests/apitests/apilib/repositorytemp4search.go b/tests/apitests/apilib/repositorytemp4search.go new file mode 100644 index 000000000..f0dead98b --- /dev/null +++ b/tests/apitests/apilib/repositorytemp4search.go @@ -0,0 +1,9 @@ +package HarborAPI + +type Repository4Search struct { + ProjectId int32 `json:"project_id,omitempty"` + ProjectName string `json:"project_name,omitempty"` + ProjectPublic int32 `json:"project_public,omitempty"` + RepoName string `json:"repository_name,omitempty"` +} + diff --git a/tests/apitests/apilib/role.go b/tests/apitests/apilib/role.go new file mode 100644 index 000000000..c6e53b044 --- /dev/null +++ b/tests/apitests/apilib/role.go @@ -0,0 +1,7 @@ +package HarborAPI + +type Role struct { + RoleId int32 `json:"role_id,omitempty"` + RoleCode string `json:"role_code,omitempty"` + RoleName string `json:"role_name,omitempty"` +} diff --git a/tests/apitests/apilib/roleparam.go b/tests/apitests/apilib/roleparam.go new file mode 100644 index 000000000..12cf755a5 --- /dev/null +++ b/tests/apitests/apilib/roleparam.go @@ -0,0 +1,6 @@ +package HarborAPI + +type RoleParam struct { + Roles []int32 `json:"roles,omitempty"` + UserName string `json:"user_name,omitempty"` +} diff --git a/tests/apitests/apilib/search.go b/tests/apitests/apilib/search.go new file mode 100644 index 000000000..81ca0fb20 --- /dev/null +++ b/tests/apitests/apilib/search.go @@ -0,0 +1,8 @@ +package HarborAPI + +import () + +type Search struct { + Projects []Project4Search `json:"project,omitempty"` + Repositories []Repository4Search `json:"repository,omitempty"` +} diff --git a/tests/apitests/apilib/user.go b/tests/apitests/apilib/user.go new file mode 100644 index 000000000..10a07933d --- /dev/null +++ b/tests/apitests/apilib/user.go @@ -0,0 +1,11 @@ +package HarborAPI + +type User struct { + UserId int32 `json:"user_id,omitempty"` + Username string `json:"username,omitempty"` + Email string `json:"email,omitempty"` + Password string `json:"password,omitempty"` + Realname string `json:"realname,omitempty"` + Comment string `json:"comment,omitempty"` + Deleted int32 `json:"deleted,omitempty"` +} diff --git a/tests/apitests/hbapiaddprj_test.go b/tests/apitests/hbapiaddprj_test.go new file mode 100644 index 000000000..3a4673311 --- /dev/null +++ b/tests/apitests/hbapiaddprj_test.go @@ -0,0 +1,95 @@ +package HarborAPItest + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "testing" + "github.com/yhua123/harbor/tests/apitests/apilib" +) + +func TestAddProject(t *testing.T) { + + fmt.Println("Test for Project Add (ProjectsPost) API\n") + assert := assert.New(t) + + apiTest := HarborAPI.NewHarborAPI() + + //prepare for test + adminEr := &HarborAPI.UsrInfo{"admin", "Harbor1234"} + admin := &HarborAPI.UsrInfo{"admin", "Harbor12345"} + + prjUsr := &HarborAPI.UsrInfo{"unknown", "unknown"} + + var project HarborAPI.Project + project.ProjectName = "testProject" + project.Public = true + + //case 1: admin login fail, expect project creation fail. + fmt.Println("case 1: admin login fail, expect project creation fail.") + resault, err := apiTest.HarborLogin(*adminEr) + if err != nil { + t.Error("Error while admin login", err.Error()) + t.Log(err) + } else { + assert.Equal(resault, int(401), "Admin login status should be 401") + //t.Log(resault) + } + + resault, err = apiTest.ProjectsPost(*prjUsr, project) + if err != nil { + t.Error("Error while creat project", err.Error()) + t.Log(err) + } else { + assert.Equal(resault, int(401), "Case 1: Project creation status should be 401") + //t.Log(resault) + } + + //case 2: admin successful login, expect project creation success. + fmt.Println("case 2: admin successful login, expect project creation success.") + resault, err = apiTest.HarborLogin(*admin) + if err != nil { + t.Error("Error while admin login", err.Error()) + t.Log(err) + } else { + assert.Equal(resault, int(200), "Admin login status should be 200") + //t.Log(resault) + } + if resault != 200 { + t.Log(resault) + } else { + prjUsr = admin + } + + resault, err = apiTest.ProjectsPost(*prjUsr, project) + if err != nil { + t.Error("Error while creat project", err.Error()) + t.Log(err) + } else { + assert.Equal(resault, int(201), "Case 2: Project creation status should be 201") + //t.Log(resault) + } + + //case 3: duplicate project name, create project fail + fmt.Println("case 3: duplicate project name, create project fail") + resault, err = apiTest.ProjectsPost(*prjUsr, project) + if err != nil { + t.Error("Error while creat project", err.Error()) + t.Log(err) + } else { + assert.Equal(resault, int(409), "Case 3: Project creation status should be 409") + //t.Log(resault) + } + + //resault1, err := apiTest.HarborLogout() + //if err != nil { + // t.Error("Error while admin logout", err.Error()) + // t.Log(err) + //} else { + // assert.Equal(resault1, int(200), "Admin logout status") + // //t.Log(resault) + //} + //if resault1 != 200 { + // t.Log(resault) + //} + +} diff --git a/tests/apitests/hbapisearch_test.go b/tests/apitests/hbapisearch_test.go new file mode 100644 index 000000000..58c6fd97e --- /dev/null +++ b/tests/apitests/hbapisearch_test.go @@ -0,0 +1,31 @@ +package HarborAPItest + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "testing" + "github.com/yhua123/harbor/tests/apitests/apilib" +) + +func TestSearch(t *testing.T) { + fmt.Println("Test for Search (SearchGet) API\n") + assert := assert.New(t) + + apiTest := HarborAPI.NewHarborAPI() + var resault HarborAPI.Search + resault, err := apiTest.SearchGet("library") + //fmt.Printf("%+v\n", resault) + if err != nil { + t.Error("Error while search project or repository", err.Error()) + t.Log(err) + } else { + assert.Equal(resault.Projects[0].ProjectId, int32(1), "Project id should be equal") + assert.Equal(resault.Projects[0].ProjectName, "library", "Project name should be library") + assert.Equal(resault.Projects[0].Public, int32(1), "Project public status should be 1 (true)") + //t.Log(resault) + } + //if resault.Response.StatusCode != 200 { + // t.Log(resault.Response) + //} + +} diff --git a/tests/hostcfg.sh b/tests/hostcfg.sh new file mode 100755 index 000000000..d25c9dfec --- /dev/null +++ b/tests/hostcfg.sh @@ -0,0 +1,4 @@ +#!/bin/bash +IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'` +#echo $IP +sed "s/reg.mydomain.com/$IP/" -i Deploy/harbor.cfg diff --git a/tests/startuptest.go b/tests/startuptest.go new file mode 100644 index 000000000..f611ef58c --- /dev/null +++ b/tests/startuptest.go @@ -0,0 +1,36 @@ +// Fetch prints the content found at a URL. +package main + +import ( + "fmt" + "io/ioutil" + "net/http" + "os" + "strings" + "time" +) + +func main() { + time.Sleep(60*time.Second) + for _, url := range os.Args[1:] { + resp, err := http.Get(url) + if err != nil { + fmt.Fprintf(os.Stderr, "fetch: %v\n", err) + os.Exit(1) + } + b, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err) + os.Exit(1) + } +// fmt.Printf("%s", b) + if strings.Contains(string(b), "Harbor") { + fmt.Printf("sucess!\n") + } else { + os.Exit(1) + } + + } +} + diff --git a/tests/testprepare.sh b/tests/testprepare.sh new file mode 100755 index 000000000..0d9a30ce2 --- /dev/null +++ b/tests/testprepare.sh @@ -0,0 +1,11 @@ +IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'` +#echo $IP +docker pull hello-world +docker pull docker +#docker login -u admin -p Harbor12345 $IP + +docker tag hello-world $IP/library/hello-world +docker push $IP/library/hello-world + +docker tag docker $IP/library/docker +docker push $IP/library/docker diff --git a/tests/userlogintest.go b/tests/userlogintest.go new file mode 100644 index 000000000..3e8303119 --- /dev/null +++ b/tests/userlogintest.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" + "flag" +) + +func main() { + usrNamePtr := flag.String("name","anaymous","user name") + usrPasswdPtr := flag.String("passwd","anaymous","user password") + flag.Parse() + + v := url.Values{} + v.Set("principal", *usrNamePtr) + v.Set("password", *usrPasswdPtr) + + body := ioutil.NopCloser(strings.NewReader(v.Encode())) //endode v:[body struce] + fmt.Println(v) + + client := &http.Client{} + reqest, err := http.NewRequest("POST", "http://localhost/login", body) + if err != nil { + fmt.Println("Fatal error ", err.Error()) + } + + reqest.Header.Set("Content-Type", "application/x-www-form-urlencoded;param=value") //setting post head + + resp, err := client.Do(reqest) + defer resp.Body.Close() //close resp.Body + + fmt.Println("login status: ", resp.StatusCode) //print status code + + //content_post, err := ioutil.ReadAll(resp.Body) + //if err != nil { + // fmt.Println("Fatal error ", err.Error()) + //} + + //fmt.Println(string(content_post)) //print reply + + response, err := http.Get("http://localhost/api/logout") + if err != nil { + fmt.Println("Fatal error ", err.Error()) + } + + defer response.Body.Close() + + fmt.Println("logout status: ", resp.StatusCode) //print status code + //content_get, err := ioutil.ReadAll(response.Body) + //fmt.Println(string(content_get)) + +}