Add UT cases for the code of supporting chart repo server

add UT cases for cache
add UT cases for ChartOperator
add UT cases for the main API service of chart server
export some consts in the security.go of filter package to let them visible in chartserver package
This commit is contained in:
Steven Zou 2018-07-10 22:28:22 +08:00
parent 44cbb29664
commit da4359f56b
9 changed files with 745 additions and 18 deletions

File diff suppressed because one or more lines are too long

View File

@ -160,7 +160,7 @@ func initCacheDriver(driverType string) (beego_cache.Cache, string) {
break
}
hlog.Info("Driver type %s is not suppotred, enable memory cache by default for chart caching")
hlog.Infof("Failed to config cache with driver '%s', enable memory cache by default for chart cache instead", driverType)
//Any other cases, use memory cache
return beego_cache.NewMemoryCache(), cacheDriverMem
}

View File

@ -0,0 +1,90 @@
package chartserver
import (
"os"
"testing"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/chart"
helm_repo "k8s.io/helm/pkg/repo"
)
var (
mockChart = &ChartVersionDetails{
Metadata: &helm_repo.ChartVersion{
Metadata: &chart.Metadata{
Name: "mock_chart",
Version: "0.1.0",
},
Digest: "mock_digest",
},
Dependencies: make([]*chartutil.Dependency, 0),
}
)
//Test the no cache set scenario
func TestNoCache(t *testing.T) {
chartCache := NewChartCache()
if chartCache == nil {
t.Fatalf("cache instance should not be nil")
}
if chartCache.IsEnabled() {
t.Fatal("chart cache should not be enabled")
}
}
//Test the in memory cache
func TestInMemoryCache(t *testing.T) {
os.Setenv(cacheDriverENVKey, cacheDriverMem)
chartCache := NewChartCache()
if chartCache == nil {
t.Fatalf("cache instance should not be nil")
}
if !chartCache.IsEnabled() {
t.Fatal("chart cache should be enabled")
}
if chartCache.driverType != cacheDriverMem {
t.Fatalf("expect driver type %s but got %s", cacheDriverMem, chartCache.driverType)
}
chartCache.PutChart(mockChart)
theCachedChart := chartCache.GetChart(mockChart.Metadata.Digest)
if theCachedChart == nil || theCachedChart.Metadata.Name != mockChart.Metadata.Name {
t.Fatal("In memory cache does work")
}
os.Unsetenv(cacheDriverENVKey)
}
//Test redis cache
//Failed to config redis cache and then use in memory instead
func TestRedisCache(t *testing.T) {
os.Setenv(cacheDriverENVKey, cacheDriverRedis)
os.Setenv(redisENVKey, ":6379")
chartCache := NewChartCache()
if chartCache == nil {
t.Fatalf("cache instance should not be nil")
}
if !chartCache.IsEnabled() {
t.Fatal("chart cache should be enabled")
}
if chartCache.driverType != cacheDriverRedis {
t.Fatalf("expect driver type '%s' but got '%s'", cacheDriverRedis, chartCache.driverType)
}
chartCache.PutChart(mockChart)
theCachedChart := chartCache.GetChart(mockChart.Metadata.Digest)
if theCachedChart == nil || theCachedChart.Metadata.Name != mockChart.Metadata.Name {
t.Fatal("In memory cache does work")
}
os.Unsetenv(redisENVKey)
os.Unsetenv(cacheDriverENVKey)
}

View File

