mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-29 05:35:43 +01:00
Merge branch 'master' into remove-lib
This commit is contained in:
commit
a52b99e180
10
.travis.yml
10
.travis.yml
@ -1,23 +1,23 @@
|
|||||||
sudo: true
|
sudo: true
|
||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.12.12
|
- 1.13.4
|
||||||
go_import_path: github.com/goharbor/harbor
|
go_import_path: github.com/goharbor/harbor
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
dist: trusty
|
dist: trusty
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- go: 1.12.12
|
- go: 1.13.4
|
||||||
env:
|
env:
|
||||||
- UTTEST=true
|
- UTTEST=true
|
||||||
- go: 1.12.12
|
- go: 1.13.4
|
||||||
env:
|
env:
|
||||||
- APITEST_DB=true
|
- APITEST_DB=true
|
||||||
- go: 1.12.12
|
- go: 1.13.4
|
||||||
env:
|
env:
|
||||||
- APITEST_LDAP=true
|
- APITEST_LDAP=true
|
||||||
- go: 1.12.12
|
- go: 1.13.4
|
||||||
env:
|
env:
|
||||||
- OFFLINE=true
|
- OFFLINE=true
|
||||||
- language: node_js
|
- language: node_js
|
||||||
|
@ -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.7 | 1.9.2 |
|
||||||
| 1.8 | 1.11.2 |
|
| 1.8 | 1.11.2 |
|
||||||
| 1.9 | 1.12.12 |
|
| 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.
|
Ensure your GOPATH and PATH have been configured in accordance with the Go environment instructions.
|
||||||
|
|
||||||
|
6
Makefile
6
Makefile
@ -9,7 +9,7 @@
|
|||||||
# compile_golangimage:
|
# compile_golangimage:
|
||||||
# compile from golang image
|
# compile from golang image
|
||||||
# for example: make compile_golangimage -e GOBUILDIMAGE= \
|
# for example: make compile_golangimage -e GOBUILDIMAGE= \
|
||||||
# golang:1.12.12
|
# golang:1.13.4
|
||||||
# compile_core, compile_jobservice: compile specific binary
|
# compile_core, compile_jobservice: compile specific binary
|
||||||
#
|
#
|
||||||
# build: build Harbor docker images from photon baseimage
|
# build: build Harbor docker images from photon baseimage
|
||||||
@ -138,7 +138,7 @@ GOINSTALL=$(GOCMD) install
|
|||||||
GOTEST=$(GOCMD) test
|
GOTEST=$(GOCMD) test
|
||||||
GODEP=$(GOTEST) -i
|
GODEP=$(GOTEST) -i
|
||||||
GOFMT=gofmt -w
|
GOFMT=gofmt -w
|
||||||
GOBUILDIMAGE=golang:1.12.12
|
GOBUILDIMAGE=golang:1.13.4
|
||||||
GOBUILDPATH=/harbor
|
GOBUILDPATH=/harbor
|
||||||
|
|
||||||
# go build
|
# go build
|
||||||
@ -323,7 +323,7 @@ build:
|
|||||||
-e CLAIRVERSION=$(CLAIRVERSION) -e CLAIRADAPTERVERSION=$(CLAIRADAPTERVERSION) -e VERSIONTAG=$(VERSIONTAG) \
|
-e CLAIRVERSION=$(CLAIRVERSION) -e CLAIRADAPTERVERSION=$(CLAIRADAPTERVERSION) -e VERSIONTAG=$(VERSIONTAG) \
|
||||||
-e BUILDBIN=$(BUILDBIN) -e REDISVERSION=$(REDISVERSION) -e MIGRATORVERSION=$(MIGRATORVERSION) \
|
-e BUILDBIN=$(BUILDBIN) -e REDISVERSION=$(REDISVERSION) -e MIGRATORVERSION=$(MIGRATORVERSION) \
|
||||||
-e CHARTMUSEUMVERSION=$(CHARTMUSEUMVERSION) -e DOCKERIMAGENAME_CHART_SERVER=$(DOCKERIMAGENAME_CHART_SERVER) \
|
-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:
|
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 \
|
@for name in chartserver clair clair-adapter core db jobservice log nginx notary-server notary-signer portal prepare redis registry registryctl; do \
|
||||||
|
@ -13,10 +13,6 @@
|
|||||||
|------------------|
|
|------------------|
|
||||||
|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).|
|
|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>
|
</br> </br>
|
||||||
|
|
||||||
**Note**: The `master` branch may be in an *unstable or even broken state* during development.
|
**Note**: The `master` branch may be in an *unstable or even broken state* during development.
|
||||||
|
@ -43,25 +43,25 @@ You can compile the code by one of the three approaches:
|
|||||||
- Get official Golang image from docker hub:
|
- Get official Golang image from docker hub:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ docker pull golang:1.12.12
|
$ docker pull golang:1.13.4
|
||||||
```
|
```
|
||||||
|
|
||||||
- Build, install and bring up Harbor without Notary:
|
- Build, install and bring up Harbor without Notary:
|
||||||
|
|
||||||
```sh
|
```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:
|
- Build, install and bring up Harbor with Notary:
|
||||||
|
|
||||||
```sh
|
```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:
|
- Build, install and bring up Harbor with Clair:
|
||||||
|
|
||||||
```sh
|
```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
|
#### II. Compile code with your own Golang environment, then build Harbor
|
||||||
|
@ -36,10 +36,10 @@ version | set harbor version
|
|||||||
#### EXAMPLE:
|
#### EXAMPLE:
|
||||||
|
|
||||||
#### Build and run harbor from source code.
|
#### 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
|
### 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
|
### Start harbor with notary
|
||||||
make -e NOTARYFLAG=true start
|
make -e NOTARYFLAG=true start
|
||||||
|
@ -4,7 +4,7 @@ set +e
|
|||||||
|
|
||||||
usage(){
|
usage(){
|
||||||
echo "Usage: builder <golang image:version> <code path> <code release tag> <main.go path> <binary name>"
|
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
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.12.12
|
FROM golang:1.13.4
|
||||||
|
|
||||||
ADD . /go/src/github.com/goharbor/harbor-scanner-clair/
|
ADD . /go/src/github.com/goharbor/harbor-scanner-clair/
|
||||||
WORKDIR /go/src/github.com/goharbor/harbor-scanner-clair/
|
WORKDIR /go/src/github.com/goharbor/harbor-scanner-clair/
|
||||||
|
@ -23,7 +23,7 @@ TEMP=`mktemp -d ${TMPDIR-/tmp}/clair-adapter.XXXXXX`
|
|||||||
git clone https://github.com/goharbor/harbor-scanner-clair.git $TEMP
|
git clone https://github.com/goharbor/harbor-scanner-clair.git $TEMP
|
||||||
cd $TEMP; git checkout $VERSION; cd -
|
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
|
cp Dockerfile.binary $TEMP
|
||||||
docker build -f $TEMP/Dockerfile.binary -t clair-adapter-golang $TEMP
|
docker build -f $TEMP/Dockerfile.binary -t clair-adapter-golang $TEMP
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.12.12
|
FROM golang:1.13.4
|
||||||
|
|
||||||
ADD . /go/src/github.com/coreos/clair/
|
ADD . /go/src/github.com/coreos/clair/
|
||||||
WORKDIR /go/src/github.com/coreos/clair/
|
WORKDIR /go/src/github.com/coreos/clair/
|
||||||
|
@ -23,7 +23,7 @@ TEMP=`mktemp -d /$TMPDIR/clair.XXXXXX`
|
|||||||
git clone https://github.com/coreos/clair.git $TEMP
|
git clone https://github.com/coreos/clair.git $TEMP
|
||||||
cd $TEMP; git checkout $VERSION; cd -
|
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
|
cp Dockerfile.binary $TEMP
|
||||||
docker build -f $TEMP/Dockerfile.binary -t clair-golang $TEMP
|
docker build -f $TEMP/Dockerfile.binary -t clair-golang $TEMP
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.12.12
|
FROM golang:1.13.4
|
||||||
|
|
||||||
ARG NOTARY_VERSION
|
ARG NOTARY_VERSION
|
||||||
ARG MIGRATE_VERSION
|
ARG MIGRATE_VERSION
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.12.12
|
FROM golang:1.13.4
|
||||||
|
|
||||||
ENV DISTRIBUTION_DIR /go/src/github.com/docker/distribution
|
ENV DISTRIBUTION_DIR /go/src/github.com/docker/distribution
|
||||||
ENV BUILDTAGS include_oss include_gcs
|
ENV BUILDTAGS include_oss include_gcs
|
||||||
|
@ -50,7 +50,7 @@ type ProjectAPI struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const projectNameMaxLen int = 255
|
const projectNameMaxLen int = 255
|
||||||
const projectNameMinLen int = 2
|
const projectNameMinLen int = 1
|
||||||
const restrictedNameChars = `[a-z0-9]+(?:[._-][a-z0-9]+)*`
|
const restrictedNameChars = `[a-z0-9]+(?:[._-][a-z0-9]+)*`
|
||||||
|
|
||||||
// Prepare validates the URL and the user
|
// Prepare validates the URL and the user
|
||||||
|
@ -119,7 +119,7 @@ func TestAddProject(t *testing.T) {
|
|||||||
// case 4: response code = 400 : Project name is illegal in length
|
// case 4: response code = 400 : Project name is illegal in length
|
||||||
fmt.Println("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 {
|
if err != nil {
|
||||||
t.Error("Error while creat project", err.Error())
|
t.Error("Error while creat project", err.Error())
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
|
@ -34,7 +34,6 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/core/auth"
|
"github.com/goharbor/harbor/src/core/auth"
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
"github.com/goharbor/harbor/src/core/config"
|
||||||
"github.com/goharbor/harbor/src/pkg/authproxy"
|
"github.com/goharbor/harbor/src/pkg/authproxy"
|
||||||
k8s_api_v1beta1 "k8s.io/api/authentication/v1beta1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const refreshDuration = 2 * time.Second
|
const refreshDuration = 2 * time.Second
|
||||||
@ -101,31 +100,12 @@ func (a *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
|
|||||||
s := session{}
|
s := session{}
|
||||||
err = json.Unmarshal(data, &s)
|
err = json.Unmarshal(data, &s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to read session %v", err)
|
return nil, auth.NewErrAuth(fmt.Sprintf("failed to read session %v", err))
|
||||||
}
|
}
|
||||||
|
if err := a.tokenReview(s.SessionID, user); err != nil {
|
||||||
reviewResponse, err := a.tokenReview(s.SessionID)
|
return nil, auth.NewErrAuth(err.Error())
|
||||||
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
|
|
||||||
}
|
}
|
||||||
return user, nil
|
return user, nil
|
||||||
|
|
||||||
} else if resp.StatusCode == http.StatusUnauthorized {
|
} else if resp.StatusCode == http.StatusUnauthorized {
|
||||||
return nil, auth.ErrAuth{}
|
return nil, auth.ErrAuth{}
|
||||||
} else {
|
} 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)
|
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))
|
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()
|
httpAuthProxySetting, err := config.HTTPAuthProxySetting()
|
||||||
if err != nil {
|
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.
|
// OnBoardUser delegates to dao pkg to insert/update data in DB.
|
||||||
|
@ -325,16 +325,16 @@ func (ap *authProxyReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
|||||||
log.Errorf("fail to get auth proxy settings, %v", err)
|
log.Errorf("fail to get auth proxy settings, %v", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
tokenReviewResponse, err := authproxy.TokenReview(proxyPwd, httpAuthProxyConf)
|
tokenReviewStatus, err := authproxy.TokenReview(proxyPwd, httpAuthProxyConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("fail to review token, %v", err)
|
log.Errorf("fail to review token, %v", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if rawUserName != tokenReviewStatus.User.Username {
|
||||||
if !tokenReviewResponse.Status.Authenticated {
|
log.Errorf("user name doesn't match with token: %s", rawUserName)
|
||||||
log.Errorf("fail to auth user: %s", rawUserName)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := dao.GetUser(models.User{
|
user, err := dao.GetUser(models.User{
|
||||||
Username: rawUserName,
|
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)
|
log.Errorf("User: %s has not been on boarded yet.", rawUserName)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if rawUserName != tokenReviewResponse.Status.User.Username {
|
u2, err := authproxy.UserFromReviewStatus(tokenReviewStatus)
|
||||||
log.Errorf("user name doesn't match with token: %s", rawUserName)
|
if err != nil {
|
||||||
|
log.Errorf("Failed to get user information from token review status, error: %v", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
user.GroupIDs = u2.GroupIDs
|
||||||
pm := config.GlobalProjectMgr
|
pm := config.GlobalProjectMgr
|
||||||
log.Debug("creating local database security context for auth proxy...")
|
log.Debug("creating local database security context for auth proxy...")
|
||||||
securCtx := local.NewSecurityContext(user, pm)
|
securCtx := local.NewSecurityContext(user, pm)
|
||||||
|
@ -2,6 +2,9 @@ package authproxy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"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/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils/log"
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
k8s_api_v1beta1 "k8s.io/api/authentication/v1beta1"
|
k8s_api_v1beta1 "k8s.io/api/authentication/v1beta1"
|
||||||
@ -13,8 +16,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// TokenReview ...
|
// 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.
|
// Init auth client with the auth proxy endpoint.
|
||||||
authClientCfg := &rest.Config{
|
authClientCfg := &rest.Config{
|
||||||
Host: authProxyConfig.TokenReviewEndpoint,
|
Host: authProxyConfig.TokenReviewEndpoint,
|
||||||
@ -22,14 +26,14 @@ func TokenReview(sessionID string, authProxyConfig *models.HTTPAuthProxy) (*k8s_
|
|||||||
GroupVersion: &schema.GroupVersion{},
|
GroupVersion: &schema.GroupVersion{},
|
||||||
NegotiatedSerializer: serializer.DirectCodecFactory{CodecFactory: scheme.Codecs},
|
NegotiatedSerializer: serializer.DirectCodecFactory{CodecFactory: scheme.Codecs},
|
||||||
},
|
},
|
||||||
BearerToken: sessionID,
|
BearerToken: rawToken,
|
||||||
TLSClientConfig: rest.TLSClientConfig{
|
TLSClientConfig: rest.TLSClientConfig{
|
||||||
Insecure: !authProxyConfig.VerifyCert,
|
Insecure: !authProxyConfig.VerifyCert,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
authClient, err := rest.RESTClientFor(authClientCfg)
|
authClient, err := rest.RESTClientFor(authClientCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return emptyStatus, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do auth with the token.
|
// Do auth with the token.
|
||||||
@ -39,27 +43,49 @@ func TokenReview(sessionID string, authProxyConfig *models.HTTPAuthProxy) (*k8s_
|
|||||||
APIVersion: "authentication.k8s.io/v1beta1",
|
APIVersion: "authentication.k8s.io/v1beta1",
|
||||||
},
|
},
|
||||||
Spec: k8s_api_v1beta1.TokenReviewSpec{
|
Spec: k8s_api_v1beta1.TokenReviewSpec{
|
||||||
Token: sessionID,
|
Token: rawToken,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
res := authClient.Post().Body(tokenReviewRequest).Do()
|
res := authClient.Post().Body(tokenReviewRequest).Do()
|
||||||
err = res.Error()
|
err = res.Error()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("fail to POST auth request, %v", err)
|
log.Errorf("fail to POST auth request, %v", err)
|
||||||
return nil, err
|
return emptyStatus, err
|
||||||
}
|
}
|
||||||
resRaw, err := res.Raw()
|
resRaw, err := res.Raw()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("fail to get raw data of token review, %v", err)
|
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.
|
// Parse the auth response, check the user name and authenticated status.
|
||||||
tokenReviewResponse := &k8s_api_v1beta1.TokenReview{}
|
tokenReviewResponse := &k8s_api_v1beta1.TokenReview{}
|
||||||
err = json.Unmarshal(resRaw, &tokenReviewResponse)
|
err = json.Unmarshal(resRaw, tokenReviewResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("fail to decode token review, %v", err)
|
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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
87
src/pkg/authproxy/http_test.go
Normal file
87
src/pkg/authproxy/http_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgModule, APP_INITIALIZER, LOCALE_ID, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { NgModule, APP_INITIALIZER, LOCALE_ID, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
import { InterceptHttpService } from './intercept-http.service';
|
||||||
|
|
||||||
import { BaseModule } from './base/base.module';
|
import { BaseModule } from './base/base.module';
|
||||||
import { HarborRoutingModule } from './harbor-routing.module';
|
import { HarborRoutingModule } from './harbor-routing.module';
|
||||||
@ -43,6 +44,8 @@ import { InterrogationServicesComponent } from "./interrogation-services/interro
|
|||||||
import { LabelsComponent } from './labels/labels.component';
|
import { LabelsComponent } from './labels/labels.component';
|
||||||
import { ProjectQuotasComponent } from './project-quotas/project-quotas.component';
|
import { ProjectQuotasComponent } from './project-quotas/project-quotas.component';
|
||||||
import { HarborLibraryModule } from "../lib/harbor-library.module";
|
import { HarborLibraryModule } from "../lib/harbor-library.module";
|
||||||
|
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||||
|
|
||||||
|
|
||||||
registerLocaleData(zh, 'zh-cn');
|
registerLocaleData(zh, 'zh-cn');
|
||||||
registerLocaleData(es, 'es-es');
|
registerLocaleData(es, 'es-es');
|
||||||
@ -95,7 +98,9 @@ export function getCurrentLanguage(translateService: TranslateService) {
|
|||||||
deps: [ AppConfigService, SkinableConfig],
|
deps: [ AppConfigService, SkinableConfig],
|
||||||
multi: true
|
multi: true
|
||||||
},
|
},
|
||||||
{provide: LOCALE_ID, useValue: "en-US"}
|
{provide: LOCALE_ID, useValue: "en-US"},
|
||||||
|
{ provide: HTTP_INTERCEPTORS, useClass: InterceptHttpService, multi: true }
|
||||||
|
|
||||||
],
|
],
|
||||||
schemas: [
|
schemas: [
|
||||||
CUSTOM_ELEMENTS_SCHEMA
|
CUSTOM_ELEMENTS_SCHEMA
|
||||||
|
58
src/portal/src/app/intercept-http.service.spec.ts
Normal file
58
src/portal/src/app/intercept-http.service.spec.ts
Normal 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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
28
src/portal/src/app/intercept-http.service.ts
Normal file
28
src/portal/src/app/intercept-http.service.ts
Normal 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);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@
|
|||||||
<div class="clr-control-container" [class.clr-error]="!isNameValid">
|
<div class="clr-control-container" [class.clr-error]="!isNameValid">
|
||||||
<div class="clr-input-wrapper">
|
<div class="clr-input-wrapper">
|
||||||
<input type="text" id="create_project_name" [(ngModel)]="project.name" name="create_project_name" class="clr-input input-width"
|
<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()'>
|
(keyup)='handleValidation()'>
|
||||||
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
|
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
|
||||||
<span class="spinner spinner-inline" [hidden]="!checkOnGoing"></span>
|
<span class="spinner spinner-inline" [hidden]="!checkOnGoing"></span>
|
||||||
|
@ -214,9 +214,8 @@
|
|||||||
"PUBLIC_PROJECTS": "Public Projects",
|
"PUBLIC_PROJECTS": "Public Projects",
|
||||||
"PROJECT": "Project",
|
"PROJECT": "Project",
|
||||||
"NEW_PROJECT": "New 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_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_ALREADY_EXISTS": "Project name already exists.",
|
||||||
"NAME_IS_ILLEGAL": "Project name is invalid.",
|
"NAME_IS_ILLEGAL": "Project name is invalid.",
|
||||||
"UNKNOWN_ERROR": "An unknown error occurred while creating the project.",
|
"UNKNOWN_ERROR": "An unknown error occurred while creating the project.",
|
||||||
|
@ -214,10 +214,9 @@
|
|||||||
"PUBLIC_PROJECTS": "Public Projects",
|
"PUBLIC_PROJECTS": "Public Projects",
|
||||||
"PROJECT": "Proyecto",
|
"PROJECT": "Proyecto",
|
||||||
"NEW_PROJECT": "Nuevo 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.",
|
"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_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_ALREADY_EXISTS": "Ya existe un proyecto con ese nombre.",
|
||||||
"NAME_IS_ILLEGAL": "El nombre del proyecto no es valido.",
|
"NAME_IS_ILLEGAL": "El nombre del proyecto no es valido.",
|
||||||
"UNKNOWN_ERROR": "Ha ocurrido un error al crear el proyecto.",
|
"UNKNOWN_ERROR": "Ha ocurrido un error al crear el proyecto.",
|
||||||
|
@ -208,9 +208,8 @@
|
|||||||
"PUBLIC_PROJECTS": "Projets Publics",
|
"PUBLIC_PROJECTS": "Projets Publics",
|
||||||
"PROJECT": "Projet",
|
"PROJECT": "Projet",
|
||||||
"NEW_PROJECT": "Nouveau 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_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_ALREADY_EXISTS": "Le nom du projet existe déjà.",
|
||||||
"NAME_IS_ILLEGAL": "Le nom du projet est invalide.",
|
"NAME_IS_ILLEGAL": "Le nom du projet est invalide.",
|
||||||
"UNKNOWN_ERROR": "Une erreur inconnue s'est produite lors de la création du projet.",
|
"UNKNOWN_ERROR": "Une erreur inconnue s'est produite lors de la création du projet.",
|
||||||
|
@ -212,9 +212,8 @@
|
|||||||
"PUBLIC_PROJECTS": "Projetos Públicos",
|
"PUBLIC_PROJECTS": "Projetos Públicos",
|
||||||
"PROJECT": "Projeto",
|
"PROJECT": "Projeto",
|
||||||
"NEW_PROJECT": "Novo 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_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_ALREADY_EXISTS": "Nome do projeto já existe.",
|
||||||
"NAME_IS_ILLEGAL": "Nome do projeto é inválido.",
|
"NAME_IS_ILLEGAL": "Nome do projeto é inválido.",
|
||||||
"UNKNOWN_ERROR": "Um erro desconhecido ocorreu ao criar o projeto.",
|
"UNKNOWN_ERROR": "Um erro desconhecido ocorreu ao criar o projeto.",
|
||||||
|
@ -214,9 +214,8 @@
|
|||||||
"PUBLIC_PROJECTS": "Genel Projeler",
|
"PUBLIC_PROJECTS": "Genel Projeler",
|
||||||
"PROJECT": "Proje",
|
"PROJECT": "Proje",
|
||||||
"NEW_PROJECT": "Yeni 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_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_ALREADY_EXISTS": "Proje adı zaten var.",
|
||||||
"NAME_IS_ILLEGAL": "Proje adı geçersiz.",
|
"NAME_IS_ILLEGAL": "Proje adı geçersiz.",
|
||||||
"UNKNOWN_ERROR": "Proje oluşturulurken bilinmeyen bir hata oluştu.",
|
"UNKNOWN_ERROR": "Proje oluşturulurken bilinmeyen bir hata oluştu.",
|
||||||
|
@ -213,9 +213,8 @@
|
|||||||
"PUBLIC_PROJECTS": "公开项目",
|
"PUBLIC_PROJECTS": "公开项目",
|
||||||
"PROJECT": "项目",
|
"PROJECT": "项目",
|
||||||
"NEW_PROJECT": "新建项目",
|
"NEW_PROJECT": "新建项目",
|
||||||
"NAME_TOOLTIP": "项目名称由小写字符、数字和._-组成且至少2个字符并以字符或者数字开头。",
|
"NAME_TOOLTIP": "项目名称由小写字符、数字和._-组成且至少1个字符并以字符或者数字开头。",
|
||||||
"NAME_IS_REQUIRED": "项目名称为必填项。",
|
"NAME_IS_REQUIRED": "项目名称为必填项。",
|
||||||
"NAME_MINIMUM_LENGTH": "项目名称长度过短,至少多于2个字符。",
|
|
||||||
"NAME_ALREADY_EXISTS": "项目名称已存在。",
|
"NAME_ALREADY_EXISTS": "项目名称已存在。",
|
||||||
"NAME_IS_ILLEGAL": "项目名称非法。",
|
"NAME_IS_ILLEGAL": "项目名称非法。",
|
||||||
"UNKNOWN_ERROR": "创建项目时发生未知错误。",
|
"UNKNOWN_ERROR": "创建项目时发生未知错误。",
|
||||||
|
@ -93,6 +93,14 @@ export Harbor_Assets_Version=$Harbor_Assets_Version
|
|||||||
export Harbor_Package_Version=$Harbor_Package_Version
|
export Harbor_Package_Version=$Harbor_Package_Version
|
||||||
export NPM_REGISTRY=$NPM_REGISTRY
|
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 "--------------------------------------------------"
|
||||||
echo "Harbor Package version: $Harbor_Package_Version"
|
echo "Harbor Package version: $Harbor_Package_Version"
|
||||||
echo "Harbor Assets version: $Harbor_Assets_Version"
|
echo "Harbor Assets version: $Harbor_Assets_Version"
|
||||||
|
@ -26,9 +26,8 @@ Go Into Project
|
|||||||
\ Retry Wait Element ${search_input}
|
\ Retry Wait Element ${search_input}
|
||||||
\ Retry Clear Element Text ${search_input}
|
\ Retry Clear Element Text ${search_input}
|
||||||
\ Input Text ${search_input} ${project}
|
\ 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!\")] DoAssert=${false}
|
||||||
\ ${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}/')] 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}/')]
|
|
||||||
\ Log To Console ${out}
|
\ Log To Console ${out}
|
||||||
\ Run Keyword If ${out} == 'PASS' Exit For Loop
|
\ Run Keyword If ${out} == 'PASS' Exit For Loop
|
||||||
\ Sleep 1
|
\ Sleep 1
|
||||||
|
@ -55,7 +55,7 @@ Package Harbor Offline
|
|||||||
[Arguments] ${with_notary}=true ${with_clair}=true ${with_migrator}=true ${with_chartmuseum}=true
|
[Arguments] ${with_notary}=true ${with_clair}=true ${with_migrator}=true ${with_chartmuseum}=true
|
||||||
Log To Console \nStart Docker Daemon
|
Log To Console \nStart Docker Daemon
|
||||||
Start Docker Daemon Locally
|
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=
|
${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 ${rc}
|
||||||
Log To Console ${output}
|
Log To Console ${output}
|
||||||
|
@ -246,7 +246,7 @@ Retry Keyword When Return Value Mismatch
|
|||||||
Should Be Equal As Strings ${status} 'PASS'
|
Should Be Equal As Strings ${status} 'PASS'
|
||||||
|
|
||||||
Retry Double Keywords When Error
|
Retry Double Keywords When Error
|
||||||
[Arguments] ${keyword1} ${element1} ${keyword2} ${element2}
|
[Arguments] ${keyword1} ${element1} ${keyword2} ${element2} ${DoAssert}=${true}
|
||||||
:For ${n} IN RANGE 1 5
|
:For ${n} IN RANGE 1 5
|
||||||
\ Log To Console Trying ${keyword1} and ${keyword2} ${n} times ...
|
\ Log To Console Trying ${keyword1} and ${keyword2} ${n} times ...
|
||||||
\ ${out1} Run Keyword And Ignore Error ${keyword1} ${element1}
|
\ ${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]}
|
\ Log To Console Return value is ${out1[0]} ${out2[0]}
|
||||||
\ Exit For Loop If '${out2[0]}'=='PASS'
|
\ Exit For Loop If '${out2[0]}'=='PASS'
|
||||||
\ Sleep 1
|
\ Sleep 1
|
||||||
|
Return From Keyword If ${DoAssert} == ${false} '${out2[0]}'
|
||||||
Should Be Equal As Strings '${out2[0]}' 'PASS'
|
Should Be Equal As Strings '${out2[0]}' 'PASS'
|
||||||
[Return] 'PASS'
|
|
||||||
|
|
||||||
Run Curl And Return Json
|
Run Curl And Return Json
|
||||||
[Arguments] ${curl_cmd}
|
[Arguments] ${curl_cmd}
|
||||||
|
@ -499,7 +499,7 @@ Test Case - Project Storage Quotas Dispaly And Control
|
|||||||
${storage_quota_unit}= Set Variable MB
|
${storage_quota_unit}= Set Variable MB
|
||||||
${image_a}= Set Variable redis
|
${image_a}= Set Variable redis
|
||||||
${image_b}= Set Variable logstash
|
${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_b_size}= Set Variable 321.03MB
|
||||||
${image_a_ver}= Set Variable 5.0
|
${image_a_ver}= Set Variable 5.0
|
||||||
${image_b_ver}= Set Variable 6.8.3
|
${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}
|
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}
|
${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}
|
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}
|
Go Into Project project${d}
|
||||||
Delete Repo project${d}/${image_b}
|
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}
|
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}= 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}
|
Should Be Equal As Strings ${storage_quota_ret} ${image_a_size} of ${storage_quota}${storage_quota_unit}
|
||||||
Close Browser
|
Close Browser
|
||||||
|
|
||||||
|
@ -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 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 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 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
|
sleep 10
|
||||||
|
@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
set -e
|
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_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.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.13.4 COMPILETAG=compile_golangimage NOTARYFLAG=true CLAIRFLAG=true MIGRATORFLAG=false CHARTFLAG=true HTTPPROXY=
|
||||||
|
Loading…
Reference in New Issue
Block a user