Merge pull request #2672 from ywk253100/170628_getpm

Create a global project manager
This commit is contained in:
Wenkai Yin 2017-06-30 16:38:29 +08:00 committed by GitHub
commit 2818c047bf
11 changed files with 259 additions and 46 deletions

View File

@ -83,6 +83,7 @@ services:
- ./common/config/ui/private_key.pem:/etc/ui/private_key.pem:z
- /data/secretkey:/etc/ui/key:z
- /data/ca_download/:/etc/ui/ca/:z
- /data/service_token:/etc/ui/service_token:z
networks:
- harbor
depends_on:

View File

@ -77,6 +77,7 @@ services:
- ./common/config/ui/private_key.pem:/etc/ui/private_key.pem:z
- /data/secretkey:/etc/ui/key:z
- /data/ca_download/:/etc/ui/ca/:z
- /data/service_token:/etc/ui/service_token:z
networks:
- harbor
depends_on:

View File

@ -15,8 +15,10 @@
package config
import (
"crypto/tls"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
@ -33,8 +35,9 @@ import (
)
const (
defaultKeyPath string = "/etc/ui/key"
secretCookieName string = "secret"
defaultKeyPath string = "/etc/ui/key"
defaultTokenFilePath string = "/etc/ui/service_token"
secretCookieName string = "secret"
)
var (
@ -46,6 +49,9 @@ var (
GlobalProjectMgr projectmanager.ProjectManager
mg *comcfg.Manager
keyProvider comcfg.KeyProvider
// AdmiralClient is initialized only under integration deploy mode
// and can be passed to project manager as a parameter
AdmiralClient *http.Client
)
// Init configurations
@ -105,9 +111,19 @@ func initProjectManager() {
}
// integration with admiral
// TODO create project manager based on pms using service account
log.Info("initializing the project manager based on PMS...")
GlobalProjectMgr = pms.NewProjectManager(AdmiralEndpoint(), "")
// TODO read ca/cert file and pass it to the TLS config
AdminserverClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
GlobalProjectMgr = pms.NewProjectManager(AdminserverClient,
AdmiralEndpoint(), &pms.FileTokenReader{
Path: defaultTokenFilePath,
})
}
// Load configurations

View File

@ -51,8 +51,8 @@ func Init() {
if config.WithAdmiral() {
reqCtxModifiers = []ReqCtxModifier{
&secretReqCtxModifier{config.SecretStore},
&basicAuthReqCtxModifier{},
&tokenReqCtxModifier{},
&basicAuthReqCtxModifier{},
&unauthorizedReqCtxModifier{}}
return
}
@ -131,15 +131,14 @@ func (b *basicAuthReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
if config.WithAdmiral() {
// integration with admiral
token, authCtx, err := authcontext.Login(config.AdmiralEndpoint(), username, password)
_, authCtx, err := authcontext.Login(config.AdmiralEndpoint(), username, password)
if err != nil {
log.Errorf("failed to authenticate %s: %v", username, err)
return false
}
log.Debug("creating PMS project manager...")
pm = pms.NewProjectManager(config.AdmiralEndpoint(), token)
log.Debug("using glocal project manager...")
pm = config.GlobalProjectMgr
log.Debug("creating admiral security context...")
securCtx = admiral.NewSecurityContext(authCtx, pm)
} else {
@ -211,7 +210,10 @@ func (t *tokenReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
}
log.Debug("creating PMS project manager...")
pm := pms.NewProjectManager(config.AdmiralEndpoint(), token)
pm := pms.NewProjectManager(config.AdmiralClient,
config.AdmiralEndpoint(), &pms.RawTokenReader{
Token: token,
})
log.Debug("creating admiral security context...")
securCtx := admiral.NewSecurityContext(authContext, pm)
setSecurCtxAndPM(ctx.Request, securCtx, pm)
@ -230,7 +232,8 @@ func (u *unauthorizedReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
if config.WithAdmiral() {
// integration with admiral
log.Debug("creating PMS project manager...")
pm = pms.NewProjectManager(config.AdmiralEndpoint(), "")
pm = pms.NewProjectManager(config.AdmiralClient,
config.AdmiralEndpoint(), nil)
log.Debug("creating admiral security context...")
securCtx = admiral.NewSecurityContext(nil, pm)
} else {

View File

@ -32,14 +32,12 @@ import (
"github.com/vmware/harbor/src/common/utils/log"
)
var transport = &http.Transport{}
// ProjectManager implements projectmanager.ProjecdtManager interface
// base on project management service
type ProjectManager struct {
endpoint string
token string
client *http.Client
client *http.Client
endpoint string
tokenReader TokenReader
}
type user struct {
@ -58,13 +56,12 @@ type project struct {
}
// NewProjectManager returns an instance of ProjectManager
func NewProjectManager(endpoint, token string) *ProjectManager {
func NewProjectManager(client *http.Client, endpoint string,
tokenReader TokenReader) *ProjectManager {
return &ProjectManager{
endpoint: strings.TrimRight(endpoint, "/"),
token: token,
client: &http.Client{
Transport: transport,
},
client: client,
endpoint: strings.TrimRight(endpoint, "/"),
tokenReader: tokenReader,
}
}
@ -307,7 +304,7 @@ func (p *ProjectManager) GetPublic() ([]*models.Project, error) {
// GetByMember ...
func (p *ProjectManager) GetByMember(username string) ([]*models.Project, error) {
projects := []*models.Project{}
ctx, err := authcontext.GetAuthCtxOfUser(p.endpoint, p.token, username)
ctx, err := authcontext.GetAuthCtxOfUser(p.endpoint, p.getToken(), username)
if err != nil {
return projects, err
}
@ -425,7 +422,7 @@ func (p *ProjectManager) send(method, path string, body io.Reader) ([]byte, erro
return nil, err
}
req.Header.Add("x-xenon-auth-token", p.token)
req.Header.Add("x-xenon-auth-token", p.getToken())
url := req.URL.String()
@ -452,3 +449,16 @@ func (p *ProjectManager) send(method, path string, body io.Reader) ([]byte, erro
return b, nil
}
func (p *ProjectManager) getToken() string {
if p.tokenReader == nil {
return ""
}
token, err := p.tokenReader.ReadToken()
if err != nil {
token = ""
log.Errorf("failed to read token: %v", err)
}
return token
}

View File

@ -15,6 +15,7 @@
package pms
import (
"net/http"
"sort"
"testing"
@ -24,8 +25,11 @@ import (
)
var (
endpoint = "http://127.0.0.1:8282"
token = ""
client = http.DefaultClient
endpoint = "http://127.0.0.1:8282"
tokenReader = &RawTokenReader{
Token: "",
}
)
func TestConvert(t *testing.T) {
@ -177,7 +181,7 @@ func TestParse(t *testing.T) {
}
func TestGet(t *testing.T) {
pm := NewProjectManager(endpoint, token)
pm := NewProjectManager(client, endpoint, tokenReader)
name := "project_for_test_get"
id, err := pm.Create(&models.Project{
Name: name,
@ -211,7 +215,7 @@ func TestGet(t *testing.T) {
}
func TestIsPublic(t *testing.T) {
pm := NewProjectManager(endpoint, token)
pm := NewProjectManager(client, endpoint, tokenReader)
// invalid input type
public, err := pm.IsPublic([]string{})
@ -259,7 +263,7 @@ func TestIsPublic(t *testing.T) {
}
func TestExist(t *testing.T) {
pm := NewProjectManager(endpoint, token)
pm := NewProjectManager(client, endpoint, tokenReader)
// invalid input type
exist, err := pm.Exist([]string{})
@ -289,7 +293,7 @@ func TestExist(t *testing.T) {
}
func TestGetRoles(t *testing.T) {
pm := NewProjectManager(endpoint, token)
pm := NewProjectManager(client, endpoint, tokenReader)
// nil username, nil project
roles, err := pm.GetRoles("", nil)
@ -316,7 +320,7 @@ func TestGetRoles(t *testing.T) {
}
func TestGetPublic(t *testing.T) {
pm := NewProjectManager(endpoint, token)
pm := NewProjectManager(client, endpoint, tokenReader)
projects, err := pm.GetPublic()
assert.Nil(t, nil)
@ -350,7 +354,7 @@ func TestGetByMember(t *testing.T) {
}
func TestCreate(t *testing.T) {
pm := NewProjectManager(endpoint, token)
pm := NewProjectManager(client, endpoint, tokenReader)
name := "project_for_test_create"
id, err := pm.Create(&models.Project{
@ -375,7 +379,7 @@ func TestCreate(t *testing.T) {
}
func TestDelete(t *testing.T) {
pm := NewProjectManager(endpoint, token)
pm := NewProjectManager(client, endpoint, tokenReader)
// non-exist project
err := pm.Delete(int64(0))
@ -401,13 +405,13 @@ func TestDelete(t *testing.T) {
}
func TestUpdate(t *testing.T) {
pm := NewProjectManager(endpoint, token)
pm := NewProjectManager(client, endpoint, tokenReader)
err := pm.Update(nil, nil)
assert.NotNil(t, err)
}
func TestGetAll(t *testing.T) {
pm := NewProjectManager(endpoint, token)
pm := NewProjectManager(client, endpoint, tokenReader)
name1 := "project_for_test_get_all_01"
id1, err := pm.Create(&models.Project{
@ -471,7 +475,7 @@ func TestGetAll(t *testing.T) {
}
func TestGetTotal(t *testing.T) {
pm := NewProjectManager(endpoint, token)
pm := NewProjectManager(client, endpoint, tokenReader)
total1, err := pm.GetTotal(nil)
require.Nil(t, err)
@ -489,13 +493,13 @@ func TestGetTotal(t *testing.T) {
}
func TestGetHasReadPerm(t *testing.T) {
pm := NewProjectManager(endpoint, token)
pm := NewProjectManager(client, endpoint, tokenReader)
_, err := pm.GetHasReadPerm()
assert.NotNil(t, err)
}
func delete(t *testing.T, id int64) {
pm := NewProjectManager(endpoint, token)
pm := NewProjectManager(client, endpoint, tokenReader)
if err := pm.Delete(id); err != nil {
t.Logf("failed to delete project %d: %v", id, err)
}

View File

@ -0,0 +1,84 @@
// Copyright (c) 2017 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 pms
import (
"bufio"
"fmt"
"io"
"os"
"strings"
)
const (
key = "access_token"
)
// TokenReader is an interface used to wrap the way how to get token
type TokenReader interface {
// ReadToken reads token
ReadToken() (string, error)
}
// RawTokenReader just returns the token contained by field Token
type RawTokenReader struct {
Token string
}
// ReadToken ...
func (r *RawTokenReader) ReadToken() (string, error) {
return r.Token, nil
}
// FileTokenReader reads token from file
type FileTokenReader struct {
Path string
}
// ReadToken ...
func (f *FileTokenReader) ReadToken() (string, error) {
file, err := os.Open(f.Path)
if err != nil {
return "", err
}
defer file.Close()
return readToken(file)
}
func readToken(reader io.Reader) (string, error) {
if reader == nil {
return "", fmt.Errorf("reader is nil")
}
r := bufio.NewReader(reader)
for {
line, _, err := r.ReadLine()
if err != nil {
if err == io.EOF {
err = fmt.Errorf("%s not found", key)
}
return "", err
}
strs := strings.SplitN(string(line), "=", 2)
if len(strs) != 2 {
continue
}
if strs[0] == key {
return strs[1], nil
}
}
}

View File

@ -0,0 +1,98 @@
// Copyright (c) 2017 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 pms
import (
"bytes"
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRawTokenReader(t *testing.T) {
raw := "token"
reader := &RawTokenReader{
Token: raw,
}
token, err := reader.ReadToken()
require.Nil(t, err)
assert.Equal(t, raw, token)
}
func TestReadToken(t *testing.T) {
// nil reader
_, err := readToken(nil)
assert.NotNil(t, err)
// empty
reader := bytes.NewReader([]byte{})
_, err = readToken(reader)
assert.NotNil(t, err)
// contains no "access_token"
content := "key1=value\nkey2=value2"
reader = bytes.NewReader([]byte(content))
_, err = readToken(reader)
assert.NotNil(t, err)
// contains "access_token" but no "="
content = "access_token value\nkey2=value2"
reader = bytes.NewReader([]byte(content))
_, err = readToken(reader)
assert.NotNil(t, err)
// contains "access_token" and "=", but no value
content = "access_token=\nkey2=value2"
reader = bytes.NewReader([]byte(content))
token, err := readToken(reader)
require.Nil(t, err)
assert.Len(t, token, 0)
// valid "access_token"
content = "access_token=token\nkey2=value2"
reader = bytes.NewReader([]byte(content))
token, err = readToken(reader)
require.Nil(t, err)
assert.Equal(t, "token", token)
}
func TestFileTokenReader(t *testing.T) {
// file not exist
path := "/tmp/not_exist_file"
reader := &FileTokenReader{
Path: path,
}
_, err := reader.ReadToken()
assert.NotNil(t, err)
// file exist
path = "/tmp/exist_file"
err = ioutil.WriteFile(path, []byte("access_token=token"), 0x0666)
require.Nil(t, err)
defer os.Remove(path)
reader = &FileTokenReader{
Path: path,
}
token, err := reader.ReadToken()
require.Nil(t, err)
assert.Equal(t, "token", token)
}

View File

@ -112,7 +112,6 @@ func TestEnvPolicyChecker(t *testing.T) {
}
func TestPMSPolicyChecker(t *testing.T) {
var defaultConfigAdmiral = map[string]interface{}{
common.ExtEndpoint: "https://" + endpoint,
common.WithNotary: true,
@ -131,7 +130,8 @@ func TestPMSPolicyChecker(t *testing.T) {
panic(err)
}
pm := pms.NewProjectManager(admiralEndpoint, token)
pm := pms.NewProjectManager(http.DefaultClient,
admiralEndpoint, nil)
name := "project_for_test_get_sev_low"
id, err := pm.Create(&models.Project{
Name: name,

View File

@ -9,7 +9,6 @@ import (
// "github.com/vmware/harbor/src/ui/api"
"github.com/vmware/harbor/src/ui/config"
"github.com/vmware/harbor/src/ui/projectmanager"
"github.com/vmware/harbor/src/ui/projectmanager/pms"
"context"
"fmt"
@ -102,7 +101,7 @@ func newPMSPolicyChecker(pm projectmanager.ProjectManager) policyChecker {
// TODO: Get project manager with PM factory.
func getPolicyChecker() policyChecker {
if config.WithAdmiral() {
return newPMSPolicyChecker(pms.NewProjectManager(config.AdmiralEndpoint(), ""))
return newPMSPolicyChecker(config.GlobalProjectMgr)
}
return EnvChecker
}

View File

@ -27,7 +27,6 @@ import (
"github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/ui/api"
"github.com/vmware/harbor/src/ui/config"
"github.com/vmware/harbor/src/ui/projectmanager/pms"
uiutils "github.com/vmware/harbor/src/ui/utils"
)
@ -161,9 +160,7 @@ func autoScanEnabled(projectName string) bool {
return false
}
if config.WithAdmiral() {
//TODO get a project manager based on service account.
var pm *pms.ProjectManager = pms.NewProjectManager("", "")
p, err := pm.Get(projectName)
p, err := config.GlobalProjectMgr.Get(projectName)
if err != nil {
log.Warningf("failed to get project, error: %v", err)
return false