@ -0,0 +1,49 @@
package chartserver
import (
"testing"
)
func TestGetChartDetails(t *testing.T) {
chartOpr := ChartOperator{}
chartDetails, err := chartOpr.GetChartDetails(helmChartContent)
if err != nil {
t.Fatal(err)
}
if len(chartDetails.Dependencies) == 0 {
t.Fatal("At least 1 dependency exitsing, but we got 0 now")
}
if len(chartDetails.Values) == 0 {
t.Fatal("At least 1 value existing, but we got 0 now")
}
if chartDetails.Values["adminserver.adminPassword"] != "Harbor12345" {
t.Fatalf("The value of 'adminserver.adminPassword' should be 'Harbor12345' but we got '%s' now", chartDetails.Values["adminserver.adminPassword"])
}
}
func TestGetChartList(t *testing.T) {
chartOpr := ChartOperator{}
infos, err := chartOpr.GetChartList(chartListContent)
if err != nil {
t.Fatal(err)
}
if len(infos) != 2 {
t.Fatalf("Length of chart list should be 2, but we got %d now", len(infos))
}
foundHarbor := false
for _, chart := range infos {
if chart.Name == "harbor" {
foundHarbor = true
break
}
}
if !foundHarbor {
t.Fatal("Expect chart named with 'harbor' but got nothing")
}
}

View File

@ -53,15 +53,15 @@ func (cc *ChartClient) GetContent(url string) ([]byte, error) {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to retrieve content from url %s", url)
}
content, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to retrieve content from url '%s' with error: %s", url, content)
}
return content, nil
}

View File

