Merge master into job_service and fix conflicts

This commit is contained in:
Steven Zou 2018-03-30 19:26:04 +08:00
commit ba91fc2861
10 changed files with 91 additions and 33 deletions

View File

@ -61,7 +61,7 @@ var adminServerDefaultConfig = map[string]interface{}{
common.TokenExpiration: 30,
common.CfgExpiration: 5,
common.AdminInitialPassword: "password",
common.AdmiralEndpoint: "http://www.vmware.com",
common.AdmiralEndpoint: "",
common.WithNotary: false,
common.WithClair: false,
common.ClairDBUsername: "postgres",
@ -84,8 +84,13 @@ func NewAdminserver(config map[string]interface{}) (*httptest.Server, error) {
m := []*RequestHandlerMapping{}
if config == nil {
config = adminServerDefaultConfig
} else {
for k, v := range adminServerDefaultConfig {
if _, ok := config[k]; !ok {
config[k] = v
}
}
}
b, err := json.Marshal(config)
if err != nil {
return nil, err

View File

@ -16,8 +16,10 @@ package config
import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"strconv"
@ -29,7 +31,6 @@ import (
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/secret"
"github.com/vmware/harbor/src/common/utils/log"
jobservice_client "github.com/vmware/harbor/src/jobservice/client"
"github.com/vmware/harbor/src/ui/promgr"
"github.com/vmware/harbor/src/ui/promgr/pmsdriver"
"github.com/vmware/harbor/src/ui/promgr/pmsdriver/admiral"
@ -56,8 +57,8 @@ var (
AdmiralClient *http.Client
// TokenReader is used in integration mode to read token
TokenReader admiral.TokenReader
// GlobalJobserviceClient is a global client for jobservice
GlobalJobserviceClient jobservice_client.Client
defaultCACertPath = "/etc/ui/ca/ca.crt"
)
// Init configurations
@ -94,7 +95,10 @@ func InitByURL(adminServerURL string) error {
initSecretStore()
// init project manager based on deploy mode
initProjectManager()
if err := initProjectManager(); err != nil {
log.Errorf("Failed to initialise project manager, error: %v", err)
return err
}
return nil
}
@ -115,20 +119,28 @@ func initSecretStore() {
SecretStore = secret.NewStore(m)
}
func initProjectManager() {
func initProjectManager() error {
var driver pmsdriver.PMSDriver
if WithAdmiral() {
// integration with admiral
log.Info("initializing the project manager based on PMS...")
// TODO read ca/cert file and pass it to the TLS config
log.Debugf("Initialising Admiral client with certificate: %s", defaultCACertPath)
content, err := ioutil.ReadFile(defaultCACertPath)
if err != nil {
return err
}
pool := x509.NewCertPool()
if ok := pool.AppendCertsFromPEM(content); !ok {
return fmt.Errorf("failed to append cert content into cert pool")
}
AdmiralClient = &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
RootCAs: pool,
},
},
}
// integration with admiral
log.Info("initializing the project manager based on PMS...")
path := os.Getenv("SERVICE_TOKEN_FILE_PATH")
if len(path) == 0 {
path = defaultTokenFilePath
@ -144,6 +156,7 @@ func initProjectManager() {
driver = local.NewDriver()
}
GlobalProjectMgr = promgr.NewDefaultProjectManager(driver, true)
return nil
}

View File

@ -15,6 +15,8 @@ package config
import (
"os"
"path"
"runtime"
"testing"
"github.com/stretchr/testify/assert"
@ -24,7 +26,12 @@ import (
// test functions under package ui/config
func TestConfig(t *testing.T) {
server, err := test.NewAdminserver(nil)
defaultCACertPath = path.Join(currPath(), "test", "ca.crt")
c := map[string]interface{}{
common.AdmiralEndpoint: "http://www.vmware.com",
}
server, err := test.NewAdminserver(c)
if err != nil {
t.Fatalf("failed to create a mock admin server: %v", err)
}
@ -190,3 +197,11 @@ func TestConfig(t *testing.T) {
assert.Equal("http://myui:8888/service/token", InternalTokenServiceEndpoint())
}
func currPath() string {
_, f, _, ok := runtime.Caller(0)
if !ok {
panic("Failed to get current directory")
}
return path.Dir(f)
}

18
src/ui/config/test/ca.crt Normal file
View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC7TCCAdWgAwIBAgIJAKmFRnILlp3XMA0GCSqGSIb3DQEBCwUAMA0xCzAJBgNV
BAMMAmNhMB4XDTE3MDkyNDA3MDA1M1oXDTI3MDkyMjA3MDA1M1owDTELMAkGA1UE
AwwCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr4+HxXkY81j1p
5OD3htFkbJI+XulBgc7ja5YorU323VB7JfNBnau3rDZS8NdyvkLLEQT4rKw5Dd4p
phlmdKsmIq9ej1OlDjWnCOGr+HG2jG5POgPYRCf5WgCGoQ4eUIA+IXcVroG8f1YM
LDzZEBKlEP80W0zyh0ma/BYN8HG4Ica4q/iIjffJc7ob/tWFGt2HobI9wbTSyBgR
s7JSs6MBIISXGAuOE3cs7vJNzKtWhQSBw4j8FFUZSYCyONFYfOg2OtZG6z1XhpTC
rfVMm6cEsYla/mf9bJB2AqtRiUdUZwAOWQbalWPFKEO73Bj4/5sVNHKFCd/S6J1z
LHaWM0W7AgMBAAGjUDBOMB0GA1UdDgQWBBR0jFgTuL9K2iWE0wzU7r4RZT0k+zAf
BgNVHSMEGDAWgBR0jFgTuL9K2iWE0wzU7r4RZT0k+zAMBgNVHRMEBTADAQH/MA0G
CSqGSIb3DQEBCwUAA4IBAQCemrfEKHPe5ahb2III89+iuIDmbPgVESXqnf88UUdS
Iv+htE8hu9CkSemsErXcC0kUbPSM0vWN9IbHINq78cXucVyi+YTzaKJ8zsK01/zf
x0xYeK5bffYTQzs+BopTCwVqd9zHSs9a2zPnsBVHXCn25j30anQgQH9ODsspXZ3i
WUAkEOmZDnNuX7tGDesA+7h8BPcZ8zrz94kxsrdneMXuHdT1iHxS/hTxTEUUhOMF
FntwT6zx3fGL4cNG06d+pdjjp+CuUR+8GRxeASbYBWhXeiY1ykipiptxkp1zhZ3x
SNandCCdeMRntnNs/+xvRhsEGbhyrvzg2WFL2NrqiKtg
-----END CERTIFICATE-----

View File

@ -42,6 +42,7 @@ func filter(req *http.Request, resp http.ResponseWriter) {
}
if matchRepoTagDelete(req) {
resp.WriteHeader(http.StatusServiceUnavailable)
resp.Write([]byte("The system is in read only mode. Any modification is not prohibited."))
}
}

View File

@ -195,8 +195,10 @@ func TestCopyResp(t *testing.T) {
func TestMarshalError(t *testing.T) {
assert := assert.New(t)
js := marshalError("Not Found")
assert.Equal("{\"errors\":[{\"code\":\"PROJECT_POLICY_VIOLATION\",\"message\":\"Not Found\",\"detail\":\"Not Found\"}]}", js)
js1 := marshalError("PROJECT_POLICY_VIOLATION", "Not Found")
assert.Equal("{\"errors\":[{\"code\":\"PROJECT_POLICY_VIOLATION\",\"message\":\"Not Found\",\"detail\":\"Not Found\"}]}", js1)
js2 := marshalError("DENIED", "The action is denied")
assert.Equal("{\"errors\":[{\"code\":\"DENIED\",\"message\":\"The action is denied\",\"detail\":\"The action is denied\"}]}", js2)
}
func TestIsDigest(t *testing.T) {

View File

@ -35,7 +35,7 @@ const (
var rec *httptest.ResponseRecorder
// NotaryEndpoint , exported for testing.
var NotaryEndpoint =""
var NotaryEndpoint = ""
// MatchPullManifest checks if the request looks like a request to pull manifest. If it is returns the image and tag/sha256 digest as 2nd and 3rd return values
func MatchPullManifest(req *http.Request) (bool, string, string) {
@ -123,20 +123,20 @@ func (uh urlHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if flag {
components := strings.SplitN(repository, "/", 2)
if len(components) < 2 {
http.Error(rw, marshalError(fmt.Sprintf("Bad repository name: %s", repository)), http.StatusBadRequest)
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", fmt.Sprintf("Bad repository name: %s", repository)), http.StatusBadRequest)
return
}
client, err := uiutils.NewRepositoryClientForUI(tokenUsername, repository)
if err != nil {
log.Errorf("Error creating repository Client: %v", err)
http.Error(rw, marshalError(fmt.Sprintf("Failed due to internal Error: %v", err)), http.StatusInternalServerError)
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", fmt.Sprintf("Failed due to internal Error: %v", err)), http.StatusInternalServerError)
return
}
digest, _, err := client.ManifestExist(reference)
if err != nil {
log.Errorf("Failed to get digest for reference: %s, error: %v", reference, err)
http.Error(rw, marshalError(fmt.Sprintf("Failed due to internal Error: %v", err)), http.StatusInternalServerError)
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", fmt.Sprintf("Failed due to internal Error: %v", err)), http.StatusInternalServerError)
return
}
@ -161,7 +161,7 @@ type readonlyHandler struct {
func (rh readonlyHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if config.ReadOnly() {
if req.Method == http.MethodDelete || req.Method == http.MethodPost || req.Method == http.MethodPatch {
http.Error(rw, "Upload/Delete is prohibited in read only mode.", http.StatusServiceUnavailable)
http.Error(rw, marshalError("DENIED", "The system is in read only mode. Any modification is not prohibited."), http.StatusForbidden)
return
}
}
@ -241,12 +241,12 @@ func (cth contentTrustHandler) ServeHTTP(rw http.ResponseWriter, req *http.Reque
}
match, err := matchNotaryDigest(img)
if err != nil {
http.Error(rw, marshalError("Failed in communication with Notary please check the log"), http.StatusInternalServerError)
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", "Failed in communication with Notary please check the log"), http.StatusInternalServerError)
return
}
if !match {
log.Debugf("digest mismatch, failing the response.")
http.Error(rw, marshalError("The image is not signed in Notary."), http.StatusPreconditionFailed)
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", "The image is not signed in Notary."), http.StatusPreconditionFailed)
return
}
cth.next.ServeHTTP(rw, req)
@ -275,19 +275,19 @@ func (vh vulnerableHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request)
overview, err := dao.GetImgScanOverview(img.digest)
if err != nil {
log.Errorf("failed to get ImgScanOverview with repo: %s, reference: %s, digest: %s. Error: %v", img.repository, img.reference, img.digest, err)
http.Error(rw, marshalError("Failed to get ImgScanOverview."), http.StatusPreconditionFailed)
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", "Failed to get ImgScanOverview."), http.StatusPreconditionFailed)
return
}
// severity is 0 means that the image fails to scan or not scanned successfully.
if overview == nil || overview.Sev == 0 {
log.Debugf("cannot get the image scan overview info, failing the response.")
http.Error(rw, marshalError("Cannot get the image severity."), http.StatusPreconditionFailed)
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", "Cannot get the image severity."), http.StatusPreconditionFailed)
return
}
imageSev := overview.Sev
if imageSev >= int(projectVulnerableSeverity) {
log.Debugf("the image severity: %q is higher then project setting: %q, failing the response.", models.Severity(imageSev), projectVulnerableSeverity)
http.Error(rw, marshalError(fmt.Sprintf("The severity of vulnerability of the image: %q is equal or higher than the threshold in project setting: %q.", models.Severity(imageSev), projectVulnerableSeverity)), http.StatusPreconditionFailed)
http.Error(rw, marshalError("PROJECT_POLICY_VIOLATION", fmt.Sprintf("The severity of vulnerability of the image: %q is equal or higher than the threshold in project setting: %q.", models.Severity(imageSev), projectVulnerableSeverity)), http.StatusPreconditionFailed)
return
}
vh.next.ServeHTTP(rw, req)
@ -340,12 +340,12 @@ func copyResp(rec *httptest.ResponseRecorder, rw http.ResponseWriter) {
rw.Write(rec.Body.Bytes())
}
func marshalError(msg string) string {
func marshalError(code, msg string) string {
var tmpErrs struct {
Errors []JSONError `json:"errors,omitempty"`
}
tmpErrs.Errors = append(tmpErrs.Errors, JSONError{
Code: "PROJECT_POLICY_VIOLATION",
Code: code,
Message: msg,
Detail: msg,
})

View File

@ -5,6 +5,7 @@
from __future__ import print_function
import argparse
import os
import sys
import utils
import importlib
import glob
@ -24,9 +25,12 @@ def main():
input_version = utils.get_conf_version(args.input_path)
curr_dir = os.path.dirname(__file__)
chain = []
if input_version == target_version:
print ("Version of input harbor.cfg is identical to target %s, no need to upgrade" % input_version)
sys.exit(0)
if not search(curr_dir, input_version, target_version, chain):
print ("No migrator for version: %s" % input_version)
os.exit(1)
sys.exit(1)
else:
print ("input version: %s, migrator chain: %s" % (input_version, chain))
curr_input_path = args.input_path

View File

@ -26,12 +26,12 @@ default = {
def migrate(input_cfg, output_cfg):
d = utils.read_conf(input_cfg)
keys = default.keys()
keys = list(default.keys())
keys.extend(['hostname', 'ui_url_protocol', 'max_job_workers', 'customize_crt',
'ssl_cert', 'ssl_cert_key', 'secretkey_path', 'admiral_url', 'db_password', 'clair_db_password'])
val = {}
for k in keys:
if d.has_key(k):
if k in d:
val[k] = d[k]
else:
val[k] = default[k]

View File

@ -29,13 +29,13 @@ def read_conf(path):
def get_conf_version(path):
d = read_conf(path)
# print json.dumps(d,indent=4)
if d.has_key("_version"): # >=1.5.0
if "_version" in d: # >=1.5.0
return d["_version"]
if not d.has_key("clair_db_password"):
if not "clair_db_password" in d:
return "unsupported"
if d.has_key("registry_storage_provider_name"):
if "registry_storage_provider_name" in d:
return "1.4.0"
if d.has_key("uaa_endpoint"):
if "uaa_endpoint" in d:
return "1.3.0"
return "1.2.0"