Merge branch 'master' of https://github.com/kubesphere/kubesphere into readme
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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(®istries.ImageDetails{Status: registries.StatusFailed, Message: err.Error()})
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
73
pkg/models/registries/image_test.go
Normal file
73
pkg/models/registries/image_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user