@ -0,0 +1,210 @@
package chartserver
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
"github.com/ghodss/yaml"
helm_repo "k8s.io/helm/pkg/repo"
)
//Prepare, start the mock servers
func TestStartServers(t *testing.T) {
if err := startMockServers(); err != nil {
t.Fatal(err)
}
}
//Test /health
func TestGetHealthOfBaseHandler(t *testing.T) {
content, err := httpClient.GetContent(fmt.Sprintf("%s/health", getTheAddrOfFrontServer()))
if err != nil {
t.Fatal(err)
}
status := make(map[string]interface{})
if err := json.Unmarshal(content, &status); err != nil {
t.Fatalf("Unmarshal error: %s, %s", err, content)
}
healthy, ok := status["health"].(bool)
if !ok || !healthy {
t.Fatalf("Expect healthy of server to be 'true' but got %v", status["health"])
}
}
//Get /repo1/index.yaml
func TestGetIndexYamlByRepo(t *testing.T) {
indexFile, err := getIndexYaml("/repo1/index.yaml")
if err != nil {
t.Fatal(err)
}
if len(indexFile.Entries) != 3 {
t.Fatalf("Expect index file with 3 entries, but got %d", len(indexFile.Entries))
}
}
//Test get /index.yaml
func TestGetUnifiedYamlFile(t *testing.T) {
indexFile, err := getIndexYaml("/index.yaml")
if err != nil {
t.Fatal(err)
}
if len(indexFile.Entries) != 5 {
t.Fatalf("Expect index file with 5 entries, but got %d", len(indexFile.Entries))
}
_, ok := indexFile.Entries["repo1/harbor"]
if !ok {
t.Fatal("Expect chart entry 'repo1/harbor' but got nothing")
}
_, ok = indexFile.Entries["repo2/harbor"]
if !ok {
t.Fatal("Expect chart entry 'repo2/harbor' but got nothing")
}
}
//Test download /:repo/charts/chart.tar
//Use this case to test the proxy function
func TestDownloadChart(t *testing.T) {
content, err := httpClient.GetContent(fmt.Sprintf("%s/repo1/charts/harbor-0.2.0.tgz", getTheAddrOfFrontServer()))
if err != nil {
t.Fatal(err)
}
gotSize := len(content)
expectSize := len(helmChartContent)
if gotSize != expectSize {
t.Fatalf("Expect %d bytes data but got %d bytes", expectSize, gotSize)
}
}
//Test get /api/:repo/charts
func TestRetrieveChartList(t *testing.T) {
content, err := httpClient.GetContent(fmt.Sprintf("%s/api/repo1/charts", getTheAddrOfFrontServer()))
if err != nil {
t.Fatal(err)
}
chartList := make([]*ChartInfo, 0)
err = json.Unmarshal(content, &chartList)
if err != nil {
t.Fatalf("Unmarshal error: %s", err)
}
if len(chartList) != 2 {
t.Fatalf("Expect to get 2 charts in the list but got %d", len(chartList))
}
foundItem := false
for _, chartInfo := range chartList {
if chartInfo.Name == "hello-helm" && chartInfo.TotalVersions == 2 {
foundItem = true
break
}
}
if !foundItem {
t.Fatalf("Expect chart 'hello-helm' with 2 versions but got nothing")
}
}
//Test get /api/:repo/charts/:chart_name/:version
func TestGetChartVersion(t *testing.T) {
content, err := httpClient.GetContent(fmt.Sprintf("%s/api/repo1/charts/harbor/0.2.0", getTheAddrOfFrontServer()))
if err != nil {
t.Fatal(err)
}
chartVersion := &ChartVersionDetails{}
if err = json.Unmarshal(content, chartVersion); err != nil {
t.Fatalf("Unmarshal error: %s", err)
}
if chartVersion.Metadata.Name != "harbor" {
t.Fatalf("Expect harbor chart version but got %s", chartVersion.Metadata.Name)
}
if chartVersion.Metadata.Version != "0.2.0" {
t.Fatalf("Expect version '0.2.0' but got version %s", chartVersion.Metadata.Version)
}
if len(chartVersion.Dependencies) != 1 {
t.Fatalf("Expect 1 dependency but got %d ones", len(chartVersion.Dependencies))
}
if len(chartVersion.Values) != 99 {
t.Fatalf("Expect 99 k-v values but got %d", len(chartVersion.Values))
}
}
//Test get /api/:repo/charts/:chart_name/:version with none-existing version
func TestGetChartVersionWithError(t *testing.T) {
_, err := httpClient.GetContent(fmt.Sprintf("%s/api/repo1/charts/harbor/1.0.0", getTheAddrOfFrontServer()))
if err == nil {
t.Fatal("Expect an error but got nil")
}
}
//Get /api/repo1/charts/harbor
//401 will be rewritten to 500 with specified error
func TestResponseRewrite(t *testing.T) {
response, err := http.Get(fmt.Sprintf("%s/api/repo1/charts/harbor", getTheAddrOfFrontServer()))
if err != nil {
t.Fatal(err)
}
if response.StatusCode != http.StatusInternalServerError {
t.Fatalf("Expect status code 500 but got %d", response.StatusCode)
}
bytes, err := ioutil.ReadAll(response.Body)
if err != nil {
t.Fatalf("Read bytes from http response failed with error: %s", err)
}
defer response.Body.Close()
errObj := make(map[string]interface{})
if err = json.Unmarshal(bytes, &errObj); err != nil {
t.Fatalf("Unmarshal error: %s", err)
}
if msg, ok := errObj["error"]; !ok {
t.Fatal("Expect an error message from server but got nothing")
} else {
if !strings.Contains(msg.(string), "operation request from unauthentic source is rejected") {
t.Fatal("Missing the required error message")
}
}
}
//Clear environments
func TestStopServers(t *testing.T) {
stopMockServers()
}
//Utility method for getting index yaml file
func getIndexYaml(path string) (*helm_repo.IndexFile, error) {
content, err := httpClient.GetContent(fmt.Sprintf("%s%s", getTheAddrOfFrontServer(), path))
if err != nil {
return nil, err
}
indexFile := &helm_repo.IndexFile{}
if err := yaml.Unmarshal(content, indexFile); err != nil {
return nil, fmt.Errorf("Unmarshal error: %s", err)
}
if indexFile == nil {
return nil, fmt.Errorf("Got nil index yaml file")
}
return indexFile, nil
}

View File

