Merge branch 'master' of https://github.com/kubesphere/kubesphere into readme

This commit is contained in:
周鹏飞@yunify
2020-02-03 12:59:29 +08:00
11 changed files with 211 additions and 23 deletions

View File

@@ -9,8 +9,7 @@ git:
depth: false
go:
- "1.12.x"
- "tip"
- "1.13.x"
env:
- GO111MODULE=on
cache:
@@ -27,7 +26,7 @@ script:
- make all
before_install:
- go get -t -v ./...
- go mod vendor
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@@ -21,6 +21,7 @@ package registries
import (
"github.com/emicklei/go-restful"
"net/http"
"strings"
"kubesphere.io/kubesphere/pkg/models/registries"
"kubesphere.io/kubesphere/pkg/server/errors"
@@ -68,6 +69,25 @@ func RegistryImageBlob(request *restful.Request, response *restful.Response) {
return
}
// default use ssl
checkSSl := func(serverAddress string) bool {
if strings.HasPrefix(serverAddress, "http://") {
return false
} else {
return true
}
}
if strings.HasPrefix(imageName, "http") {
dockerurl, err := registries.ParseDockerURL(imageName)
if err != nil {
log.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), response)
return
}
imageName = dockerurl.StringWithoutScheme()
}
// parse image
image, err := registries.ParseImage(imageName)
if err != nil {
@@ -76,8 +96,10 @@ func RegistryImageBlob(request *restful.Request, response *restful.Response) {
return
}
useSSL := checkSSl(entry.ServerAddress)
// Create the registry client.
r, err := registries.CreateRegistryClient(entry.Username, entry.Password, image.Domain)
r, err := registries.CreateRegistryClient(entry.Username, entry.Password, image.Domain, useSSL)
if err != nil {
log.Errorf("%+v", err)
response.WriteAsJson(&registries.ImageDetails{Status: registries.StatusFailed, Message: err.Error()})

View File

@@ -20,8 +20,10 @@ package resources
import (
"github.com/emicklei/go-restful"
"k8s.io/api/core/v1"
"k8s.io/klog"
"net/http"
storagev1 "k8s.io/api/storage/v1"
"kubesphere.io/kubesphere/pkg/models/storage"
"kubesphere.io/kubesphere/pkg/server/errors"
)
@@ -69,3 +71,26 @@ func GetPvcListBySc(request *restful.Request, response *restful.Response) {
response.WriteAsJson(result)
}
func PatchStorageClass(request *restful.Request, response *restful.Response) {
scObj := &storagev1.StorageClass{}
err := request.ReadEntity(scObj)
if err != nil {
klog.Errorf("read entity error: %s", err.Error())
response.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
klog.V(5).Infof("succeed to read entity %v", scObj)
scName := request.PathParameter("storageclass")
if scObj.Annotations[storage.IsDefaultStorageClassAnnotation] == "true" {
// Set default storage class
sc, err := storage.SetDefaultStorageClass(scName)
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
response.WriteEntity(sc)
return
}
response.WriteEntity(errors.None)
}

View File

@@ -22,6 +22,7 @@ import (
"github.com/emicklei/go-restful-openapi"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
storagev1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/components"
"kubesphere.io/kubesphere/pkg/apiserver/git"
@@ -257,7 +258,14 @@ func addWebService(c *restful.Container) error {
Returns(http.StatusOK, ok, status.WorkLoadStatus{}).
To(workloadstatuses.GetNamespacedAbnormalWorkloads))
c.Add(webservice)
webservice.Route(webservice.PATCH("/storageclasses/{storageclass}").
To(resources.PatchStorageClass).
Doc("patch storage class").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterResourcesTag}).
Returns(http.StatusOK, ok, storagev1.StorageClass{}).
Writes(storagev1.StorageClass{}).
Param(webservice.PathParameter("storageclass", "the name of storage class")))
c.Add(webservice)
return nil
}

View File

@@ -2,9 +2,11 @@ package registries
import (
"fmt"
"github.com/docker/distribution/reference"
digest "github.com/opencontainers/go-digest"
"github.com/opencontainers/go-digest"
log "k8s.io/klog"
"net/url"
"strings"
)
// Image holds information about an image.
@@ -30,9 +32,28 @@ func (i *Image) Reference() string {
return i.Tag
}
type DockerURL struct {
*url.URL
}
func (u *DockerURL) StringWithoutScheme() string {
u.Scheme = ""
s := u.String()
return strings.Trim(s, "//")
}
func ParseDockerURL(rawurl string) (*DockerURL, error) {
url, err := url.Parse(rawurl)
if err != nil {
log.Errorf("%+v", err)
return nil, err
}
return &DockerURL{URL: url}, nil
}
// ParseImage returns an Image struct with all the values filled in for a given image.
// example : localhost:5000/nginx:latest, nginx:perl etc.
func ParseImage(image string) (Image, error) {
func ParseImage(image string) (i Image, err error) {
// Parse the image name and tag.
named, err := reference.ParseNormalizedNamed(image)
if err != nil {
@@ -41,7 +62,7 @@ func ParseImage(image string) (Image, error) {
// Add the latest lag if they did not provide one.
named = reference.TagNameOnly(named)
i := Image{
i = Image{
named: named,
Domain: reference.Domain(named),
Path: reference.Path(named),

View File

@@ -0,0 +1,73 @@
package registries
import (
"testing"
)
func TestParseImage(t *testing.T) {
type testImage struct {
inputImageName string
ExImage Image
}
testImages := []testImage{
{inputImageName: "dockerhub.qingcloud.com/kubesphere/test:v1", ExImage: Image{Domain: "dockerhub.qingcloud.com", Tag: "v1", Path: "kubesphere/test"}},
{inputImageName: "harbor.devops.kubesphere.local:30280/library/tomcat:latest", ExImage: Image{Domain: "harbor.devops.kubesphere.local:30280", Tag: "latest", Path: "library/tomcat"}},
{inputImageName: "zhuxiaoyang/nginx:v1", ExImage: Image{Domain: "docker.io", Tag: "v1", Path: "zhuxiaoyang/nginx"}},
{inputImageName: "nginx", ExImage: Image{Domain: "docker.io", Tag: "latest", Path: "library/nginx"}},
{inputImageName: "nginx:latest", ExImage: Image{Domain: "docker.io", Tag: "latest", Path: "library/nginx"}},
{inputImageName: "kubesphere/ks-account:v2.1.0", ExImage: Image{Domain: "docker.io", Tag: "v2.1.0", Path: "kubesphere/ks-account"}},
{inputImageName: "http://docker.io/nginx:latest", ExImage: Image{}},
{inputImageName: "https://harbor.devops.kubesphere.local:30280/library/tomcat:latest", ExImage: Image{}},
{inputImageName: "docker.io/nginx:latest:latest", ExImage: Image{}},
{inputImageName: "nginx:8000:latest", ExImage: Image{}},
}
for _, image := range testImages {
res, err := ParseImage(image.inputImageName)
if err != nil {
if res != image.ExImage {
t.Fatalf("Get err %s", err)
}
}
if res.Domain != image.ExImage.Domain {
t.Fatalf("Doamin got %v, expected %v", res.Domain, image.ExImage.Domain)
}
if res.Tag != image.ExImage.Tag {
t.Fatalf("Tag got %v, expected %v", res.Tag, image.ExImage.Tag)
}
if res.Path != image.ExImage.Path {
t.Fatalf("Path got %v, expected %v", res.Path, image.ExImage.Path)
}
}
}
func TestStringWithoutScheme(t *testing.T) {
type testRawUrl struct {
Rawurl string
ExUrl string
}
testRawurls := []testRawUrl{
{"http://dockerhub.qingcloud.com/kubesphere/nginx:v1", "dockerhub.qingcloud.com/kubesphere/nginx:v1"},
{"https://dockerhub.qingcloud.com/kubesphere/nginx:v1", "dockerhub.qingcloud.com/kubesphere/nginx:v1"},
{"http://harbor.devops.kubesphere.local:30280/library/tomcat:latest", "harbor.devops.kubesphere.local:30280/library/tomcat:latest"},
{"https://harbor.devops.kubesphere.local:30280/library/tomcat:latest", "harbor.devops.kubesphere.local:30280/library/tomcat:latest"},
}
for _, rawurl := range testRawurls {
dockerurl, err := ParseDockerURL(rawurl.Rawurl)
if err != nil {
t.Fatalf("Get err %s", err)
}
imageName := dockerurl.StringWithoutScheme()
if imageName != rawurl.ExUrl {
t.Fatalf("imagename got %v, expected %v", imageName, rawurl.ExUrl)
}
}
}

View File

@@ -7,7 +7,7 @@ import (
func TestDigestFromDockerHub(t *testing.T) {
testImage := Image{Domain: "docker.io", Path: "library/alpine", Tag: "latest"}
r, err := CreateRegistryClient("", "", "docker.io")
r, err := CreateRegistryClient("", "", "docker.io", true)
if err != nil {
t.Fatalf("Could not get client: %s", err)
}

View File

@@ -64,7 +64,7 @@ type authService struct {
Scope []string
}
func CreateRegistryClient(username, password, domain string) (*Registry, error) {
func CreateRegistryClient(username, password, domain string, useSSL bool) (*Registry, error) {
authDomain := domain
auth, err := GetAuthConfig(username, password, authDomain)
if err != nil {
@@ -75,6 +75,7 @@ func CreateRegistryClient(username, password, domain string) (*Registry, error)
// Create the registry client.
return New(auth, RegistryOpt{
Domain: domain,
UseSSL: useSSL,
})
}

View File

@@ -4,6 +4,8 @@ import (
"testing"
)
const DockerHub = "docker.io"
func TestCreateRegistryClient(t *testing.T) {
type imageInfo struct {
Username string
@@ -11,17 +13,19 @@ func TestCreateRegistryClient(t *testing.T) {
Domain string
ExDomain string
ExUrl string
UseSSL bool
}
testImages := []imageInfo{
{Domain: "kubesphere.io", ExDomain: "kubesphere.io", ExUrl: "http://kubesphere.io"},
{Domain: "127.0.0.1:5000", ExDomain: "127.0.0.1:5000", ExUrl: "http://127.0.0.1:5000"},
{Username: "Username", Password: "Password", Domain: "docker.io", ExDomain: "registry-1.docker.io", ExUrl: "https://registry-1.docker.io"},
{Domain: "harbor.devops.kubesphere.local:30280", ExDomain: "harbor.devops.kubesphere.local:30280", ExUrl: "http://harbor.devops.kubesphere.local:30280"},
{Domain: "kubesphere.io", ExDomain: "kubesphere.io", ExUrl: "https://kubesphere.io", UseSSL: true},
{Domain: "127.0.0.1:5000", ExDomain: "127.0.0.1:5000", ExUrl: "http://127.0.0.1:5000", UseSSL: false},
{Username: "Username", Password: "Password", Domain: DockerHub, ExDomain: "registry-1.docker.io", ExUrl: "https://registry-1.docker.io", UseSSL: true},
{Domain: "harbor.devops.kubesphere.local:30280", ExDomain: "harbor.devops.kubesphere.local:30280", ExUrl: "http://harbor.devops.kubesphere.local:30280", UseSSL: false},
{Domain: "dockerhub.qingcloud.com/zxytest/s2i-jj:jj", ExDomain: "dockerhub.qingcloud.com", ExUrl: "https://dockerhub.qingcloud.com/zxytest/s2i-jj:jj", UseSSL: true},
}
for _, testImage := range testImages {
reg, err := CreateRegistryClient(testImage.Username, testImage.Password, testImage.Domain)
reg, err := CreateRegistryClient(testImage.Username, testImage.Password, testImage.Domain, testImage.UseSSL)
if err != nil {
t.Fatalf("Get err %s", err)
}
@@ -36,8 +40,8 @@ func TestCreateRegistryClient(t *testing.T) {
}
testImage := Image{Domain: "docker.io", Path: "library/alpine", Tag: "latest"}
r, err := CreateRegistryClient("", "", "docker.io")
testImage := Image{Domain: DockerHub, Path: "library/alpine", Tag: "latest"}
r, err := CreateRegistryClient("", "", DockerHub, true)
if err != nil {
t.Fatalf("Could not get client: %s", err)
}

View File

@@ -32,7 +32,7 @@ func (asm authServiceMock) equalTo(v *authService) bool {
func TestToken(t *testing.T) {
testImage := Image{Domain: "docker.io", Path: "library/alpine", Tag: "latest"}
r, err := CreateRegistryClient("", "", "docker.io")
r, err := CreateRegistryClient("", "", "docker.io", true)
if err != nil {
t.Fatalf("Could not get registry client: %s", err)
}

View File

@@ -18,6 +18,7 @@
package storage
import (
"kubesphere.io/kubesphere/pkg/simple/client"
"strconv"
"k8s.io/api/core/v1"
@@ -27,16 +28,17 @@ import (
"kubesphere.io/kubesphere/pkg/informers"
)
const (
IsDefaultStorageClassAnnotation = "storageclass.kubernetes.io/is-default-class"
betaIsDefaultStorageClassAnnotation = "storageclass.beta.kubernetes.io/is-default-class"
)
type ScMetrics struct {
Capacity string `json:"capacity,omitempty"`
Usage string `json:"usage,omitempty"`
PvcNumber string `json:"pvcNumber"`
}
func init() {
}
func GetPvcListBySc(scName string) ([]*v1.PersistentVolumeClaim, error) {
persistentVolumeClaimLister := informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister()
all, err := persistentVolumeClaimLister.List(labels.Everything())
@@ -102,3 +104,36 @@ func GetScList() ([]*storageV1.StorageClass, error) {
return scList, nil
}
func SetDefaultStorageClass(defaultScName string) (*storageV1.StorageClass, error) {
scLister := informers.SharedInformerFactory().Storage().V1().StorageClasses().Lister()
// 1. verify storage class name
sc, err := scLister.Get(defaultScName)
if sc == nil || err != nil {
return sc, err
}
// 2. unset all default sc and then set default sc
scList, err := scLister.List(labels.Everything())
if err != nil {
return nil, err
}
k8sClient := client.ClientSets().K8s().Kubernetes()
var defaultSc *storageV1.StorageClass
for _, sc := range scList {
_, hasDefault := sc.Annotations[IsDefaultStorageClassAnnotation]
_, hasBeta := sc.Annotations[betaIsDefaultStorageClassAnnotation]
if sc.Name == defaultScName || hasDefault || hasBeta {
delete(sc.Annotations, IsDefaultStorageClassAnnotation)
delete(sc.Annotations, betaIsDefaultStorageClassAnnotation)
if sc.Name == defaultScName {
sc.Annotations[IsDefaultStorageClassAnnotation] = "true"
defaultSc = sc
}
_, err := k8sClient.StorageV1().StorageClasses().Update(sc)
if err != nil {
return nil, err
}
}
}
return defaultSc, nil
}