mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-18 03:56:36 +00:00
Feat: addon support version check (#3304)
* addon support version check add more tests Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> * add kubernetes tests Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> * add more tests Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> * reorgnize the check logic Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> * fix addon error bug in apiserver Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> fix comments fix test * add err addon bcode Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com> * fix comments Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
This commit is contained in:
@@ -31,6 +31,8 @@ const (
|
||||
DefaultAppNamespace = "default"
|
||||
// AutoDetectWorkloadDefinition defines the default workload type for ComponentDefinition which doesn't specify a workload
|
||||
AutoDetectWorkloadDefinition = "autodetects.core.oam.dev"
|
||||
// KubeVelaControllerDeployment defines the KubeVela controller's deployment name
|
||||
KubeVelaControllerDeployment = "kubevela-vela-core"
|
||||
)
|
||||
|
||||
// DefaultKubeVelaNS defines the default KubeVela namespace in Kubernetes
|
||||
|
||||
+108
-1
@@ -32,8 +32,10 @@ import (
|
||||
"cuelang.org/go/cue"
|
||||
cueyaml "cuelang.org/go/encoding/yaml"
|
||||
"github.com/google/go-github/v32/github"
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/oauth2"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -41,6 +43,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
k8syaml "k8s.io/apimachinery/pkg/runtime/serializer/yaml"
|
||||
types2 "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"k8s.io/klog/v2"
|
||||
@@ -60,6 +63,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
version2 "github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -865,10 +869,11 @@ type Installer struct {
|
||||
registryMeta map[string]SourceMeta
|
||||
args map[string]interface{}
|
||||
cache *Cache
|
||||
dc *discovery.DiscoveryClient
|
||||
}
|
||||
|
||||
// NewAddonInstaller will create an installer for addon
|
||||
func NewAddonInstaller(ctx context.Context, cli client.Client, apply apply.Applicator, config *rest.Config, r *Registry, args map[string]interface{}, cache *Cache) Installer {
|
||||
func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r *Registry, args map[string]interface{}, cache *Cache) Installer {
|
||||
return Installer{
|
||||
ctx: ctx,
|
||||
config: config,
|
||||
@@ -877,12 +882,18 @@ func NewAddonInstaller(ctx context.Context, cli client.Client, apply apply.Appli
|
||||
r: r,
|
||||
args: args,
|
||||
cache: cache,
|
||||
dc: discoveryClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Installer) enableAddon(addon *InstallPackage) error {
|
||||
var err error
|
||||
h.addon = addon
|
||||
err = checkAddonVersionMeetRequired(h.ctx, addon.SystemRequirements, h.cli, h.dc)
|
||||
if err != nil {
|
||||
return ErrVersionMismatch
|
||||
}
|
||||
|
||||
if err = h.installDependency(addon); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1104,3 +1115,99 @@ func FetchAddonRelatedApp(ctx context.Context, cli client.Client, addonName stri
|
||||
}
|
||||
return app, nil
|
||||
}
|
||||
|
||||
// checkAddonVersionMeetRequired will check the version of cli/ux and kubevela-core-controller whether meet the addon requirement, if not will return an error
|
||||
// please notice that this func is for check production environment which vela cli/ux or vela core is officalVersion
|
||||
// if version is for test or debug eg: latest/commit-id/branch-name this func will return nil error
|
||||
func checkAddonVersionMeetRequired(ctx context.Context, require *SystemRequirements, k8sClient client.Client, dc *discovery.DiscoveryClient) error {
|
||||
if require == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if not semver version, bypass check cli/ux. eg: {branch name/git commit id/UNKNOWN}
|
||||
if version2.IsOfficialKubeVelaVersion(version2.VelaVersion) {
|
||||
res, err := checkSemVer(version2.VelaVersion, require.VelaVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !res {
|
||||
return fmt.Errorf("vela cli/ux version: %s cannot meet requirement", version2.VelaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
// check vela core controller version
|
||||
imageVersion, err := fetchVelaCoreImageTag(ctx, k8sClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if not semver version, bypass check vela-core.
|
||||
if version2.IsOfficialKubeVelaVersion(imageVersion) {
|
||||
res, err := checkSemVer(imageVersion, require.VelaVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !res {
|
||||
return fmt.Errorf("the vela core controller: %s cannot meet requirement ", imageVersion)
|
||||
}
|
||||
}
|
||||
|
||||
// discovery client is nil so bypass check kubernetes version
|
||||
if dc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
k8sVersion, err := dc.ServerVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if not semver version, bypass check kubernetes version.
|
||||
if version2.IsOfficialKubeVelaVersion(k8sVersion.GitVersion) {
|
||||
res, err := checkSemVer(k8sVersion.GitVersion, require.KubernetesVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !res {
|
||||
return fmt.Errorf("the kubernetes version %s cannot meet requirement", k8sVersion.GitVersion)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkSemVer(actual string, require string) (bool, error) {
|
||||
if len(require) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
smeVer := strings.TrimPrefix(actual, "v")
|
||||
l := strings.ReplaceAll(require, "v", " ")
|
||||
constraint, err := version.NewConstraint(l)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
v, err := version.NewVersion(smeVer)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return constraint.Check(v), nil
|
||||
}
|
||||
|
||||
func fetchVelaCoreImageTag(ctx context.Context, k8sClient client.Client) (string, error) {
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := k8sClient.Get(ctx, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: types.KubeVelaControllerDeployment}, deploy); err != nil {
|
||||
return "", err
|
||||
}
|
||||
var tag string
|
||||
for _, c := range deploy.Spec.Template.Spec.Containers {
|
||||
if c.Name == types.DefaultKubeVelaReleaseName {
|
||||
l := strings.Split(c.Image, ":")
|
||||
if len(l) == 1 {
|
||||
// if tag is empty mean use latest image
|
||||
return "latest", nil
|
||||
}
|
||||
tag = l[1]
|
||||
}
|
||||
}
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
|
||||
types2 "k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
@@ -30,6 +32,8 @@ import (
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
var _ = Describe("Addon test", func() {
|
||||
@@ -176,6 +180,47 @@ var _ = Describe("Addon test", func() {
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Addon func test", func() {
|
||||
var deploy appsv1.Deployment
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(k8sClient.Delete(ctx, &deploy))
|
||||
})
|
||||
|
||||
It("fetchVelaCoreImageTag func test", func() {
|
||||
deploy = appsv1.Deployment{}
|
||||
tag, err := fetchVelaCoreImageTag(ctx, k8sClient)
|
||||
Expect(err).Should(util.NotFoundMatcher{})
|
||||
Expect(tag).Should(BeEquivalentTo(""))
|
||||
|
||||
Expect(yaml.Unmarshal([]byte(deployYaml), &deploy)).Should(BeNil())
|
||||
deploy.SetNamespace(types.DefaultKubeVelaNS)
|
||||
Expect(k8sClient.Create(ctx, &deploy)).Should(BeNil())
|
||||
|
||||
Eventually(func() error {
|
||||
tag, err := fetchVelaCoreImageTag(ctx, k8sClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tag != "v1.2.3" {
|
||||
return fmt.Errorf("tag missmatch want %s actual %s", "v1.2.3", tag)
|
||||
}
|
||||
return err
|
||||
}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
})
|
||||
|
||||
It("checkAddonVersionMeetRequired func test", func() {
|
||||
deploy = appsv1.Deployment{}
|
||||
Expect(checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=v1.2.1"}, k8sClient, dc)).Should(util.NotFoundMatcher{})
|
||||
Expect(yaml.Unmarshal([]byte(deployYaml), &deploy)).Should(BeNil())
|
||||
deploy.SetNamespace(types.DefaultKubeVelaNS)
|
||||
Expect(k8sClient.Create(ctx, &deploy)).Should(BeNil())
|
||||
|
||||
Expect(checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=v1.2.1"}, k8sClient, dc)).Should(BeNil())
|
||||
Expect(checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=v1.2.4"}, k8sClient, dc)).ShouldNot(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
const (
|
||||
appYaml = `apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
@@ -201,4 +246,56 @@ spec:
|
||||
image: crccheck/hello-world
|
||||
port: 8000
|
||||
`
|
||||
deployYaml = `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: kubevela-vela-core
|
||||
namespace: vela-system
|
||||
spec:
|
||||
progressDeadlineSeconds: 600
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 10
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: kubevela
|
||||
app.kubernetes.io/name: vela-core
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
prometheus.io/path: /metrics
|
||||
prometheus.io/port: "8080"
|
||||
prometheus.io/scrape: "true"
|
||||
labels:
|
||||
app.kubernetes.io/instance: kubevela
|
||||
app.kubernetes.io/name: vela-core
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
image: oamdev/vela-core:v1.2.3
|
||||
imagePullPolicy: Always
|
||||
name: kubevela
|
||||
ports:
|
||||
- containerPort: 9443
|
||||
name: webhook-server
|
||||
protocol: TCP
|
||||
- containerPort: 9440
|
||||
name: healthz
|
||||
protocol: TCP
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 20Mi
|
||||
dnsPolicy: ClusterFirst
|
||||
restartPolicy: Always
|
||||
schedulerName: default-scheduler
|
||||
securityContext: {}
|
||||
terminationGracePeriodSeconds: 30`
|
||||
)
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -27,6 +28,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
version2 "github.com/oam-dev/kubevela/version"
|
||||
|
||||
"github.com/crossplane/crossplane-runtime/pkg/test"
|
||||
"github.com/google/go-github/v32/github"
|
||||
v1alpha12 "github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1"
|
||||
@@ -651,3 +654,96 @@ func TestGitLabReaderNotPanic(t *testing.T) {
|
||||
_, err := NewAsyncReader("https://gitlab.com/test/catalog", "", "addons", "", gitType)
|
||||
assert.EqualError(t, err, "git type repository only support github for now")
|
||||
}
|
||||
|
||||
func TestCheckSemVer(t *testing.T) {
|
||||
testCases := []struct {
|
||||
actual string
|
||||
require string
|
||||
nilError bool
|
||||
res bool
|
||||
}{
|
||||
{
|
||||
actual: "v1.2.1",
|
||||
require: "<=v1.2.1",
|
||||
res: true,
|
||||
},
|
||||
{
|
||||
actual: "v1.2.1",
|
||||
require: ">v1.2.1",
|
||||
res: false,
|
||||
},
|
||||
{
|
||||
actual: "v1.2.1",
|
||||
require: "<=v1.2.3",
|
||||
res: true,
|
||||
},
|
||||
{
|
||||
actual: "v1.2",
|
||||
require: "<=v1.2.3",
|
||||
res: true,
|
||||
},
|
||||
{
|
||||
actual: "v1.2.1",
|
||||
require: ">v1.2.3",
|
||||
res: false,
|
||||
},
|
||||
{
|
||||
actual: "v1.2.1",
|
||||
require: "=v1.2.1",
|
||||
res: true,
|
||||
},
|
||||
{
|
||||
actual: "1.2.1",
|
||||
require: "=v1.2.1",
|
||||
res: true,
|
||||
},
|
||||
{
|
||||
actual: "1.2.1",
|
||||
require: "",
|
||||
res: true,
|
||||
},
|
||||
{
|
||||
actual: "v1.2.2",
|
||||
require: "<=v1.2.3, >=v1.2.1",
|
||||
res: true,
|
||||
},
|
||||
{
|
||||
actual: "v1.2.0",
|
||||
require: "v1.2.0, <=v1.2.3",
|
||||
res: true,
|
||||
},
|
||||
{
|
||||
actual: "1.2.2",
|
||||
require: "v1.2.2",
|
||||
res: true,
|
||||
},
|
||||
{
|
||||
actual: "1.2.02",
|
||||
require: "v1.2.2",
|
||||
res: true,
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
result, err := checkSemVer(testCase.actual, testCase.require)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, result, testCase.res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckAddonVersionMeetRequired(t *testing.T) {
|
||||
k8sClient := &test.MockClient{
|
||||
MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {
|
||||
return nil
|
||||
}),
|
||||
}
|
||||
ctx := context.Background()
|
||||
assert.NoError(t, checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=1.2.4"}, k8sClient, nil))
|
||||
|
||||
version2.VelaVersion = "v1.2.3"
|
||||
if err := checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=1.2.4"}, k8sClient, nil); err == nil {
|
||||
assert.Error(t, fmt.Errorf("should meet error"))
|
||||
}
|
||||
|
||||
version2.VelaVersion = "v1.2.4"
|
||||
assert.NoError(t, checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=1.2.4"}, k8sClient, nil))
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ var (
|
||||
|
||||
// ErrNotExist means addon not exists
|
||||
ErrNotExist = NewAddonError("addon not exist")
|
||||
|
||||
// ErrVersionMismatch means addon version requirement mismatch
|
||||
ErrVersionMismatch = NewAddonError("addon version requirements mismatch")
|
||||
)
|
||||
|
||||
// WrapErrRateLimit return ErrRateLimit if is the situation, or return error directly
|
||||
|
||||
+6
-4
@@ -21,6 +21,8 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/client-go/discovery"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@@ -49,8 +51,8 @@ const (
|
||||
)
|
||||
|
||||
// EnableAddon will enable addon with dependency check, source is where addon from.
|
||||
func EnableAddon(ctx context.Context, name string, cli client.Client, apply apply.Applicator, config *rest.Config, r Registry, args map[string]interface{}, cache *Cache) error {
|
||||
h := NewAddonInstaller(ctx, cli, apply, config, &r, args, cache)
|
||||
func EnableAddon(ctx context.Context, name string, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r Registry, args map[string]interface{}, cache *Cache) error {
|
||||
h := NewAddonInstaller(ctx, cli, discoveryClient, apply, config, &r, args, cache)
|
||||
pkg, err := h.loadInstallPackage(name)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -76,7 +78,7 @@ func DisableAddon(ctx context.Context, cli client.Client, name string) error {
|
||||
}
|
||||
|
||||
// EnableAddonByLocalDir enable an addon from local dir
|
||||
func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli client.Client, applicator apply.Applicator, config *rest.Config, args map[string]interface{}) error {
|
||||
func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli client.Client, dc *discovery.DiscoveryClient, applicator apply.Applicator, config *rest.Config, args map[string]interface{}) error {
|
||||
r := localReader{dir: dir, name: name}
|
||||
metas, err := r.ListAddonMeta()
|
||||
if err != nil {
|
||||
@@ -91,7 +93,7 @@ func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli cli
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := NewAddonInstaller(ctx, cli, applicator, config, &Registry{Name: LocalAddonRegistryName}, args, nil)
|
||||
h := NewAddonInstaller(ctx, cli, dc, applicator, config, &Registry{Name: LocalAddonRegistryName}, args, nil)
|
||||
needEnableAddonNames, err := h.checkDependency(pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/discovery"
|
||||
|
||||
v12 "k8s.io/api/core/v1"
|
||||
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -49,6 +51,7 @@ var testEnv *envtest.Environment
|
||||
var dm discoverymapper.DiscoveryMapper
|
||||
var pd *packages.PackageDiscover
|
||||
var testns string
|
||||
var dc *discovery.DiscoveryClient
|
||||
|
||||
func TestAddon(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
@@ -79,6 +82,11 @@ var _ = BeforeSuite(func(done Done) {
|
||||
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(k8sClient).ToNot(BeNil())
|
||||
|
||||
dc, err = discovery.NewDiscoveryClientForConfig(cfg)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(dc).ShouldNot(BeNil())
|
||||
|
||||
dm, err = discoverymapper.New(cfg)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(dm).ToNot(BeNil())
|
||||
|
||||
+17
-10
@@ -54,16 +54,17 @@ type InstallPackage struct {
|
||||
|
||||
// Meta defines the format for a single addon
|
||||
type Meta struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Version string `json:"version"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
DeployTo *DeployTo `json:"deployTo,omitempty"`
|
||||
Dependencies []*Dependency `json:"dependencies,omitempty"`
|
||||
NeedNamespace []string `json:"needNamespace,omitempty"`
|
||||
Invisible bool `json:"invisible"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Version string `json:"version"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
DeployTo *DeployTo `json:"deployTo,omitempty"`
|
||||
Dependencies []*Dependency `json:"dependencies,omitempty"`
|
||||
NeedNamespace []string `json:"needNamespace,omitempty"`
|
||||
Invisible bool `json:"invisible"`
|
||||
SystemRequirements *SystemRequirements `json:"system,omitempty"`
|
||||
}
|
||||
|
||||
// DeployTo defines where the addon to deploy to
|
||||
@@ -84,3 +85,9 @@ type ElementFile struct {
|
||||
Data string
|
||||
Name string
|
||||
}
|
||||
|
||||
// SystemRequirements is this addon need version
|
||||
type SystemRequirements struct {
|
||||
VelaVersion string `json:"vela,omitempty"`
|
||||
KubernetesVersion string `json:"kubernetes,omitempty"`
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package clients
|
||||
|
||||
import (
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
@@ -84,3 +85,16 @@ func GetPackageDiscover() (*packages.PackageDiscover, error) {
|
||||
}
|
||||
return pd, nil
|
||||
}
|
||||
|
||||
// GetDiscoveryClient return a discovery client
|
||||
func GetDiscoveryClient() (*discovery.DiscoveryClient, error) {
|
||||
conf, err := GetKubeConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dc, err := discovery.NewDiscoveryClientForConfig(conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dc, nil
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/discovery"
|
||||
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@@ -101,6 +103,10 @@ func NewAddonUsecase(cacheTime time.Duration) AddonHandler {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dc, err := clients.GetDiscoveryClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ds := pkgaddon.NewRegistryDataStore(kubecli)
|
||||
cache := pkgaddon.NewCache(ds)
|
||||
|
||||
@@ -114,6 +120,7 @@ func NewAddonUsecase(cacheTime time.Duration) AddonHandler {
|
||||
config: config,
|
||||
apply: apply.NewAPIApplicator(kubecli),
|
||||
mutex: new(sync.RWMutex),
|
||||
discoveryClient: dc,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +130,7 @@ type defaultAddonHandler struct {
|
||||
kubeClient client.Client
|
||||
config *rest.Config
|
||||
apply apply.Applicator
|
||||
discoveryClient *discovery.DiscoveryClient
|
||||
|
||||
mutex *sync.RWMutex
|
||||
}
|
||||
@@ -352,15 +360,23 @@ func (u *defaultAddonHandler) EnableAddon(ctx context.Context, name string, args
|
||||
return err
|
||||
}
|
||||
for _, r := range registries {
|
||||
err = pkgaddon.EnableAddon(ctx, name, u.kubeClient, u.apply, u.config, r, args.Args, u.addonRegistryCache)
|
||||
err = pkgaddon.EnableAddon(ctx, name, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil && errors.As(err, &pkgaddon.ErrNotExist) {
|
||||
// if reach this line error must is not nil
|
||||
if errors.Is(err, pkgaddon.ErrNotExist) {
|
||||
// one registry return addon not exist error, should not break other registry func
|
||||
continue
|
||||
}
|
||||
|
||||
// wrap this error with special bcode
|
||||
if errors.Is(err, pkgaddon.ErrVersionMismatch) {
|
||||
return bcode.ErrAddonSystemVersionMismatch
|
||||
}
|
||||
// except `addon not found`, other errors should return directly
|
||||
return err
|
||||
}
|
||||
return bcode.ErrAddonNotExist
|
||||
}
|
||||
@@ -412,13 +428,21 @@ func (u *defaultAddonHandler) UpdateAddon(ctx context.Context, name string, args
|
||||
}
|
||||
|
||||
for _, r := range registries {
|
||||
err = pkgaddon.EnableAddon(ctx, name, u.kubeClient, u.apply, u.config, r, args.Args, u.addonRegistryCache)
|
||||
err = pkgaddon.EnableAddon(ctx, name, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if err != nil && !errors.Is(err, pkgaddon.ErrNotExist) {
|
||||
return bcode.WrapGithubRateLimitErr(err)
|
||||
|
||||
if errors.Is(err, pkgaddon.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
// wrap this error with special bcode
|
||||
if errors.Is(err, pkgaddon.ErrVersionMismatch) {
|
||||
return bcode.ErrAddonSystemVersionMismatch
|
||||
}
|
||||
// except `addon not found`, other errors should return directly
|
||||
return err
|
||||
}
|
||||
return bcode.ErrAddonNotExist
|
||||
}
|
||||
|
||||
@@ -61,6 +61,9 @@ var (
|
||||
|
||||
// ErrAddonDependencyNotSatisfy means addon's dependencies is not enabled
|
||||
ErrAddonDependencyNotSatisfy = NewBcode(500, 50017, "addon's dependencies is not enabled")
|
||||
|
||||
// ErrAddonSystemVersionMismatch means addon's version required mismatch
|
||||
ErrAddonSystemVersionMismatch = NewBcode(400, 50018, "addon's system version requirement mismatch")
|
||||
)
|
||||
|
||||
// isGithubRateLimit check if error is github rate limit
|
||||
|
||||
@@ -19,6 +19,8 @@ package common
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/client-go/discovery"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/util/flowcontrol"
|
||||
@@ -36,6 +38,7 @@ type Args struct {
|
||||
client client.Client
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
dc *discovery.DiscoveryClient
|
||||
}
|
||||
|
||||
// SetConfig insert kubeconfig into Args
|
||||
@@ -122,3 +125,20 @@ func (a *Args) GetPackageDiscover() (*packages.PackageDiscover, error) {
|
||||
a.pd = pd
|
||||
return pd, nil
|
||||
}
|
||||
|
||||
// GetDiscoveryClient return a discovery client from cli args
|
||||
func (a *Args) GetDiscoveryClient() (*discovery.DiscoveryClient, error) {
|
||||
if a.dc != nil {
|
||||
return a.dc, nil
|
||||
}
|
||||
cfg, err := a.GetConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dc, err := discovery.NewDiscoveryClientForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dc, nil
|
||||
}
|
||||
|
||||
+18
-8
@@ -24,6 +24,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/discovery"
|
||||
|
||||
"helm.sh/helm/v3/pkg/strvals"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
@@ -134,6 +136,10 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dc, err := c.GetDiscoveryClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
addonOrDir := args[0]
|
||||
var name = addonOrDir
|
||||
if file, err := os.Stat(addonOrDir); err == nil {
|
||||
@@ -143,7 +149,7 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
|
||||
ioStream.Infof("enable addon by local dir: %s \n", addonOrDir)
|
||||
// args[0] is a local path install with local dir, use base dir name as addonName
|
||||
name = filepath.Base(addonOrDir)
|
||||
err = enableAddonByLocal(ctx, name, addonOrDir, k8sClient, config, addonArgs)
|
||||
err = enableAddonByLocal(ctx, name, addonOrDir, k8sClient, dc, config, addonArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -152,7 +158,7 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
|
||||
return fmt.Errorf("addon directory %s not found in local", addonOrDir)
|
||||
}
|
||||
|
||||
err = enableAddon(ctx, k8sClient, config, name, addonArgs)
|
||||
err = enableAddon(ctx, k8sClient, dc, config, name, addonArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -204,6 +210,10 @@ func NewAddonUpgradeCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Co
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dc, err := c.GetDiscoveryClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
addonArgs, err := parseToMap(args[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -221,7 +231,7 @@ func NewAddonUpgradeCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Co
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "cannot fetch addon related addon %s", name)
|
||||
}
|
||||
err = enableAddonByLocal(ctx, name, addonOrDir, k8sClient, config, addonArgs)
|
||||
err = enableAddonByLocal(ctx, name, addonOrDir, k8sClient, dc, config, addonArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -234,7 +244,7 @@ func NewAddonUpgradeCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Co
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "cannot fetch addon related addon %s", addonOrDir)
|
||||
}
|
||||
err = enableAddon(ctx, k8sClient, config, addonOrDir, addonArgs)
|
||||
err = enableAddon(ctx, k8sClient, dc, config, addonOrDir, addonArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -305,7 +315,7 @@ func NewAddonStatusCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
|
||||
}
|
||||
}
|
||||
|
||||
func enableAddon(ctx context.Context, k8sClient client.Client, config *rest.Config, name string, args map[string]interface{}) error {
|
||||
func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.DiscoveryClient, config *rest.Config, name string, args map[string]interface{}) error {
|
||||
var err error
|
||||
registryDS := pkgaddon.NewRegistryDataStore(k8sClient)
|
||||
registries, err := registryDS.ListRegistries(ctx)
|
||||
@@ -314,7 +324,7 @@ func enableAddon(ctx context.Context, k8sClient client.Client, config *rest.Conf
|
||||
}
|
||||
|
||||
for _, registry := range registries {
|
||||
err = pkgaddon.EnableAddon(ctx, name, k8sClient, apply.NewAPIApplicator(k8sClient), config, registry, args, nil)
|
||||
err = pkgaddon.EnableAddon(ctx, name, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, registry, args, nil)
|
||||
if errors.Is(err, pkgaddon.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
@@ -330,8 +340,8 @@ func enableAddon(ctx context.Context, k8sClient client.Client, config *rest.Conf
|
||||
}
|
||||
|
||||
// enableAddonByLocal enable addon in local dir and return the addon name
|
||||
func enableAddonByLocal(ctx context.Context, name string, dir string, k8sClient client.Client, config *rest.Config, args map[string]interface{}) error {
|
||||
if err := pkgaddon.EnableAddonByLocalDir(ctx, name, dir, k8sClient, apply.NewAPIApplicator(k8sClient), config, args); err != nil {
|
||||
func enableAddonByLocal(ctx context.Context, name string, dir string, k8sClient client.Client, dc *discovery.DiscoveryClient, config *rest.Config, args map[string]interface{}) error {
|
||||
if err := pkgaddon.EnableAddonByLocalDir(ctx, name, dir, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, args); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := waitApplicationRunning(k8sClient, name); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user