@ -79,7 +79,7 @@ func (mh *ManipulationHandler) GetChartVersion(w http.ResponseWriter, req *http.
//NOT hit!!
//TODO:
namespace := "repo2"
namespace := "repo1"
content, err := mh.getChartVersionContent(namespace, chartV.URLs[0])
if err != nil {
writeInternalError(w, err)

View File

@ -36,7 +36,8 @@ import (
"github.com/vmware/harbor/src/ui/promgr/pmsdriver/admiral"
)
type key string
//ContextValueKey for content value
type ContextValueKey string
type pathMethod struct {
path string
@ -44,8 +45,11 @@ type pathMethod struct {
}
const (
securCtxKey key = "harbor_security_context"
pmKey key = "harbor_project_manager"
//SecurCtxKey is context value key for security context
SecurCtxKey ContextValueKey = "harbor_security_context"
//PmKey is context value key for the project manager
PmKey ContextValueKey = "harbor_project_manager"
)
var (
@ -307,8 +311,8 @@ func (u *unauthorizedReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
}
func setSecurCtxAndPM(req *http.Request, ctx security.Context, pm promgr.ProjectManager) {
addToReqContext(req, securCtxKey, ctx)
addToReqContext(req, pmKey, pm)
addToReqContext(req, SecurCtxKey, ctx)
addToReqContext(req, PmKey, pm)
}
func addToReqContext(req *http.Request, key, value interface{}) {
@ -321,7 +325,7 @@ func GetSecurityContext(req *http.Request) (security.Context, error) {
return nil, fmt.Errorf("request is nil")
}
ctx := req.Context().Value(securCtxKey)
ctx := req.Context().Value(SecurCtxKey)
if ctx == nil {
return nil, fmt.Errorf("the security context got from request is nil")
}
@ -340,7 +344,7 @@ func GetProjectManager(req *http.Request) (promgr.ProjectManager, error) {
return nil, fmt.Errorf("request is nil")
}
pm := req.Context().Value(pmKey)
pm := req.Context().Value(PmKey)
if pm == nil {
return nil, fmt.Errorf("the project manager got from request is nil")
}

View File

@ -292,7 +292,7 @@ func projectManager(ctx *beegoctx.Context) interface{} {
if ctx.Request == nil {
return nil
}
return ctx.Request.Context().Value(pmKey)
return ctx.Request.Context().Value(PmKey)
}
func TestGetSecurityContext(t *testing.T) {
@ -310,7 +310,7 @@ func TestGetSecurityContext(t *testing.T) {
req, err = http.NewRequest("", "", nil)
assert.Nil(t, err)
req = req.WithContext(context.WithValue(req.Context(),
securCtxKey, "test"))
SecurCtxKey, "test"))
ctx, err = GetSecurityContext(req)
assert.NotNil(t, err)
@ -318,7 +318,7 @@ func TestGetSecurityContext(t *testing.T) {
req, err = http.NewRequest("", "", nil)
assert.Nil(t, err)
req = req.WithContext(context.WithValue(req.Context(),
securCtxKey, local.NewSecurityContext(nil, nil)))
SecurCtxKey, local.NewSecurityContext(nil, nil)))
ctx, err = GetSecurityContext(req)
assert.Nil(t, err)
_, ok := ctx.(security.Context)
@ -340,7 +340,7 @@ func TestGetProjectManager(t *testing.T) {
req, err = http.NewRequest("", "", nil)
assert.Nil(t, err)
req = req.WithContext(context.WithValue(req.Context(),
pmKey, "test"))
PmKey, "test"))
pm, err = GetProjectManager(req)
assert.NotNil(t, err)
@ -348,7 +348,7 @@ func TestGetProjectManager(t *testing.T) {
req, err = http.NewRequest("", "", nil)
assert.Nil(t, err)
req = req.WithContext(context.WithValue(req.Context(),
pmKey, promgr.NewDefaultProjectManager(driver_local.NewDriver(), true)))
PmKey, promgr.NewDefaultProjectManager(driver_local.NewDriver(), true)))
pm, err = GetProjectManager(req)
assert.Nil(t, err)
_, ok := pm.(promgr.ProjectManager)