Merge branch 'master' into remove-lib

This commit is contained in:
Will Sun 2019-11-27 17:44:30 +08:00 committed by GitHub
commit a52b99e180
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 293 additions and 97 deletions

View File

@ -1,23 +1,23 @@
sudo: true
language: go
go:
- 1.12.12
- 1.13.4
go_import_path: github.com/goharbor/harbor
services:
- docker
dist: trusty
matrix:
include:
- go: 1.12.12
- go: 1.13.4
env:
- UTTEST=true
- go: 1.12.12
- go: 1.13.4
env:
- APITEST_DB=true
- go: 1.12.12
- go: 1.13.4
env:
- APITEST_LDAP=true
- go: 1.12.12
- go: 1.13.4
env:
- OFFLINE=true
- language: node_js

View File

@ -128,6 +128,8 @@ Harbor backend is written in [Go](http://golang.org/). If you don't have a Harbo
| 1.7 | 1.9.2 |
| 1.8 | 1.11.2 |
| 1.9 | 1.12.12 |
| 1.10 | 1.12.12 |
| 1.11 | 1.13.4 |
Ensure your GOPATH and PATH have been configured in accordance with the Go environment instructions.

View File

@ -9,7 +9,7 @@
# compile_golangimage:
# compile from golang image
# for example: make compile_golangimage -e GOBUILDIMAGE= \
# golang:1.12.12
# golang:1.13.4
# compile_core, compile_jobservice: compile specific binary
#
# build: build Harbor docker images from photon baseimage
@ -138,7 +138,7 @@ GOINSTALL=$(GOCMD) install
GOTEST=$(GOCMD) test
GODEP=$(GOTEST) -i
GOFMT=gofmt -w
GOBUILDIMAGE=golang:1.12.12
GOBUILDIMAGE=golang:1.13.4
GOBUILDPATH=/harbor
# go build
@ -323,7 +323,7 @@ build:
-e CLAIRVERSION=$(CLAIRVERSION) -e CLAIRADAPTERVERSION=$(CLAIRADAPTERVERSION) -e VERSIONTAG=$(VERSIONTAG) \
-e BUILDBIN=$(BUILDBIN) -e REDISVERSION=$(REDISVERSION) -e MIGRATORVERSION=$(MIGRATORVERSION) \
-e CHARTMUSEUMVERSION=$(CHARTMUSEUMVERSION) -e DOCKERIMAGENAME_CHART_SERVER=$(DOCKERIMAGENAME_CHART_SERVER) \
-e NPM_REGISTRY=$(NPM_REGISTRY) -e BASEIMAGETAG=${BASEIMAGETAG}
-e NPM_REGISTRY=$(NPM_REGISTRY) -e BASEIMAGETAG=$(BASEIMAGETAG)
build_base_docker:
@for name in chartserver clair clair-adapter core db jobservice log nginx notary-server notary-signer portal prepare redis registry registryctl; do \

View File

@ -12,10 +12,6 @@
|![notification](docs/img/bell-outline-badged.svg)Community Meeting|
|------------------|
|The Harbor Project holds bi-weekly community calls in two different timezones. To join the community calls or to watch previous meeting notes and recordings, please visit the [meeting schedule](https://github.com/goharbor/community/blob/master/MEETING_SCHEDULE.md).|
We welcome you to join the below Harbor community events and meet with project maintainers and users:
**November 18-21, 2019**, [KubeCon US, San Diego](https://events19.linuxfoundation.org/events/kubecon-cloudnativecon-north-america-2019): Harbor Lunch & Learn led by Joe Beda, Intro and Deep-dive sessions.
</br> </br>

View File

@ -43,25 +43,25 @@ You can compile the code by one of the three approaches:
- Get official Golang image from docker hub:
```sh
$ docker pull golang:1.12.12
$ docker pull golang:1.13.4
```
- Build, install and bring up Harbor without Notary:
```sh
$ make install GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage
$ make install GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage
```
- Build, install and bring up Harbor with Notary:
```sh
$ make install GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage NOTARYFLAG=true
$ make install GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage NOTARYFLAG=true
```
- Build, install and bring up Harbor with Clair:
```sh
$ make install GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage CLAIRFLAG=true
$ make install GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage CLAIRFLAG=true
```
#### II. Compile code with your own Golang environment, then build Harbor

View File

@ -36,10 +36,10 @@ version | set harbor version
#### EXAMPLE:
#### Build and run harbor from source code.
make install GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage NOTARYFLAG=true
make install GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage NOTARYFLAG=true
### Package offline installer
make package_offline GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage NOTARYFLAG=true
make package_offline GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage NOTARYFLAG=true
### Start harbor with notary
make -e NOTARYFLAG=true start

View File

@ -4,7 +4,7 @@ set +e
usage(){
echo "Usage: builder <golang image:version> <code path> <code release tag> <main.go path> <binary name>"
echo "e.g: builder golang:1.11.2 github.com/helm/chartmuseum v0.9.0 cmd/chartmuseum chartm"
echo "e.g: builder golang:1.13.4 https://github.com/helm/chartmuseum v0.9.0 cmd/chartmuseum chartm"
exit 1
}

View File

@ -1,4 +1,4 @@
FROM golang:1.12.12
FROM golang:1.13.4
ADD . /go/src/github.com/goharbor/harbor-scanner-clair/
WORKDIR /go/src/github.com/goharbor/harbor-scanner-clair/

View File

@ -23,7 +23,7 @@ TEMP=`mktemp -d ${TMPDIR-/tmp}/clair-adapter.XXXXXX`
git clone https://github.com/goharbor/harbor-scanner-clair.git $TEMP
cd $TEMP; git checkout $VERSION; cd -
echo 'build the clair adapter binary bases on the golang:1.12.12'
echo 'build the clair adapter binary bases on the golang:1.13.4'
cp Dockerfile.binary $TEMP
docker build -f $TEMP/Dockerfile.binary -t clair-adapter-golang $TEMP

View File

@ -1,4 +1,4 @@
FROM golang:1.12.12
FROM golang:1.13.4
ADD . /go/src/github.com/coreos/clair/
WORKDIR /go/src/github.com/coreos/clair/

View File

@ -23,7 +23,7 @@ TEMP=`mktemp -d /$TMPDIR/clair.XXXXXX`
git clone https://github.com/coreos/clair.git $TEMP
cd $TEMP; git checkout $VERSION; cd -
echo 'build the clair binary bases on the golang:1.12.12'
echo 'build the clair binary bases on the golang:1.13.4'
cp Dockerfile.binary $TEMP
docker build -f $TEMP/Dockerfile.binary -t clair-golang $TEMP

View File

@ -1,4 +1,4 @@
FROM golang:1.12.12
FROM golang:1.13.4
ARG NOTARY_VERSION
ARG MIGRATE_VERSION

View File

@ -1,4 +1,4 @@
FROM golang:1.12.12
FROM golang:1.13.4
ENV DISTRIBUTION_DIR /go/src/github.com/docker/distribution
ENV BUILDTAGS include_oss include_gcs

View File

@ -50,7 +50,7 @@ type ProjectAPI struct {
}
const projectNameMaxLen int = 255
const projectNameMinLen int = 2
const projectNameMinLen int = 1
const restrictedNameChars = `[a-z0-9]+(?:[._-][a-z0-9]+)*`
// Prepare validates the URL and the user

View File

@ -119,7 +119,7 @@ func TestAddProject(t *testing.T) {
// case 4: response code = 400 : Project name is illegal in length
fmt.Println("case 4 : response code = 400 : Project name is illegal in length ")
result, err = apiTest.ProjectsPost(*admin, apilib.ProjectReq{ProjectName: "t", Metadata: map[string]string{models.ProMetaPublic: "true"}})
result, err = apiTest.ProjectsPost(*admin, apilib.ProjectReq{ProjectName: "", Metadata: map[string]string{models.ProMetaPublic: "true"}})
if err != nil {
t.Error("Error while creat project", err.Error())
t.Log(err)

View File

@ -34,7 +34,6 @@ import (
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/pkg/authproxy"
k8s_api_v1beta1 "k8s.io/api/authentication/v1beta1"
)
const refreshDuration = 2 * time.Second
@ -101,31 +100,12 @@ func (a *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
s := session{}
err = json.Unmarshal(data, &s)
if err != nil {
log.Errorf("failed to read session %v", err)
return nil, auth.NewErrAuth(fmt.Sprintf("failed to read session %v", err))
}
reviewResponse, err := a.tokenReview(s.SessionID)
if err != nil {
return nil, err
}
if reviewResponse == nil {
return nil, auth.ErrAuth{}
}
// Attach user group ID information
ugList := reviewResponse.Status.User.Groups
log.Debugf("user groups %+v", ugList)
if len(ugList) > 0 {
userGroups := models.UserGroupsFromName(ugList, common.HTTPGroupType)
groupIDList, err := group.PopulateGroup(userGroups)
if err != nil {
return nil, err
}
log.Debugf("current user's group ID list is %+v", groupIDList)
user.GroupIDs = groupIDList
if err := a.tokenReview(s.SessionID, user); err != nil {
return nil, auth.NewErrAuth(err.Error())
}
return user, nil
} else if resp.StatusCode == http.StatusUnauthorized {
return nil, auth.ErrAuth{}
} else {
@ -134,17 +114,24 @@ func (a *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
log.Warningf("Failed to read response body, error: %v", err)
}
return nil, fmt.Errorf("failed to authenticate, status code: %d, text: %s", resp.StatusCode, string(data))
}
}
func (a *Auth) tokenReview(sessionID string) (*k8s_api_v1beta1.TokenReview, error) {
func (a *Auth) tokenReview(sessionID string, user *models.User) error {
httpAuthProxySetting, err := config.HTTPAuthProxySetting()
if err != nil {
return nil, err
return err
}
return authproxy.TokenReview(sessionID, httpAuthProxySetting)
reviewStatus, err := authproxy.TokenReview(sessionID, httpAuthProxySetting)
if err != nil {
return err
}
u2, err := authproxy.UserFromReviewStatus(reviewStatus)
if err != nil {
return err
}
user.GroupIDs = u2.GroupIDs
return nil
}
// OnBoardUser delegates to dao pkg to insert/update data in DB.

View File

@ -325,16 +325,16 @@ func (ap *authProxyReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
log.Errorf("fail to get auth proxy settings, %v", err)
return false
}
tokenReviewResponse, err := authproxy.TokenReview(proxyPwd, httpAuthProxyConf)
tokenReviewStatus, err := authproxy.TokenReview(proxyPwd, httpAuthProxyConf)
if err != nil {
log.Errorf("fail to review token, %v", err)
return false
}
if !tokenReviewResponse.Status.Authenticated {
log.Errorf("fail to auth user: %s", rawUserName)
if rawUserName != tokenReviewStatus.User.Username {
log.Errorf("user name doesn't match with token: %s", rawUserName)
return false
}
user, err := dao.GetUser(models.User{
Username: rawUserName,
})
@ -346,11 +346,12 @@ func (ap *authProxyReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
log.Errorf("User: %s has not been on boarded yet.", rawUserName)
return false
}
if rawUserName != tokenReviewResponse.Status.User.Username {
log.Errorf("user name doesn't match with token: %s", rawUserName)
u2, err := authproxy.UserFromReviewStatus(tokenReviewStatus)
if err != nil {
log.Errorf("Failed to get user information from token review status, error: %v", err)
return false
}
user.GroupIDs = u2.GroupIDs
pm := config.GlobalProjectMgr
log.Debug("creating local database security context for auth proxy...")
securCtx := local.NewSecurityContext(user, pm)

View File

@ -2,6 +2,9 @@ package authproxy
import (
"encoding/json"
"fmt"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils/log"
k8s_api_v1beta1 "k8s.io/api/authentication/v1beta1"
@ -13,8 +16,9 @@ import (
)
// TokenReview ...
func TokenReview(sessionID string, authProxyConfig *models.HTTPAuthProxy) (*k8s_api_v1beta1.TokenReview, error) {
func TokenReview(rawToken string, authProxyConfig *models.HTTPAuthProxy) (k8s_api_v1beta1.TokenReviewStatus, error) {
emptyStatus := k8s_api_v1beta1.TokenReviewStatus{}
// Init auth client with the auth proxy endpoint.
authClientCfg := &rest.Config{
Host: authProxyConfig.TokenReviewEndpoint,
@ -22,14 +26,14 @@ func TokenReview(sessionID string, authProxyConfig *models.HTTPAuthProxy) (*k8s_
GroupVersion: &schema.GroupVersion{},
NegotiatedSerializer: serializer.DirectCodecFactory{CodecFactory: scheme.Codecs},
},
BearerToken: sessionID,
BearerToken: rawToken,
TLSClientConfig: rest.TLSClientConfig{
Insecure: !authProxyConfig.VerifyCert,
},
}
authClient, err := rest.RESTClientFor(authClientCfg)
if err != nil {
return nil, err
return emptyStatus, err
}
// Do auth with the token.
@ -39,27 +43,49 @@ func TokenReview(sessionID string, authProxyConfig *models.HTTPAuthProxy) (*k8s_
APIVersion: "authentication.k8s.io/v1beta1",
},
Spec: k8s_api_v1beta1.TokenReviewSpec{
Token: sessionID,
Token: rawToken,
},
}
res := authClient.Post().Body(tokenReviewRequest).Do()
err = res.Error()
if err != nil {
log.Errorf("fail to POST auth request, %v", err)
return nil, err
return emptyStatus, err
}
resRaw, err := res.Raw()
if err != nil {
log.Errorf("fail to get raw data of token review, %v", err)
return nil, err
return emptyStatus, err
}
// Parse the auth response, check the user name and authenticated status.
tokenReviewResponse := &k8s_api_v1beta1.TokenReview{}
err = json.Unmarshal(resRaw, &tokenReviewResponse)
err = json.Unmarshal(resRaw, tokenReviewResponse)
if err != nil {
log.Errorf("fail to decode token review, %v", err)
return nil, err
return emptyStatus, err
}
return tokenReviewResponse, nil
return tokenReviewResponse.Status, nil
}
// UserFromReviewStatus transform a review status to a user model.
// Group entries will be populated if needed.
func UserFromReviewStatus(status k8s_api_v1beta1.TokenReviewStatus) (*models.User, error) {
if !status.Authenticated {
return nil, fmt.Errorf("failed to authenticate the token, error in status: %s", status.Error)
}
user := &models.User{
Username: status.User.Username,
}
if len(status.User.Groups) > 0 {
userGroups := models.UserGroupsFromName(status.User.Groups, common.HTTPGroupType)
groupIDList, err := group.PopulateGroup(userGroups)
if err != nil {
return nil, err
}
log.Debugf("current user's group ID list is %+v", groupIDList)
user.GroupIDs = groupIDList
}
return user, nil
}

View File

@ -0,0 +1,87 @@
package authproxy
import (
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/stretchr/testify/assert"
"k8s.io/api/authentication/v1beta1"
"os"
"testing"
)
func TestMain(m *testing.M) {
dao.PrepareTestForPostgresSQL()
result := m.Run()
if result != 0 {
os.Exit(result)
}
}
func TestUserFromReviewStatus(t *testing.T) {
type result struct {
hasErr bool
username string
groupLen int
}
cases := []struct {
input v1beta1.TokenReviewStatus
expect result
}{
{
input: v1beta1.TokenReviewStatus{
Authenticated: false,
Error: "connection error",
},
expect: result{
hasErr: true,
},
},
{
input: v1beta1.TokenReviewStatus{
Authenticated: true,
User: v1beta1.UserInfo{
Username: "jack",
UID: "u-1",
},
},
expect: result{
hasErr: false,
username: "jack",
groupLen: 0,
},
},
{
input: v1beta1.TokenReviewStatus{
Authenticated: true,
User: v1beta1.UserInfo{
Username: "daniel",
Groups: []string{"group1", "group2"},
},
Error: "",
},
expect: result{
hasErr: false,
username: "daniel",
groupLen: 2,
},
},
}
for _, c := range cases {
u, err := UserFromReviewStatus(c.input)
if c.expect.hasErr == true {
assert.NotNil(t, err)
} else {
assert.Nil(t, err)
assert.Equal(t, c.expect.username, u.Username)
assert.Equal(t, c.expect.groupLen, len(u.GroupIDs))
}
if u != nil {
for _, gid := range u.GroupIDs {
t.Logf("Deleting group %d", gid)
if err := group.DeleteUserGroup(gid); err != nil {
panic(err)
}
}
}
}
}

View File

@ -14,6 +14,7 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER, LOCALE_ID, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { AppComponent } from './app.component';
import { InterceptHttpService } from './intercept-http.service';
import { BaseModule } from './base/base.module';
import { HarborRoutingModule } from './harbor-routing.module';
@ -43,6 +44,8 @@ import { InterrogationServicesComponent } from "./interrogation-services/interro
import { LabelsComponent } from './labels/labels.component';
import { ProjectQuotasComponent } from './project-quotas/project-quotas.component';
import { HarborLibraryModule } from "../lib/harbor-library.module";
import { HTTP_INTERCEPTORS } from '@angular/common/http';
registerLocaleData(zh, 'zh-cn');
registerLocaleData(es, 'es-es');
@ -95,7 +98,9 @@ export function getCurrentLanguage(translateService: TranslateService) {
deps: [ AppConfigService, SkinableConfig],
multi: true
},
{provide: LOCALE_ID, useValue: "en-US"}
{provide: LOCALE_ID, useValue: "en-US"},
{ provide: HTTP_INTERCEPTORS, useClass: InterceptHttpService, multi: true }
],
schemas: [
CUSTOM_ELEMENTS_SCHEMA

View File

@ -0,0 +1,58 @@
import { TestBed, inject } from '@angular/core/testing';
import { InterceptHttpService } from './intercept-http.service';
import { CookieService } from 'ngx-cookie';
import { HttpRequest, HttpResponse } from '@angular/common/http';
import { of, throwError } from 'rxjs';
describe('InterceptHttpService', () => {
let cookie = "fdsa|ds";
const mockCookieService = {
get: function () {
return cookie;
},
set: function (cookieStr: string) {
cookie = cookieStr;
}
};
const mockRequest = new HttpRequest('PUT', "", {
headers: new Map()
});
const mockHandle = {
handle: (request) => {
if (request.headers.has('X-Xsrftoken')) {
return of(new HttpResponse({status: 200}));
} else {
return throwError(new HttpResponse( {
status: 403
}));
}
}
};
beforeEach(() => TestBed.configureTestingModule({}));
beforeEach(() => {
TestBed.configureTestingModule({
imports: [],
providers: [
InterceptHttpService,
{ provide: CookieService, useValue: mockCookieService }
]
});
});
it('should be initialized', inject([InterceptHttpService], (service: InterceptHttpService) => {
expect(service).toBeTruthy();
}));
it('should be get right token and send right request when the cookie not exists', inject([InterceptHttpService],
(service: InterceptHttpService) => {
mockCookieService.set("fdsa|ds");
service.intercept(mockRequest, mockHandle).subscribe(res => {
if (res.status === 403) {
expect(btoa(mockRequest.headers.get("X-Xsrftoken"))).toEqual(cookie.split("|")[0]);
} else {
expect(res.status).toEqual(200);
}
});
}));
});

View File

@ -0,0 +1,28 @@
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
import { CookieService } from 'ngx-cookie';
@Injectable({
providedIn: 'root'
})
export class InterceptHttpService implements HttpInterceptor {
constructor(private cookie: CookieService) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
return next.handle(request).pipe(catchError(error => {
if (error.status === 403) {
let Xsrftoken = this.cookie.get("_xsrf") ? atob(this.cookie.get("_xsrf").split("|")[0]) : null;
if (Xsrftoken && !request.headers.has('X-Xsrftoken')) {
request = request.clone({ headers: request.headers.set('X-Xsrftoken', Xsrftoken) });
return next.handle(request);
}
}
return throwError(error);
}));
}
}

View File

@ -8,7 +8,7 @@
<div class="clr-control-container" [class.clr-error]="!isNameValid">
<div class="clr-input-wrapper">
<input type="text" id="create_project_name" [(ngModel)]="project.name" name="create_project_name" class="clr-input input-width"
required pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$" minlength="2" #projectName="ngModel" autocomplete="off"
required pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$" #projectName="ngModel" autocomplete="off"
(keyup)='handleValidation()'>
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
<span class="spinner spinner-inline" [hidden]="!checkOnGoing"></span>

View File

@ -214,9 +214,8 @@
"PUBLIC_PROJECTS": "Public Projects",
"PROJECT": "Project",
"NEW_PROJECT": "New Project",
"NAME_TOOLTIP": "Project name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"NAME_TOOLTIP": "Project name should be 1~255 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"NAME_IS_REQUIRED": "Project name is required.",
"NAME_MINIMUM_LENGTH": "Project name is too short, it should be greater than 2 characters.",
"NAME_ALREADY_EXISTS": "Project name already exists.",
"NAME_IS_ILLEGAL": "Project name is invalid.",
"UNKNOWN_ERROR": "An unknown error occurred while creating the project.",

View File

@ -214,10 +214,9 @@
"PUBLIC_PROJECTS": "Public Projects",
"PROJECT": "Proyecto",
"NEW_PROJECT": "Nuevo proyecto",
"NAME_TOOLTIP": "Project name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"NAME_TOOLTIP": "Project name should be 1~255 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"DESTINATION_NAME_TOOLTIP": "Destination name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"NAME_IS_REQUIRED": "El nombre del proyecto es obligatorio.",
"NAME_MINIMUM_LENGTH": "El nombre del proyecto es demasiado corto, debe ser mayor de 2 caracteres.",
"NAME_ALREADY_EXISTS": "Ya existe un proyecto con ese nombre.",
"NAME_IS_ILLEGAL": "El nombre del proyecto no es valido.",
"UNKNOWN_ERROR": "Ha ocurrido un error al crear el proyecto.",

View File

@ -208,9 +208,8 @@
"PUBLIC_PROJECTS": "Projets Publics",
"PROJECT": "Projet",
"NEW_PROJECT": "Nouveau Projet",
"NAME_TOOLTIP": "Le nom du projet doit comporter au moins 2 caractères avec des minuscules, des chiffres et. _- et doit commencer par des caractères ou des chiffres.",
"NAME_TOOLTIP": "Le nom du projet doit comporter 1~255 caractères avec des minuscules, des chiffres et. _- et doit commencer par des caractères ou des chiffres.",
"NAME_IS_REQUIRED": "Le nom du projet est obligatoire.",
"NAME_MINIMUM_LENGTH": "Le nom du projet est trop court, il doit être supérieur à 2 caractères.",
"NAME_ALREADY_EXISTS": "Le nom du projet existe déjà.",
"NAME_IS_ILLEGAL": "Le nom du projet est invalide.",
"UNKNOWN_ERROR": "Une erreur inconnue s'est produite lors de la création du projet.",

View File

@ -212,9 +212,8 @@
"PUBLIC_PROJECTS": "Projetos Públicos",
"PROJECT": "Projeto",
"NEW_PROJECT": "Novo Projeto",
"NAME_TOOLTIP": "Nome do projeto deve conter ao menos 2 caracteres sendo minusculos, números e ._- e deve iniciar com letras ou números.",
"NAME_TOOLTIP": "Nome do projeto deve conter 1~255 caracteres sendo minusculos, números e ._- e deve iniciar com letras ou números.",
"NAME_IS_REQUIRED": "Nome do projeto é obrigatório.",
"NAME_MINIMUM_LENGTH": "Nome do projeto é muito curto, deve conter ao menos 2 caracteres.",
"NAME_ALREADY_EXISTS": "Nome do projeto já existe.",
"NAME_IS_ILLEGAL": "Nome do projeto é inválido.",
"UNKNOWN_ERROR": "Um erro desconhecido ocorreu ao criar o projeto.",

View File

@ -214,9 +214,8 @@
"PUBLIC_PROJECTS": "Genel Projeler",
"PROJECT": "Proje",
"NEW_PROJECT": "Yeni Proje",
"NAME_TOOLTIP": "Proje adı, en az 2 karakter uzunluğunda, küçük harf, rakam ve ._- ile yazılmalı ve karakter veya rakamlarla başlamalıdır.",
"NAME_TOOLTIP": "Proje adı, en az 1~255 karakter uzunluğunda, küçük harf, rakam ve ._- ile yazılmalı ve karakter veya rakamlarla başlamalıdır.",
"NAME_IS_REQUIRED": "Proje adı gerekli.",
"NAME_MINIMUM_LENGTH": "Proje adı çok kısa, 2 karakterden büyük olmalıdır.",
"NAME_ALREADY_EXISTS": "Proje adı zaten var.",
"NAME_IS_ILLEGAL": "Proje adı geçersiz.",
"UNKNOWN_ERROR": "Proje oluşturulurken bilinmeyen bir hata oluştu.",

View File

@ -213,9 +213,8 @@
"PUBLIC_PROJECTS": "公开项目",
"PROJECT": "项目",
"NEW_PROJECT": "新建项目",
"NAME_TOOLTIP": "项目名称由小写字符、数字和._-组成且至少2个字符并以字符或者数字开头。",
"NAME_TOOLTIP": "项目名称由小写字符、数字和._-组成且至少1个字符并以字符或者数字开头。",
"NAME_IS_REQUIRED": "项目名称为必填项。",
"NAME_MINIMUM_LENGTH": "项目名称长度过短至少多于2个字符。",
"NAME_ALREADY_EXISTS": "项目名称已存在。",
"NAME_IS_ILLEGAL": "项目名称非法。",
"UNKNOWN_ERROR": "创建项目时发生未知错误。",

View File

@ -93,6 +93,14 @@ export Harbor_Assets_Version=$Harbor_Assets_Version
export Harbor_Package_Version=$Harbor_Package_Version
export NPM_REGISTRY=$NPM_REGISTRY
# release branch must have their own base image with branch name, master and others will use the dev as base.
if [[ $DRONE_BRANCH == "release-"* ]]; then
Harbor_Build_Base_Tag=$DRONE_BRANCH
else
Harbor_Build_Base_Tag=dev
fi
export Harbor_Build_Base_Tag=$Harbor_Build_Base_Tag
echo "--------------------------------------------------"
echo "Harbor Package version: $Harbor_Package_Version"
echo "Harbor Assets version: $Harbor_Assets_Version"

View File

@ -26,9 +26,8 @@ Go Into Project
\ Retry Wait Element ${search_input}
\ Retry Clear Element Text ${search_input}
\ Input Text ${search_input} ${project}
\ Retry Wait Until Page Contains ${project}
\ ${out} Run Keyword If ${has_image}==${false} Retry Double Keywords When Error Retry Element Click xpath=//*[@id='project-results']//clr-dg-cell[contains(.,'${project}')]/a Wait Until Element Is Visible And Enabled xpath=//clr-dg-placeholder[contains(.,\"We couldn\'t find any repositories!\")]
\ ... ELSE Retry Double Keywords When Error Retry Element Click xpath=//*[@id='project-results']//clr-dg-cell[contains(.,'${project}')]/a Wait Until Element Is Visible And Enabled xpath=//project-detail//hbr-repository-gridview//clr-dg-cell[contains(.,'${project}/')]
\ ${out} Run Keyword If ${has_image}==${false} Retry Double Keywords When Error Retry Element Click xpath=//*[@id='project-results']//clr-dg-cell[contains(.,'${project}')]/a Wait Until Element Is Visible And Enabled xpath=//clr-dg-placeholder[contains(.,\"We couldn\'t find any repositories!\")] DoAssert=${false}
\ ... ELSE Retry Double Keywords When Error Retry Element Click xpath=//*[@id='project-results']//clr-dg-cell[contains(.,'${project}')]/a Wait Until Element Is Visible And Enabled xpath=//project-detail//hbr-repository-gridview//clr-dg-cell[contains(.,'${project}/')] DoAssert=${false}
\ Log To Console ${out}
\ Run Keyword If ${out} == 'PASS' Exit For Loop
\ Sleep 1

View File

@ -55,7 +55,7 @@ Package Harbor Offline
[Arguments] ${with_notary}=true ${with_clair}=true ${with_migrator}=true ${with_chartmuseum}=true
Log To Console \nStart Docker Daemon
Start Docker Daemon Locally
Log To Console \n\nmake package_offline NPM_REGISTRY=%{NPM_REGISTRY} VERSIONTAG=%{Harbor_Assets_Version} PKGVERSIONTAG=%{Harbor_Package_Version} NOTARYFLAG=${with_notary} CLAIRFLAG=${with_clair} MIGRATORFLAG=${with_migrator} CHARTFLAG=${with_chartmuseum} HTTPPROXY=
Log To Console \n\nmake package_offline BASEIMAGETAG=%{Harbor_Build_Base_Tag} NPM_REGISTRY=%{NPM_REGISTRY} VERSIONTAG=%{Harbor_Assets_Version} PKGVERSIONTAG=%{Harbor_Package_Version} NOTARYFLAG=${with_notary} CLAIRFLAG=${with_clair} MIGRATORFLAG=${with_migrator} CHARTFLAG=${with_chartmuseum} HTTPPROXY=
${rc} ${output}= Run And Return Rc And Output make package_offline NPM_REGISTRY=%{NPM_REGISTRY} VERSIONTAG=%{Harbor_Assets_Version} PKGVERSIONTAG=%{Harbor_Package_Version} NOTARYFLAG=${with_notary} CLAIRFLAG=${with_clair} MIGRATORFLAG=${with_migrator} CHARTFLAG=${with_chartmuseum} HTTPPROXY=
Log To Console ${rc}
Log To Console ${output}

View File

@ -246,7 +246,7 @@ Retry Keyword When Return Value Mismatch
Should Be Equal As Strings ${status} 'PASS'
Retry Double Keywords When Error
[Arguments] ${keyword1} ${element1} ${keyword2} ${element2}
[Arguments] ${keyword1} ${element1} ${keyword2} ${element2} ${DoAssert}=${true}
:For ${n} IN RANGE 1 5
\ Log To Console Trying ${keyword1} and ${keyword2} ${n} times ...
\ ${out1} Run Keyword And Ignore Error ${keyword1} ${element1}
@ -257,8 +257,8 @@ Retry Double Keywords When Error
\ Log To Console Return value is ${out1[0]} ${out2[0]}
\ Exit For Loop If '${out2[0]}'=='PASS'
\ Sleep 1
Return From Keyword If ${DoAssert} == ${false} '${out2[0]}'
Should Be Equal As Strings '${out2[0]}' 'PASS'
[Return] 'PASS'
Run Curl And Return Json
[Arguments] ${curl_cmd}

View File

@ -499,7 +499,7 @@ Test Case - Project Storage Quotas Dispaly And Control
${storage_quota_unit}= Set Variable MB
${image_a}= Set Variable redis
${image_b}= Set Variable logstash
${image_a_size}= Set Variable 34.16MB
${image_a_size}= Set Variable 34.15MB
${image_b_size}= Set Variable 321.03MB
${image_a_ver}= Set Variable 5.0
${image_b_ver}= Set Variable 6.8.3
@ -508,11 +508,16 @@ Test Case - Project Storage Quotas Dispaly And Control
Push Image With Tag ${ip} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} project${d} ${image_b} tag=${image_b_ver} tag1=${image_b_ver}
${storage_quota_ret}= Get Project Storage Quota Text From Project Quotas List project${d}
Should Be Equal As Strings ${storage_quota_ret} ${image_b_size} of ${storage_quota}${storage_quota_unit}
Cannot Push image ${ip} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} project${d} ${image_a}:${image_a_ver} err_msg=Quota exceeded when processing the request of adding 25.9 MiB of storage resource, which when updated to current usage of 329.3 MiB will exceed the configured upper limit of 330.0 MiB
Cannot Push image ${ip} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} project${d} ${image_a}:${image_a_ver} err_msg=Quota exceeded when processing the request of adding 25.8 MiB of storage resource, which when updated to current usage of 329.3 MiB will exceed the configured upper limit of 330.0 MiB
Go Into Project project${d}
Delete Repo project${d}/${image_b}
Push Image With Tag ${ip} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} project${d} ${image_a} tag=${image_a_ver} tag1=${image_a_ver}
${storage_quota_ret}= Get Project Storage Quota Text From Project Quotas List project${d}
${storage_quota_ret_str_left} Fetch From Left ${storage_quota_ret} 25.
Log ${storage_quota_ret_str_left}
${storage_quota_ret_str_right} Fetch From Left ${storage_quota_ret} 25.
Log ${storage_quota_ret_str_right}
Log ${storage_quota_ret_str_left}${storage_quota_ret_str_right}
Should Be Equal As Strings ${storage_quota_ret} ${image_a_size} of ${storage_quota}${storage_quota_unit}
Close Browser
@ -603,7 +608,7 @@ Test Case - Update Webhook
Create An New Project project${d}
Go Into Project project${d} has_image=${false}
Switch To Project Webhooks
Create A New Webhook ${HARBOR_URL} auth_header=auth_header${d}
Create A New Webhook ${HARBOR_URL} auth_header=auth_header${d}
Sleep 3
${d1}= Get Current Date
Update A Webhook 101.17.109.20 auth_header=auth_header${d1}

View File

@ -24,5 +24,5 @@ sudo curl -o /home/travis/gopath/src/github.com/goharbor/harbor/tests/apitests/p
sudo apt-get update && sudo apt-get install -y --no-install-recommends python-dev openjdk-7-jdk libssl-dev && sudo apt-get autoremove -y && sudo rm -rf /var/lib/apt/lists/*
sudo wget https://bootstrap.pypa.io/get-pip.py && sudo python ./get-pip.py && sudo pip install --ignore-installed urllib3 chardet requests && sudo pip install robotframework==3.0.4 robotframework-httplibrary requests dbbot robotframework-pabot --upgrade
sudo make swagger_client
sudo make install GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage CLARITYIMAGE=goharbor/harbor-clarity-ui-builder:1.6.0 NOTARYFLAG=true CLAIRFLAG=true CHARTFLAG=true
sudo make install GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage CLARITYIMAGE=goharbor/harbor-clarity-ui-builder:1.6.0 NOTARYFLAG=true CLAIRFLAG=true CHARTFLAG=true
sleep 10

View File

@ -2,5 +2,5 @@
set -e
sudo make package_online VERSIONTAG=dev-travis PKGVERSIONTAG=dev-travis UIVERSIONTAG=dev-travis GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage NOTARYFLAG=true CLAIRFLAG=true MIGRATORFLAG=false CHARTFLAG=true HTTPPROXY=
sudo make package_offline VERSIONTAG=dev-travis PKGVERSIONTAG=dev-travis UIVERSIONTAG=dev-travis GOBUILDIMAGE=golang:1.12.12 COMPILETAG=compile_golangimage NOTARYFLAG=true CLAIRFLAG=true MIGRATORFLAG=false CHARTFLAG=true HTTPPROXY=
sudo make package_online VERSIONTAG=dev-travis PKGVERSIONTAG=dev-travis UIVERSIONTAG=dev-travis GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage NOTARYFLAG=true CLAIRFLAG=true MIGRATORFLAG=false CHARTFLAG=true HTTPPROXY=
sudo make package_offline VERSIONTAG=dev-travis PKGVERSIONTAG=dev-travis UIVERSIONTAG=dev-travis GOBUILDIMAGE=golang:1.13.4 COMPILETAG=compile_golangimage NOTARYFLAG=true CLAIRFLAG=true MIGRATORFLAG=false CHARTFLAG=true HTTPPROXY=