From cff4fae0a1f0008f7db009e6f4a0d3b2d14f3557 Mon Sep 17 00:00:00 2001 From: hys Date: Sat, 18 May 2024 15:03:55 +0800 Subject: [PATCH] feat: add downloading progress for app image pull --- .github/workflows/publish_docker.yaml | 2 +- .github/workflows/publish_imageservice.yaml | 28 ++ Dockerfile.image | 29 ++ .../v1alpha1/application_states.go | 3 +- .../v1alpha1/appmanager_states.go | 2 + .../v1alpha1/appmanager_types.go | 8 + .../v1alpha1/imagemanager_types.go | 64 ++++ .../v1alpha1/zz_generated.deepcopy.go | 125 +++++++ cmd/image-service/main.go | 88 +++++ .../app.bytetrade.io_applicationmanagers.yaml | 8 + .../bases/app.bytetrade.io_applications.yaml | 1 + .../bases/app.bytetrade.io_imagemanagers.yaml | 109 ++++++ controllers/application_controller.go | 1 + controllers/appmgr_controller.go | 331 +++++++++++++++--- controllers/image_controller.go | 252 +++++++++++++ go.mod | 28 +- go.sum | 60 +++- pkg/apiserver/handler_app.go | 11 +- pkg/apiserver/handler_installer.go | 64 ++-- pkg/apiserver/handler_model.go | 2 +- pkg/apiserver/webservice.go | 4 +- pkg/appinstaller/helm.go | 2 +- .../versioned/fake/clientset_generated.go | 2 + pkg/generated/clientset/versioned/fake/doc.go | 2 + .../clientset/versioned/fake/register.go | 16 +- .../clientset/versioned/scheme/doc.go | 2 + .../clientset/versioned/scheme/register.go | 14 +- .../v1alpha1/app.bytetrade.io_client.go | 5 + .../fake/fake_app.bytetrade.io_client.go | 4 + .../v1alpha1/fake/fake_imagemanager.go | 117 +++++++ .../v1alpha1/generated_expansion.go | 2 + .../app.bytetrade.io/v1alpha1/imagemanager.go | 168 +++++++++ .../app.bytetrade.io/v1alpha1/imagemanager.go | 73 ++++ .../app.bytetrade.io/v1alpha1/interface.go | 7 + .../informers/externalversions/generic.go | 2 + .../v1alpha1/expansion_generated.go | 4 + .../app.bytetrade.io/v1alpha1/imagemanager.go | 52 +++ pkg/images/jobs.go | 260 ++++++++++++++ pkg/images/puller.go | 119 +++++++ pkg/utils/app.go | 58 +-- pkg/utils/helm.go | 219 ++++++++++++ 41 files changed, 2219 insertions(+), 129 deletions(-) create mode 100644 .github/workflows/publish_imageservice.yaml create mode 100644 Dockerfile.image create mode 100644 api/app.bytetrade.io/v1alpha1/imagemanager_types.go create mode 100644 cmd/image-service/main.go create mode 100644 config/crd/bases/app.bytetrade.io_imagemanagers.yaml create mode 100644 controllers/image_controller.go create mode 100644 pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/fake/fake_imagemanager.go create mode 100644 pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/imagemanager.go create mode 100644 pkg/generated/informers/externalversions/app.bytetrade.io/v1alpha1/imagemanager.go create mode 100644 pkg/generated/listers/app.bytetrade.io/v1alpha1/imagemanager.go create mode 100644 pkg/images/jobs.go create mode 100644 pkg/images/puller.go create mode 100644 pkg/utils/helm.go diff --git a/.github/workflows/publish_docker.yaml b/.github/workflows/publish_docker.yaml index 957c071..d06db22 100644 --- a/.github/workflows/publish_docker.yaml +++ b/.github/workflows/publish_docker.yaml @@ -1,4 +1,4 @@ -name: Publish to Dockerhub +name: Publish app-service to Dockerhub on: workflow_dispatch: diff --git a/.github/workflows/publish_imageservice.yaml b/.github/workflows/publish_imageservice.yaml new file mode 100644 index 0000000..78f9c00 --- /dev/null +++ b/.github/workflows/publish_imageservice.yaml @@ -0,0 +1,28 @@ +name: Publish image-service to Dockerhub + +on: + workflow_dispatch: + inputs: + tags: + description: 'Release Tags' + +jobs: + update_dockerhub: + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASS }} + + - name: Build and push Docker image + uses: docker/build-push-action@v3 + with: + push: true + tags: beclab/image-service:${{ github.event.inputs.tags }} + file: Dockerfile.image + diff --git a/Dockerfile.image b/Dockerfile.image new file mode 100644 index 0000000..6aa5db5 --- /dev/null +++ b/Dockerfile.image @@ -0,0 +1,29 @@ +# Build the manager binary +FROM golang:1.18 as builder + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY go.mod bytetrade.io/web3os/app-service/go.mod +COPY go.sum bytetrade.io/web3os/app-service/go.sum + +RUN cd bytetrade.io/web3os/app-service && \ + go mod download + +# Copy the go source +COPY cmd/ bytetrade.io/web3os/app-service/cmd/ +COPY api/ bytetrade.io/web3os/app-service/api/ +COPY controllers/ bytetrade.io/web3os/app-service/controllers/ +COPY pkg/ bytetrade.io/web3os/app-service/pkg/ + +# Build +RUN cd bytetrade.io/web3os/app-service && \ + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -a -o image-service cmd/image-service/main.go + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:debug +WORKDIR / +COPY --from=builder /workspace/bytetrade.io/web3os/app-service/image-service . + +ENTRYPOINT ["/image-service"] +USER 65532:65532 \ No newline at end of file diff --git a/api/app.bytetrade.io/v1alpha1/application_states.go b/api/app.bytetrade.io/v1alpha1/application_states.go index 960a05d..ea7dc6c 100644 --- a/api/app.bytetrade.io/v1alpha1/application_states.go +++ b/api/app.bytetrade.io/v1alpha1/application_states.go @@ -19,7 +19,8 @@ const ( // AppUpgrading means that an upgrade operation is underway. AppUpgrading ApplicationState = "upgrading" // AppResuming means that a resume operation is underway. - AppResuming ApplicationState = "resuming" + AppResuming ApplicationState = "resuming" + AppDownloading ApplicationState = "downloading" ) func (a ApplicationState) String() string { diff --git a/api/app.bytetrade.io/v1alpha1/appmanager_states.go b/api/app.bytetrade.io/v1alpha1/appmanager_states.go index c895047..965bc87 100644 --- a/api/app.bytetrade.io/v1alpha1/appmanager_states.go +++ b/api/app.bytetrade.io/v1alpha1/appmanager_states.go @@ -26,6 +26,8 @@ const ( // Stopping means that the suspend operation is underway. Stopping ApplicationManagerState = "stopping" + Downloading ApplicationManagerState = "downloading" + // Processing means that the intermediate state of an operation, include // installing,upgrading,uninstalling,resuming,canceling,stopping. Processing ApplicationManagerState = "processing" diff --git a/api/app.bytetrade.io/v1alpha1/appmanager_types.go b/api/app.bytetrade.io/v1alpha1/appmanager_types.go index 72d63db..0bb36b0 100644 --- a/api/app.bytetrade.io/v1alpha1/appmanager_types.go +++ b/api/app.bytetrade.io/v1alpha1/appmanager_types.go @@ -64,6 +64,14 @@ type OpRecord struct { StatusTime *metav1.Time `json:"statusTime"` } +//type ImageProgress struct { +// AppName string `json:"appName"` +// OwnerName string `json:"ownerName"` +// NodeName string `json:"nodeName"` +// ImageRef string `json:"imageRef"` +// Progress string `json:"progress"` +//} + //+kubebuilder:object:root=true //+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/api/app.bytetrade.io/v1alpha1/imagemanager_types.go b/api/app.bytetrade.io/v1alpha1/imagemanager_types.go new file mode 100644 index 0000000..4f85a7c --- /dev/null +++ b/api/app.bytetrade.io/v1alpha1/imagemanager_types.go @@ -0,0 +1,64 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +//+genclient +//+genclient:nonNamespaced +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:scope=Cluster, shortName={im}, categories={all} +//+kubebuilder:printcolumn:JSONPath=.spec.appName, name=application name, type=string +//+kubebuilder:printcolumn:JSONPath=.spec.appNamespace, name=namespace, type=string +//+kubebuilder:printcolumn:JSONPath=.status.state, name=state, type=string +//+kubebuilder:printcolumn:JSONPath=.metadata.creationTimestamp, name=age, type=date +//+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ImageManager is the Schema for the image managers API +type ImageManager struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ImageManagerSpec `json:"spec,omitempty"` + Status ImageManagerStatus `json:"status,omitempty"` +} + +// ImageManagerStatus defines the observed state of ApplicationManager +type ImageManagerStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + Conditions []ImageProgress `json:"conditions,omitempty"` + Message string `json:"message,omitempty"` + State string `json:"state"` + UpdateTime *metav1.Time `json:"updateTime"` + StatusTime *metav1.Time `json:"statusTime"` +} + +// ImageManagerSpec defines the desired state of ImageManager +type ImageManagerSpec struct { + AppName string `json:"appName"` + AppNamespace string `json:"appNamespace,omitempty"` + AppOwner string `json:"appOwner,omitempty"` + Refs []string `json:"refs"` +} + +type ImageProgress struct { + NodeName string `json:"nodeName"` + ImageRef string `json:"imageRef"` + Progress string `json:"progress"` +} + +//+kubebuilder:object:root=true +//+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ImageManagerList contains a list of ApplicationManager +type ImageManagerList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ImageManager `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ImageManager{}, &ImageManagerList{}) +} diff --git a/api/app.bytetrade.io/v1alpha1/zz_generated.deepcopy.go b/api/app.bytetrade.io/v1alpha1/zz_generated.deepcopy.go index 45f5a3c..f147941 100644 --- a/api/app.bytetrade.io/v1alpha1/zz_generated.deepcopy.go +++ b/api/app.bytetrade.io/v1alpha1/zz_generated.deepcopy.go @@ -1,3 +1,6 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + // Code generated by controller-gen. DO NOT EDIT. package v1alpha1 @@ -241,6 +244,128 @@ func (in *Entrance) DeepCopy() *Entrance { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageManager) DeepCopyInto(out *ImageManager) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageManager. +func (in *ImageManager) DeepCopy() *ImageManager { + if in == nil { + return nil + } + out := new(ImageManager) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ImageManager) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageManagerList) DeepCopyInto(out *ImageManagerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ImageManager, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageManagerList. +func (in *ImageManagerList) DeepCopy() *ImageManagerList { + if in == nil { + return nil + } + out := new(ImageManagerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ImageManagerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageManagerSpec) DeepCopyInto(out *ImageManagerSpec) { + *out = *in + if in.Refs != nil { + in, out := &in.Refs, &out.Refs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageManagerSpec. +func (in *ImageManagerSpec) DeepCopy() *ImageManagerSpec { + if in == nil { + return nil + } + out := new(ImageManagerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageManagerStatus) DeepCopyInto(out *ImageManagerStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]ImageProgress, len(*in)) + copy(*out, *in) + } + if in.UpdateTime != nil { + in, out := &in.UpdateTime, &out.UpdateTime + *out = (*in).DeepCopy() + } + if in.StatusTime != nil { + in, out := &in.StatusTime, &out.StatusTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageManagerStatus. +func (in *ImageManagerStatus) DeepCopy() *ImageManagerStatus { + if in == nil { + return nil + } + out := new(ImageManagerStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageProgress) DeepCopyInto(out *ImageProgress) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageProgress. +func (in *ImageProgress) DeepCopy() *ImageProgress { + if in == nil { + return nil + } + out := new(ImageProgress) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OpRecord) DeepCopyInto(out *OpRecord) { *out = *in diff --git a/cmd/image-service/main.go b/cmd/image-service/main.go new file mode 100644 index 0000000..2b06cea --- /dev/null +++ b/cmd/image-service/main.go @@ -0,0 +1,88 @@ +package main + +import ( + appv1alpha1 "bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1" + sysv1alpha1 "bytetrade.io/web3os/app-service/api/sys.bytetrade.io/v1alpha1" + "bytetrade.io/web3os/app-service/controllers" + "context" + "go.uber.org/zap/zapcore" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "os" + "os/signal" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "syscall" +) + +var ( + scheme = runtime.NewScheme() + imageLog = ctrl.Log.WithName("image") +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(appv1alpha1.AddToScheme(scheme)) + utilruntime.Must(sysv1alpha1.AddToScheme(scheme)) + //+kubebuilder:scaffold:scheme +} + +var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM} + +func main() { + opts := zap.Options{ + Development: true, + TimeEncoder: zapcore.RFC3339TimeEncoder, + } + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + config := ctrl.GetConfigOrDie() + mgr, err := ctrl.NewManager(config, ctrl.Options{ + Scheme: scheme, + MetricsBindAddress: ":8087", + HealthProbeBindAddress: ":7081", + LeaderElection: false, + }) + if err != nil { + imageLog.Error(err, "Unable to start image manager") + os.Exit(1) + } + + if err = (&controllers.ImageManagerController{ + Client: mgr.GetClient(), + }).SetupWithManager(mgr); err != nil { + imageLog.Error(err, "Unable to create image controller") + os.Exit(1) + } + + if err = mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + imageLog.Error(err, "Unable to set up health check") + os.Exit(1) + } + if err = mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + imageLog.Error(err, "Unable to set up ready check") + os.Exit(1) + } + + ictx, cancelFunc := context.WithCancel(context.Background()) + + c := make(chan os.Signal, 2) + signal.Notify(c, shutdownSignals...) + go func() { + select { + case <-c: + cancelFunc() + <-c + os.Exit(1) // second signal. Exit directly. + + } + }() + + if err = mgr.Start(ictx); err != nil { + cancelFunc() + imageLog.Error(err, "Unable to running image manager") + os.Exit(1) + } + cancelFunc() +} diff --git a/config/crd/bases/app.bytetrade.io_applicationmanagers.yaml b/config/crd/bases/app.bytetrade.io_applicationmanagers.yaml index 88ab6f6..56d3f99 100644 --- a/config/crd/bases/app.bytetrade.io_applicationmanagers.yaml +++ b/config/crd/bases/app.bytetrade.io_applicationmanagers.yaml @@ -64,6 +64,7 @@ spec: source: type: string type: + description: Type means the entity that system support. type: string required: - appName @@ -80,14 +81,18 @@ spec: type: integer opRecords: items: + description: OpRecord contains details of an operation. properties: message: type: string opType: + description: OpType represents the type of operation being performed. type: string source: type: string status: + description: ApplicationManagerState is the state of an applicationmanager + at current time type: string statusTime: format: date-time @@ -104,6 +109,7 @@ spec: type: object type: array opType: + description: OpType represents the type of operation being performed. type: string payload: additionalProperties: @@ -112,6 +118,8 @@ spec: progress: type: string state: + description: ApplicationManagerState is the state of an applicationmanager + at current time type: string statusTime: format: date-time diff --git a/config/crd/bases/app.bytetrade.io_applications.yaml b/config/crd/bases/app.bytetrade.io_applications.yaml index cf097c2..3e7b8c8 100644 --- a/config/crd/bases/app.bytetrade.io_applications.yaml +++ b/config/crd/bases/app.bytetrade.io_applications.yaml @@ -65,6 +65,7 @@ spec: entrances: description: Entrances []Entrance `json:"entrances,omitempty"` items: + description: Entrance contains details for application entrance properties: authLevel: type: string diff --git a/config/crd/bases/app.bytetrade.io_imagemanagers.yaml b/config/crd/bases/app.bytetrade.io_imagemanagers.yaml new file mode 100644 index 0000000..90aa20b --- /dev/null +++ b/config/crd/bases/app.bytetrade.io_imagemanagers.yaml @@ -0,0 +1,109 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: imagemanagers.app.bytetrade.io +spec: + group: app.bytetrade.io + names: + categories: + - all + kind: ImageManager + listKind: ImageManagerList + plural: imagemanagers + shortNames: + - im + singular: imagemanager + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.appName + name: application name + type: string + - jsonPath: .spec.appNamespace + name: namespace + type: string + - jsonPath: .status.state + name: state + type: string + - jsonPath: .metadata.creationTimestamp + name: age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ImageManager is the Schema for the image managers API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ImageManagerSpec defines the desired state of ImageManager + properties: + appName: + type: string + appNamespace: + type: string + appOwner: + type: string + refs: + items: + type: string + type: array + required: + - appName + - refs + type: object + status: + description: ImageManagerStatus defines the observed state of ApplicationManager + properties: + conditions: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + items: + properties: + imageRef: + type: string + nodeName: + type: string + progress: + type: string + required: + - imageRef + - nodeName + - progress + type: object + type: array + message: + type: string + state: + type: string + statusTime: + format: date-time + type: string + updateTime: + format: date-time + type: string + required: + - state + - statusTime + - updateTime + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/controllers/application_controller.go b/controllers/application_controller.go index 9e8cd50..0fb33aa 100644 --- a/controllers/application_controller.go +++ b/controllers/application_controller.go @@ -194,6 +194,7 @@ func (r *ApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Request) if err != nil { return ctrl.Result{}, err } + //klog.Infof("in application xxxxxx conditiosn: %v", appMgr.Status.Conditions) now := metav1.Now() state := appv1alpha1.Completed opRecord := appv1alpha1.OpRecord{ diff --git a/controllers/appmgr_controller.go b/controllers/appmgr_controller.go index c4cbb45..ab4be0c 100644 --- a/controllers/appmgr_controller.go +++ b/controllers/appmgr_controller.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "math" "net/http" "path/filepath" "reflect" @@ -206,7 +207,7 @@ func (r *ApplicationManagerController) reconcile2(cur *appv1alpha1.ApplicationMa case appv1alpha1.UninstallOp: err = r.uninstall(ctx, cur) case appv1alpha1.CancelOp: - if cur.Status.State == appv1alpha1.Installing { + if cur.Status.State == appv1alpha1.Installing || cur.Status.State == appv1alpha1.Downloading { err = r.cancel(cur) } if cur.Status.State == appv1alpha1.Pending { @@ -278,7 +279,6 @@ func (r *ApplicationManagerController) preEnqueueCheckForUpdate(old, new client. func (r *ApplicationManagerController) updateStatus(appMgr *appv1alpha1.ApplicationManager, state appv1alpha1.ApplicationManagerState, opRecord *appv1alpha1.OpRecord, appState appv1alpha1.ApplicationState, message string) error { var err error - now := metav1.Now() appMgrCopy := appMgr.DeepCopy() appMgr.Status.State = state @@ -291,7 +291,6 @@ func (r *ApplicationManagerController) updateStatus(appMgr *appv1alpha1.Applicat if len(appMgr.Status.OpRecords) > 20 { appMgr.Status.OpRecords = appMgr.Status.OpRecords[:20:20] } - err = r.Status().Patch(context.TODO(), appMgr, client.MergeFrom(appMgrCopy)) if err != nil { return err @@ -310,58 +309,150 @@ func (r *ApplicationManagerController) install(ctx context.Context, appMgr *appv var ops *appinstaller.HelmOps defer func() { if err != nil { - if err.Error() != "canceled" { - state := appv1alpha1.Failed - now := metav1.Now() - message := err.Error() - var appMgrCur appv1alpha1.ApplicationManager - e := r.Get(ctx, types.NamespacedName{Name: appMgr.Name}, &appMgrCur) - if e != nil { - klog.Errorf("Failed to get applicationmanagers name=%s err=%v", appMgr.Name, e) - } + klog.Infof("install failed .......err=%v", err) - e = ops.Uninstall() - if e != nil { - klog.Errorf("Failed to uninstall app name=%s err=%v", appMgr.Spec.AppName, e) - } + //todo + if errors.Is(err, context.Canceled) { + klog.Infof("isfkls;f;s ..... canceled") + } else { + klog.Infof("isfkls;f;s ..... not canceled") + } + if err.Error() == "canceled" { + return + } + //todo - opRecord := appv1alpha1.OpRecord{ - OpType: appv1alpha1.InstallOp, - Message: message, - Source: appMgr.Spec.Source, - Version: version, - Status: state, - StatusTime: &now, - } + state := appv1alpha1.Failed + now := metav1.Now() + message := err.Error() + var appMgrCur appv1alpha1.ApplicationManager + e := r.Get(ctx, types.NamespacedName{Name: appMgr.Name}, &appMgrCur) + if e != nil { + klog.Errorf("Failed to get applicationmanagers name=%s err=%v", appMgr.Name, e) + } + // TODO: check release if exits, if exist do uninstall + e = ops.Uninstall() + if e != nil { + klog.Errorf("Failed to uninstall app name=%s err=%v", appMgr.Spec.AppName, e) + } - e = r.updateStatus(appMgr, state, &opRecord, "", message) - if e != nil { - klog.Errorf("Failed to update applicationmanagers status name=%s err=%v", appMgr.Name, e) - } + opRecord := appv1alpha1.OpRecord{ + OpType: appv1alpha1.InstallOp, + Message: message, + Source: appMgr.Spec.Source, + Version: version, + Status: state, + StatusTime: &now, + } + if errors.Is(err, context.Canceled) { + klog.Errorf("CCCCCCCCCCCCCCCCC") + opRecord.Status = appv1alpha1.Canceled + opRecord.OpType = appv1alpha1.CancelOp + opRecord.Message = constants.OperationCanceledByUserTpl + } + klog.Infof("opercordexxefwf: %#v", opRecord) + e = r.updateStatus(appMgr, opRecord.Status, &opRecord, "", opRecord.Message) + if e != nil { + klog.Errorf("Failed to update applicationmanagers status name=%s err=%v", appMgr.Name, e) } } + }() - // this time app has not been created, so do not update app status - message := fmt.Sprintf("Start to install %s: %s", appMgr.Spec.Type.String(), appMgr.Spec.AppName) - err = r.updateStatus(appMgr, appv1alpha1.Installing, nil, "", message) + payload := appMgr.Status.Payload + version = payload["version"] + token := payload["token"] + cfgURL := payload["cfgURL"] + repoURL := payload["repoURL"] + + var appconfig *appinstaller.ApplicationConfig + var chartPath string + kubeConfig, err := ctrl.GetConfig() if err != nil { return err } - kubeConfig, err := ctrl.GetConfig() + appconfig, chartPath, err = apiserver.GetAppConfig(ctx, appMgr.Spec.AppName, appMgr.Spec.AppOwner, cfgURL, repoURL, "", token) if err != nil { return err } - var appconfig *appinstaller.ApplicationConfig + klog.Infof("chartPath: %v", chartPath) + ops, err = appinstaller.NewHelmOps(ctx, kubeConfig, appconfig, token, appinstaller.Opt{Source: appMgr.Spec.Source}) + if err != nil { + return err + } + // get images that need to download + refs, err := utils.GetRefFromResourceList(chartPath) + if err != nil { + klog.Infof("get ref err=%v", err) + return err + } + klog.Infof("refs: %v", refs) - payload := appMgr.Status.Payload - version = payload["version"] - token := payload["token"] - cfgURL := payload["cfgURL"] - repoURL := payload["repoURL"] - appconfig, err = apiserver.GetAppConfig(ctx, appMgr.Spec.AppName, appMgr.Spec.AppOwner, cfgURL, repoURL, "", token) + //var nodes corev1.NodeList + //err = r.List(ctx, &nodes, &client.ListOptions{}) + //if err != nil { + // return err + //} - ops, err = appinstaller.NewHelmOps(ctx, kubeConfig, appconfig, token, appinstaller.Opt{Source: appMgr.Spec.Source}) + err = r.createImageManager(ctx, appMgr, refs) + if err != nil { + return err + } + + // wait image to be downloaded + err = r.updateStatus(appMgr, appv1alpha1.Downloading, nil, "", "downloading") + if err != nil { + return err + } + + err = r.pollDownloadProgress(ctx, appMgr) + klog.Infof("download failedxxxxx err=%v", err) + + if err != nil { + //if errors.Is(err, context.Canceled) { + // klog.Infof("is err canceled ....") + // //err = r.updateStatus(instance, appv1alpha1.Canceled, err.Error()) + // + //} else { + // klog.Infof("is not concelex.....") + //} + //if errors.Is(err, context.Canceled) { + // now := metav1.Now() + // + // opRecord := appv1alpha1.OpRecord{ + // OpType: appv1alpha1.CancelOp, + // Message: fmt.Sprintf(constants.OperationCanceledByUserTpl), + // Source: appMgr.Spec.Source, + // Version: appMgr.Status.Payload["version"], + // Status: appv1alpha1.Canceled, + // StatusTime: &now, + // } + // err = r.updateStatus(appMgr, appv1alpha1.Canceled, &opRecord, "", opRecord.Message) + // if err != nil { + // klog.Infof("update status in conceld err=%v", err) + // } + // + // //err = r.Get(ctx, types.NamespacedName{Name: appMgr.Name}, &cur) + // //klog.Infof("after context canceled conditions: %v", cur.Status.Conditions) + //} + return err + } + klog.Infof("image xxx downloaded.....") + + err = r.Get(ctx, types.NamespacedName{Name: appMgr.Name}, appMgr) + klog.Infof("before installing conditions: %v", appMgr.Status.Progress) + + // this time app has not been created, so do not update app status + message := fmt.Sprintf("Start to install %s: %s", appMgr.Spec.Type.String(), appMgr.Spec.AppName) + err = r.updateStatus(appMgr, appv1alpha1.Installing, nil, "", message) + if err != nil { + return err + } + //err = r.Get(ctx, types.NamespacedName{Name: appMgr.Name}, &cur) + //klog.Infof("after installing conditions: %v", cur.Status.Progress) + // + //err = r.Get(ctx, types.NamespacedName{Name: appMgr.Name}, &cur) + //klog.Infof("after installing conditions: %v", cur.Status.Conditions) _, err = apiserver.CheckAppRequirement(kubeConfig, token, appconfig) if err != nil { @@ -391,6 +482,12 @@ func (r *ApplicationManagerController) install(ctx context.Context, appMgr *appv if err != nil { klog.Errorf("Failed to update applicationmanagers status name=%s err=%v", appMgr.Name, err) } + + err = r.Get(ctx, types.NamespacedName{Name: appMgr.Name}, appMgr) + //klog.Infof("after completed conditions: %v", cur.Status.Conditions) + var curAppMgr appv1alpha1.ApplicationManager + err = r.Get(ctx, types.NamespacedName{Name: appMgr.Name}, &curAppMgr) + klog.Infof("xxx.appp: status: %v", curAppMgr.Status.Progress) return err } @@ -595,7 +692,10 @@ func (r *ApplicationManagerController) upgrade(ctx context.Context, appMgr *appv }() - err = r.updateStatus(appMgr, appv1alpha1.Upgrading, nil, appv1alpha1.AppUpgrading, appv1alpha1.AppUpgrading.String()) + //appconfig, chartPath, err = apiserver.GetAppConfig(ctx, appMgr.Spec.AppName, appMgr.Spec.AppOwner, cfgURL, repoURL, "", token) + err = r.updateStatus(appMgr, appv1alpha1.Upgrading, nil, appv1alpha1.AppUpgrading, appv1alpha1.Upgrading.String()) + + //err = r.updateStatus(appMgr, appv1alpha1.Downloading, nil, appv1alpha1.AppDownloading, appv1alpha1.Downloading.String()) if err != nil { return err } @@ -614,8 +714,10 @@ func (r *ApplicationManagerController) upgrade(ctx context.Context, appMgr *appv cfgURL := appMgr.Status.Payload["cfgURL"] repoURL := appMgr.Status.Payload["repoURL"] token := appMgr.Status.Payload["token"] + var chartPath string + if !userspace.IsSysApp(appMgr.Spec.AppName) { - appconfig, err = apiserver.GetAppConfig(ctx, appMgr.Spec.AppName, appMgr.Spec.AppOwner, cfgURL, repoURL, version, token) + appconfig, chartPath, err = apiserver.GetAppConfig(ctx, appMgr.Spec.AppName, appMgr.Spec.AppOwner, cfgURL, repoURL, version, token) if err != nil { return err } @@ -624,7 +726,7 @@ func (r *ApplicationManagerController) upgrade(ctx context.Context, appMgr *appv return err } } else { - chart, err := apiserver.GetIndexAndDownloadChart(ctx, appMgr.Spec.AppName, repoURL, version, token) + chartPath, err = apiserver.GetIndexAndDownloadChart(ctx, appMgr.Spec.AppName, repoURL, version, token) if err != nil { return err } @@ -632,16 +734,32 @@ func (r *ApplicationManagerController) upgrade(ctx context.Context, appMgr *appv AppName: appMgr.Spec.AppName, Namespace: appMgr.Spec.AppNamespace, OwnerName: appMgr.Spec.AppOwner, - ChartsName: chart, + ChartsName: chartPath, RepoURL: repoURL, } - } ops, err := appinstaller.NewHelmOps(ctx, config, appconfig, token, appinstaller.Opt{Source: appMgr.Spec.Source}) if err != nil { return err } + + refs, err := utils.GetRefFromResourceList(chartPath) + if err != nil { + return err + } + err = r.createImageManager(ctx, appMgr, refs) + if err != nil { + return err + } + + err = r.pollDownloadProgress(ctx, appMgr) + if err != nil { + return err + } + //time.Sleep(30 * time.Second) + //err = r.updateStatus(appMgr, appv1alpha1.Upgrading, nil, appv1alpha1.AppUpgrading, appv1alpha1.Upgrading.String()) + err = ops.Upgrade() if err != nil { @@ -692,6 +810,18 @@ func (r *ApplicationManagerController) cancel(appMgr *appv1alpha1.ApplicationMan return errors.New("can not execute cancel") } cancel() + var im appv1alpha1.ImageManager + err = r.Get(context.TODO(), types.NamespacedName{Name: appMgr.Name}, &im) + if err != nil { + return err + } + imCopy := im.DeepCopy() + imCopy.Status.State = "canceled" + err = r.Status().Patch(context.TODO(), imCopy, client.MergeFrom(&im)) + if err != nil { + return err + } + return r.updateStatus(appMgr, appv1alpha1.Canceling, nil, "", appMgr.Status.Message) } @@ -833,3 +963,114 @@ func (r *ApplicationManagerController) resumeAppAndWaitForLaunch(appMgr *appv1al } } } + +func (r *ApplicationManagerController) pollDownloadProgress(ctx context.Context, appMgr *appv1alpha1.ApplicationManager) error { + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + klog.Infof("poll images progress status.xxxxx") + var im appv1alpha1.ImageManager + err := r.Get(ctx, types.NamespacedName{Name: appMgr.Name}, &im) + if err != nil { + klog.Infof("Failed to get applicationmanagers name=%s err=%v", appMgr.Name, err) + return err + } + //klog.Infof("pollporgress before.... patch: conditions: %v", cur.Status.Conditions) + + if im.Status.State == appv1alpha1.Failed.String() { + return errors.New(im.Status.Message) + } + //if cur.Status.State == appv1alpha1.Canceled { + // return context.Canceled + //} + type progress struct { + sum float64 + count int + } + nodeMap := make(map[string]*progress) + for _, c := range im.Status.Conditions { + if _, ok := nodeMap[c.NodeName]; ok { + n, _ := strconv.ParseFloat(c.Progress, 64) + nodeMap[c.NodeName].sum += n + nodeMap[c.NodeName].count += 1 + } else { + n, _ := strconv.ParseFloat(c.Progress, 64) + nodeMap[c.NodeName] = &progress{sum: n, count: 1} + } + } + ret := math.MaxFloat64 + for _, p := range nodeMap { + if p.sum/float64(p.count) <= ret { + ret = p.sum / float64(p.count) + } + } + + var cur appv1alpha1.ApplicationManager + err = r.Get(ctx, types.NamespacedName{Name: appMgr.Name}, &cur) + if err != nil { + klog.Infof("patch error %v", err) + } + + appMgrCopy := cur.DeepCopy() + cur.Status.Progress = strconv.FormatFloat(ret, 'f', 2, 64) + err = r.Status().Patch(ctx, &cur, client.MergeFrom(appMgrCopy)) + if err != nil { + klog.Infof("patch error %v", err) + } + //var cur appv1alpha1.ApplicationManager + err = r.Get(ctx, types.NamespacedName{Name: appMgr.Name}, &cur) + //klog.Infof("pollporgress after patch: conditions: %v", cur.Status.Conditions) + + klog.Infof("download progress.... %v", cur.Status.Progress) + if cur.Status.Progress == "100.00" { + klog.Infof("download fkds;lfsdjl;f") + return nil + } + case <-ctx.Done(): + //var cur appv1alpha1.ApplicationManager + //err := r.Get(ctx, types.NamespacedName{Name: appMgr.Name}, &cur) + //if err != nil { + // return err + //} + //klog.Infof("") + //err = r.updateStatus(&cur, appv1alpha1.Canceled, nil, "", "cancelded") + //if err != nil { + // klog.Infof("Failed to update applicationmanagers err=%v", err) + //} + //return err + return context.Canceled + } + } +} + +func (r *ApplicationManagerController) createImageManager(ctx context.Context, appMgr *appv1alpha1.ApplicationManager, refs []string) error { + var im appv1alpha1.ImageManager + err := r.Get(ctx, types.NamespacedName{Name: appMgr.Name}, &im) + if err == nil { + err = r.Delete(ctx, &im) + if err != nil { + return err + } + } + + m := appv1alpha1.ImageManager{ + ObjectMeta: metav1.ObjectMeta{ + Name: appMgr.Name, + }, + Spec: appv1alpha1.ImageManagerSpec{ + AppName: appMgr.Spec.AppName, + AppNamespace: appMgr.Spec.AppNamespace, + AppOwner: appMgr.Spec.AppOwner, + Refs: refs, + }, + } + + err = r.Create(ctx, &m) + if err != nil { + return err + } + return nil +} diff --git a/controllers/image_controller.go b/controllers/image_controller.go new file mode 100644 index 0000000..5e08100 --- /dev/null +++ b/controllers/image_controller.go @@ -0,0 +1,252 @@ +package controllers + +// +import ( + "context" + "errors" + "sync" + + appv1alpha1 "bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1" + "bytetrade.io/web3os/app-service/pkg/images" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" +) + +// ImageManagerController represents a controller for managing the lifecycle of applicationmanager. +type ImageManagerController struct { + client.Client +} + +// SetupWithManager sets up the ImageManagerController with the provided controller manager +func (r *ImageManagerController) SetupWithManager(mgr ctrl.Manager) error { + c, err := controller.New("image-controller", mgr, controller.Options{ + Reconciler: r, + }) + if err != nil { + return err + } + + err = c.Watch( + &source.Kind{Type: &appv1alpha1.ImageManager{}}, + handler.EnqueueRequestsFromMapFunc( + func(h client.Object) []reconcile.Request { + app, ok := h.(*appv1alpha1.ImageManager) + if !ok { + return nil + } + return []reconcile.Request{{NamespacedName: types.NamespacedName{ + Name: app.Name, + Namespace: app.Spec.AppOwner, + }}} + }), + predicate.Funcs{ + CreateFunc: func(e event.CreateEvent) bool { + return r.preEnqueueCheckForCreate(e.Object) + }, + UpdateFunc: func(e event.UpdateEvent) bool { + return r.preEnqueueCheckForUpdate(e.ObjectOld, e.ObjectNew) + }, + DeleteFunc: func(e event.DeleteEvent) bool { + return false + }, + }, + ) + + if err != nil { + klog.Errorf("Failed to add watch err=%v", err) + return err + } + + return nil +} + +var imageManager map[string]context.CancelFunc + +// Reconcile implements the reconciliation loop for the ImageManagerController +func (r *ImageManagerController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + ctrl.Log.Info("reconcile image manager request", "name", req.Name) + + var im appv1alpha1.ImageManager + err := r.Get(ctx, req.NamespacedName, &im) + + if err != nil { + ctrl.Log.Error(err, "get application manager error", "name", req.Name) + if apierrors.IsNotFound(err) { + return ctrl.Result{}, nil + } + // unexpected error, retry after 5s + return ctrl.Result{}, err + } + r.reconcile(&im) + + return reconcile.Result{}, nil +} + +func (r *ImageManagerController) reconcile(instance *appv1alpha1.ImageManager) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + defer func() { + delete(imageManager, instance.Name) + }() + var err error + + var cur appv1alpha1.ImageManager + err = r.Get(ctx, types.NamespacedName{Name: instance.Name}, &cur) + if err != nil { + klog.Errorf("Failed to get imagemanagers name=%s err=%v", instance.Name, err) + return + } + + if imageManager == nil { + imageManager = make(map[string]context.CancelFunc) + } + if _, ok := imageManager[instance.Name]; ok { + return + } + imageManager[instance.Name] = cancel + + refs := cur.Spec.Refs + + conditions := make([]appv1alpha1.ImageProgress, 0) + + var nodes corev1.NodeList + err = r.List(ctx, &nodes, &client.ListOptions{}) + if err != nil { + klog.Infof("Failed to list err=%v", err) + return + } + for _, ref := range refs { + for _, node := range nodes.Items { + conditions = append(conditions, appv1alpha1.ImageProgress{ + NodeName: node.Name, + ImageRef: ref, + Progress: "0.00", + }) + } + } + now := metav1.Now() + curCopy := cur.DeepCopy() + curCopy.Status.Conditions = conditions + curCopy.Status.StatusTime = &now + curCopy.Status.UpdateTime = &now + curCopy.Status.State = appv1alpha1.Downloading.String() + err = r.Status().Patch(ctx, curCopy, client.MergeFrom(&cur)) + if err != nil { + klog.Error(err) + return + } + err = r.Get(ctx, types.NamespacedName{Name: instance.Name}, &cur) + if err != nil { + klog.Error(err) + return + } + + err = r.download(ctx, refs, images.PullOptions{AppName: instance.Spec.AppName, OwnerName: instance.Spec.AppOwner}) + if err != nil { + klog.Infof("download failed err=%v", err) + + state := appv1alpha1.Failed.String() + if errors.Is(err, context.Canceled) { + state = appv1alpha1.Canceled.String() + } + err = r.updateStatus(instance, state, err.Error()) + if err != nil { + klog.Infof("Failed to update status err=%v", err) + } + return + } + err = r.updateStatus(instance, appv1alpha1.Completed.String(), "success") + //err = r.install(ctx, instance) + if err != nil { + klog.Error(err) + } + + klog.Infof("download image success") + return +} + +func (r *ImageManagerController) preEnqueueCheckForCreate(obj client.Object) bool { + im, _ := obj.(*appv1alpha1.ImageManager) + klog.Infof("enqueue check: %v", im.Status) + if im.Status.State == appv1alpha1.Failed.String() || im.Status.State == appv1alpha1.Canceled.String() || im.Status.State == appv1alpha1.Completed.String() { + return false + } + return true +} + +func (r *ImageManagerController) preEnqueueCheckForUpdate(old, new client.Object) bool { + im, _ := new.(*appv1alpha1.ImageManager) + if im.Status.State == "canceled" { + go r.reconcile2(im) + } + return false +} +func (r *ImageManagerController) reconcile2(cur *appv1alpha1.ImageManager) { + r.cancel(cur) +} + +func (r *ImageManagerController) download(ctx context.Context, refs []string, opts images.PullOptions) (err error) { + var wg sync.WaitGroup + var errs []error + for _, ref := range refs { + wg.Add(1) + go func(ref string) { + defer wg.Done() + iClient, ctx, cancel := images.NewClientOrDie(ctx) + defer cancel() + _, err = iClient.PullImage(ctx, ref, opts) + if err != nil { + errs = append(errs, err) + klog.Infof("pull image failed name=%v err=%v", ref, err) + } + }(ref) + } + klog.Infof("waiting image %v to download", refs) + wg.Wait() + return err +} + +func (r *ImageManagerController) updateStatus(im *appv1alpha1.ImageManager, state string, message string) error { + var err error + err = r.Get(context.TODO(), types.NamespacedName{Name: im.Name}, im) + if err != nil { + return err + } + + now := metav1.Now() + imCopy := im.DeepCopy() + imCopy.Status.State = state + imCopy.Status.Message = message + imCopy.Status.StatusTime = &now + imCopy.Status.UpdateTime = &now + + err = r.Status().Patch(context.TODO(), imCopy, client.MergeFrom(im)) + if err != nil { + return err + } + return nil +} + +func (r *ImageManagerController) cancel(im *appv1alpha1.ImageManager) error { + cancel, ok := imageManager[im.Name] + if !ok { + return errors.New("can not execute cancel") + } + cancel() + return nil +} diff --git a/go.mod b/go.mod index 3cb5c7e..a8b85dd 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.5 require ( github.com/Masterminds/semver/v3 v3.2.0 github.com/argoproj/argo-workflows/v3 v3.5.5 + github.com/containerd/containerd v1.7.0 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/emicklei/go-restful-openapi/v2 v2.9.1 github.com/emicklei/go-restful/v3 v3.10.1 @@ -22,6 +23,8 @@ require ( github.com/json-iterator/go v1.1.12 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.23.0 + github.com/opencontainers/go-digest v1.0.0 + github.com/opencontainers/image-spec v1.1.0-rc3 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.16.0 github.com/prometheus/common v0.42.0 @@ -50,6 +53,7 @@ require ( cloud.google.com/go/iam v1.1.3 // indirect cloud.google.com/go/storage v1.35.1 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect + github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.27 // indirect @@ -57,11 +61,13 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/BurntSushi/toml v1.1.0 // indirect + github.com/BurntSushi/toml v1.2.1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/hcsshim v0.10.0-rc.7 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect github.com/aws/aws-sdk-go v1.45.1 // indirect @@ -72,7 +78,13 @@ require ( github.com/chai2010/gettext-go v1.0.2 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 // indirect - github.com/containerd/containerd v1.7.0 // indirect + github.com/containerd/cgroups v1.1.0 // indirect + github.com/containerd/console v1.0.3 // indirect + github.com/containerd/continuity v0.3.0 // indirect + github.com/containerd/fifo v1.1.0 // indirect + github.com/containerd/ttrpc v1.2.1 // indirect + github.com/containerd/typeurl/v2 v2.1.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/cli v24.0.7+incompatible // indirect @@ -80,6 +92,7 @@ require ( github.com/docker/docker v24.0.0+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect @@ -135,6 +148,9 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect + github.com/moby/sys/mountinfo v0.6.2 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/signal v0.7.0 // indirect github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -142,20 +158,24 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc3 // indirect + github.com/opencontainers/runc v1.1.4 // indirect + github.com/opencontainers/runtime-spec v1.1.0-rc.1 // indirect + github.com/opencontainers/selinux v1.11.0 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/rubenv/sql-migrate v1.1.2 // indirect github.com/russross/blackfriday v1.6.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/ulikunitz/xz v0.5.10 // indirect + github.com/urfave/cli v1.22.12 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect diff --git a/go.sum b/go.sum index 60d51d4..c74a3ad 100644 --- a/go.sum +++ b/go.sum @@ -187,6 +187,8 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= +github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652 h1:+vTEFqeoeur6XSq06bs+roX3YiT49gUniJK7Zky7Xjg= +github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= @@ -206,8 +208,8 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= @@ -224,7 +226,9 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/hcsshim v0.10.0-rc.7 h1:HBytQPxcv8Oy4244zbQbe6hnOnx544eL5QPUqhJldz8= +github.com/Microsoft/hcsshim v0.10.0-rc.7/go.mod h1:ILuwjA+kNW+MrN/w5un7n3mTqkwsFu4Bp05/okFUZlE= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -268,10 +272,12 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= +github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= @@ -282,15 +288,28 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg= github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= +github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= +github.com/containerd/ttrpc v1.2.1 h1:VWv/Rzx023TBLv4WQ+9WPXlBG/s3rsRjY3i9AJ2BJdE= +github.com/containerd/ttrpc v1.2.1/go.mod h1:sIT6l32Ph/H9cvnJsfXM5drIVzTr5A2flTf1G5tYZak= +github.com/containerd/typeurl/v2 v2.1.0 h1:yNAhJvbNEANt7ck48IlEGOxP7YAp6LLpGn5jZACDNIE= +github.com/containerd/typeurl/v2 v2.1.0/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.20 h1:VIPb/a2s17qNeQgDnkfZC35RScx+blkKF8GV68n80J4= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -312,8 +331,10 @@ github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNk github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= @@ -340,6 +361,7 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -396,6 +418,7 @@ github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXs github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -671,7 +694,13 @@ github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= +github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI= +github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -685,6 +714,7 @@ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -707,8 +737,18 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg= +github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.1.0-rc.1 h1:wHa9jroFfKGQqFHj0I1fMRKLl0pfj+ynAqBxo3v6u9w= +github.com/opencontainers/runtime-spec v1.1.0-rc.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= +github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= @@ -741,6 +781,7 @@ github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -752,9 +793,11 @@ github.com/rubenv/sql-migrate v1.1.2/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMH github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -794,10 +837,16 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw= github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= +github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -944,6 +993,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= @@ -1025,6 +1075,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1033,6 +1084,7 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1057,6 +1109,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1074,8 +1127,11 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/apiserver/handler_app.go b/pkg/apiserver/handler_app.go index aeb7fb9..f6ef3e0 100644 --- a/pkg/apiserver/handler_app.go +++ b/pkg/apiserver/handler_app.go @@ -185,6 +185,7 @@ func (h *Handler) operate(req *restful.Request, resp *restful.Response) { Message: am.Status.Message, CreationTimestamp: am.CreationTimestamp, Source: am.Spec.Source, + Progress: am.Status.Progress, } resp.WriteAsJson(operate) @@ -219,6 +220,7 @@ func (h *Handler) appsOperate(req *restful.Request, resp *restful.Response) { Message: am.Status.Message, CreationTimestamp: am.CreationTimestamp, Source: am.Spec.Source, + Progress: am.Status.Progress, } filteredOperates = append(filteredOperates, operate) } @@ -385,7 +387,8 @@ func (h *Handler) getApp(req *restful.Request, resp *restful.Response) { return } - if apierrors.IsNotFound(err) && am.Status.State != v1alpha1.Pending && am.Status.State != v1alpha1.Installing { + if apierrors.IsNotFound(err) && am.Status.State != v1alpha1.Pending && + am.Status.State != v1alpha1.Installing && am.Status.State != v1alpha1.Downloading { api.HandleNotFound(resp, req, err) return } @@ -424,8 +427,10 @@ func (h *Handler) apps(req *restful.Request, resp *restful.Response) { continue } - if am.Spec.AppOwner == owner && (am.Status.State == v1alpha1.Pending || am.Status.State == v1alpha1.Installing) { - if !stateSet.Has(v1alpha1.Pending.String()) || !stateSet.Has(v1alpha1.Installing.String()) { + if am.Spec.AppOwner == owner && (am.Status.State == v1alpha1.Pending || am.Status.State == v1alpha1.Installing || + am.Status.State == v1alpha1.Downloading) { + if !stateSet.Has(v1alpha1.Pending.String()) || !stateSet.Has(v1alpha1.Installing.String()) || + !stateSet.Has(v1alpha1.Downloading.String()) { continue } if len(isSysApp) > 0 && isSysApp == "true" { diff --git a/pkg/apiserver/handler_installer.go b/pkg/apiserver/handler_installer.go index 09152e8..31ff571 100644 --- a/pkg/apiserver/handler_installer.go +++ b/pkg/apiserver/handler_installer.go @@ -51,7 +51,7 @@ func (h *Handler) install(req *restful.Request, resp *restful.Response) { return } - appconfig, err := GetAppConfig(req.Request.Context(), app, owner, + appconfig, _, err := GetAppConfig(req.Request.Context(), app, owner, insReq.CfgURL, insReq.RepoURL, "", token) if err != nil { klog.Errorf("Failed to get appconfig err=%v", err) @@ -168,6 +168,7 @@ func (h *Handler) install(req *restful.Request, resp *restful.Response) { "repoURL": insReq.RepoURL, "version": appconfig.Version, }, + Progress: "0.00", StatusTime: &now, UpdateTime: &now, } @@ -194,6 +195,7 @@ func (h *Handler) uninstall(req *restful.Request, resp *restful.Response) { Payload: map[string]string{ "token": token, }, + Progress: "0.00", StatusTime: &now, UpdateTime: &now, } @@ -220,6 +222,7 @@ func (h *Handler) cancel(req *restful.Request, resp *restful.Response) { now := metav1.Now() status := v1alpha1.ApplicationManagerStatus{ OpType: v1alpha1.CancelOp, + Progress: "0.00", Message: cancelType, StatusTime: &now, UpdateTime: &now, @@ -282,25 +285,26 @@ func (h *Handler) checkDependencies(req *restful.Request, resp *restful.Response } // GetAppConfig get app installation configuration from app store -func GetAppConfig(ctx context.Context, app, owner, cfgURL, repoURL, version, token string) (*appinstaller.ApplicationConfig, error) { +func GetAppConfig(ctx context.Context, app, owner, cfgURL, repoURL, version, token string) (*appinstaller.ApplicationConfig, string, error) { if repoURL == "" { - return nil, fmt.Errorf("url info is empty, cfg [%s], repo [%s]", cfgURL, repoURL) + return nil, "", fmt.Errorf("url info is empty, cfg [%s], repo [%s]", cfgURL, repoURL) } var ( - appcfg *appinstaller.ApplicationConfig - err error + appcfg *appinstaller.ApplicationConfig + chartPath string + err error ) if cfgURL != "" { - appcfg, err = getAppConfigFromURL(ctx, app, cfgURL) + appcfg, chartPath, err = getAppConfigFromURL(ctx, app, cfgURL) if err != nil { - return nil, err + return nil, "", err } } else { - appcfg, err = getAppConfigFromRepo(ctx, app, repoURL, version, token) + appcfg, chartPath, err = getAppConfigFromRepo(ctx, app, repoURL, version, token) if err != nil { - return nil, err + return nil, chartPath, err } } @@ -309,57 +313,57 @@ func GetAppConfig(ctx context.Context, app, owner, cfgURL, repoURL, version, tok appcfg.Namespace = namespace appcfg.OwnerName = owner appcfg.RepoURL = repoURL - return appcfg, nil + return appcfg, chartPath, nil } -func getAppConfigFromConfigurationFile(app, chart string) (*appinstaller.ApplicationConfig, error) { +func getAppConfigFromConfigurationFile(app, chart string) (*appinstaller.ApplicationConfig, string, error) { f, err := os.Open(filepath.Join(chart, AppCfgFileName)) if err != nil { - return nil, err + return nil, chart, err } defer f.Close() data, err := ioutil.ReadAll(f) if err != nil { - return nil, err + return nil, chart, err } var cfg appinstaller.AppConfiguration if err := yaml.Unmarshal(data, &cfg); err != nil { - return nil, err + return nil, chart, err } return toApplicationConfig(app, chart, &cfg) } -func getAppConfigFromURL(ctx context.Context, app, url string) (*appinstaller.ApplicationConfig, error) { +func getAppConfigFromURL(ctx context.Context, app, url string) (*appinstaller.ApplicationConfig, string, error) { client := resty.New().SetTimeout(2 * time.Second) resp, err := client.R().Get(url) if err != nil { - return nil, err + return nil, "", err } if resp.StatusCode() >= 400 { - return nil, fmt.Errorf("app config url returns unexpected status code, %d", resp.StatusCode()) + return nil, "", fmt.Errorf("app config url returns unexpected status code, %d", resp.StatusCode()) } var cfg appinstaller.AppConfiguration if err := yaml.Unmarshal(resp.Body(), &cfg); err != nil { - return nil, err + return nil, "", err } return toApplicationConfig(app, app, &cfg) } -func getAppConfigFromRepo(ctx context.Context, app, repoURL, version, token string) (*appinstaller.ApplicationConfig, error) { +func getAppConfigFromRepo(ctx context.Context, app, repoURL, version, token string) (*appinstaller.ApplicationConfig, string, error) { chartPath, err := GetIndexAndDownloadChart(ctx, app, repoURL, version, token) if err != nil { - return nil, err + return nil, chartPath, err } return getAppConfigFromConfigurationFile(app, chartPath) } -func toApplicationConfig(app, chart string, cfg *appinstaller.AppConfiguration) (*appinstaller.ApplicationConfig, error) { +func toApplicationConfig(app, chart string, cfg *appinstaller.AppConfiguration) (*appinstaller.ApplicationConfig, string, error) { var permission []appinstaller.AppPermission if cfg.Permission.AppData { permission = append(permission, appinstaller.AppDataRW) @@ -385,7 +389,7 @@ func toApplicationConfig(app, chart string, cfg *appinstaller.AppConfiguration) } valuePtr := func(v resource.Quantity, err error) (*resource.Quantity, error) { - if err == resource.ErrFormatWrong { + if errors.Is(err, resource.ErrFormatWrong) { return nil, nil } @@ -394,22 +398,22 @@ func toApplicationConfig(app, chart string, cfg *appinstaller.AppConfiguration) mem, err := valuePtr(resource.ParseQuantity(cfg.Spec.RequiredMemory)) if err != nil { - return nil, err + return nil, chart, err } disk, err := valuePtr(resource.ParseQuantity(cfg.Spec.RequiredDisk)) if err != nil { - return nil, err + return nil, chart, err } cpu, err := valuePtr(resource.ParseQuantity(cfg.Spec.RequiredCPU)) if err != nil { - return nil, err + return nil, chart, err } gpu, err := valuePtr(resource.ParseQuantity(cfg.Spec.RequiredGPU)) if err != nil { - return nil, err + return nil, chart, err } // transform from Policy to AppPolicy @@ -428,7 +432,7 @@ func toApplicationConfig(app, chart string, cfg *appinstaller.AppConfiguration) // check dependencies version format for _, dep := range cfg.Options.Dependencies { if err = checkVersionFormat(dep.Version); err != nil { - return nil, err + return nil, chart, err } } @@ -447,13 +451,13 @@ func toApplicationConfig(app, chart string, cfg *appinstaller.AppConfiguration) if err != nil { klog.Errorf("Failed to get or parse zinc index json file err=%v", err) - return nil, err + return nil, chart, err } } } if cfg.Middleware != nil && cfg.Middleware.Redis != nil { if len(cfg.Middleware.Redis.Namespace) == 0 { - return nil, errors.New("middleware of Redis namespace can not be empty") + return nil, chart, errors.New("middleware of Redis namespace can not be empty") } } var appid string @@ -487,7 +491,7 @@ func toApplicationConfig(app, chart string, cfg *appinstaller.AppConfiguration) AppScope: cfg.Options.AppScope, WsConfig: cfg.Options.WsConfig, Upload: cfg.Options.Upload, - }, nil + }, chart, nil } func (h *Handler) installRecommend(req *restful.Request, resp *restful.Response) { diff --git a/pkg/apiserver/handler_model.go b/pkg/apiserver/handler_model.go index 93fd765..a1d31c3 100644 --- a/pkg/apiserver/handler_model.go +++ b/pkg/apiserver/handler_model.go @@ -377,7 +377,7 @@ func (h *Handler) resumeHandler(req *restful.Request, resp *restful.Response) { }() client := resty.New() - response, err := client.R().Post(fmt.Sprintf("http://dify.user-space-%s/nitro/model/%s/load", owner, modelID)) + response, err := client.SetTimeout(60 * time.Second).R().Post(fmt.Sprintf("http://dify.user-space-%s/nitro/model/%s/load", owner, modelID)) if err != nil { api.HandleError(resp, req, err) return diff --git a/pkg/apiserver/webservice.go b/pkg/apiserver/webservice.go index f7edd08..79e34b8 100644 --- a/pkg/apiserver/webservice.go +++ b/pkg/apiserver/webservice.go @@ -426,11 +426,11 @@ func addServiceToContainer(c *restful.Container, handler *Handler) error { ws.Route(ws.GET("/apps/{"+ParamAppName+"}"). To(handler.getApp). - Doc("get list of app"). + Doc("get an app"). Metadata(restfulspec.KeyOpenAPITags, MODULE_TAGS). Param(ws.PathParameter(ParamAppName, "the name of application")). Param(ws.HeaderParameter("X-Authorization", "Auth token")). - Returns(http.StatusOK, "success to get list of app", nil)) + Returns(http.StatusOK, "success to get an app", nil)) ws.Route(ws.GET("/apps/pending-installing/task"). To(handler.pendingOrInstallingApps). diff --git a/pkg/appinstaller/helm.go b/pkg/appinstaller/helm.go index 3dfc669..26bbf78 100644 --- a/pkg/appinstaller/helm.go +++ b/pkg/appinstaller/helm.go @@ -144,6 +144,7 @@ func (h *HelmOps) Install() error { if !ok { // install operation has been canceled, so to uninstall it. h.Uninstall() + //return context.Canceled return errors.New("canceled") } klog.Infof("app: %s launched success", h.app.AppName) @@ -288,7 +289,6 @@ func (h *HelmOps) setValues() (values map[string]interface{}, err error) { } entries := make(map[string]interface{}) - for i, entrance := range h.app.Entrances { var url string if len(h.app.Entrances) == 1 { diff --git a/pkg/generated/clientset/versioned/fake/clientset_generated.go b/pkg/generated/clientset/versioned/fake/clientset_generated.go index c72dd92..6a6d611 100644 --- a/pkg/generated/clientset/versioned/fake/clientset_generated.go +++ b/pkg/generated/clientset/versioned/fake/clientset_generated.go @@ -1,3 +1,5 @@ +// Code generated by client-gen. DO NOT EDIT. + package fake import ( diff --git a/pkg/generated/clientset/versioned/fake/doc.go b/pkg/generated/clientset/versioned/fake/doc.go index 4edd3fa..3630ed1 100644 --- a/pkg/generated/clientset/versioned/fake/doc.go +++ b/pkg/generated/clientset/versioned/fake/doc.go @@ -1,2 +1,4 @@ +// Code generated by client-gen. DO NOT EDIT. + // This package has the automatically generated fake clientset. package fake diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go index 4a92882..eb73925 100644 --- a/pkg/generated/clientset/versioned/fake/register.go +++ b/pkg/generated/clientset/versioned/fake/register.go @@ -1,3 +1,5 @@ +// Code generated by client-gen. DO NOT EDIT. + package fake import ( @@ -19,14 +21,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/pkg/generated/clientset/versioned/scheme/doc.go b/pkg/generated/clientset/versioned/scheme/doc.go index d6cdaa7..14db57a 100644 --- a/pkg/generated/clientset/versioned/scheme/doc.go +++ b/pkg/generated/clientset/versioned/scheme/doc.go @@ -1,2 +1,4 @@ +// Code generated by client-gen. DO NOT EDIT. + // This package contains the scheme of the automatically generated clientset. package scheme diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go index fd6c7f3..eff27e9 100644 --- a/pkg/generated/clientset/versioned/scheme/register.go +++ b/pkg/generated/clientset/versioned/scheme/register.go @@ -21,14 +21,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/app.bytetrade.io_client.go b/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/app.bytetrade.io_client.go index f21d515..45d24c3 100644 --- a/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/app.bytetrade.io_client.go +++ b/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/app.bytetrade.io_client.go @@ -14,6 +14,7 @@ type AppV1alpha1Interface interface { RESTClient() rest.Interface ApplicationsGetter ApplicationManagersGetter + ImageManagersGetter } // AppV1alpha1Client is used to interact with features provided by the app.bytetrade.io group. @@ -29,6 +30,10 @@ func (c *AppV1alpha1Client) ApplicationManagers() ApplicationManagerInterface { return newApplicationManagers(c) } +func (c *AppV1alpha1Client) ImageManagers() ImageManagerInterface { + return newImageManagers(c) +} + // NewForConfig creates a new AppV1alpha1Client for the given config. // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), // where httpClient was generated with rest.HTTPClientFor(c). diff --git a/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/fake/fake_app.bytetrade.io_client.go b/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/fake/fake_app.bytetrade.io_client.go index 47440d5..2d8d624 100644 --- a/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/fake/fake_app.bytetrade.io_client.go +++ b/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/fake/fake_app.bytetrade.io_client.go @@ -20,6 +20,10 @@ func (c *FakeAppV1alpha1) ApplicationManagers() v1alpha1.ApplicationManagerInter return &FakeApplicationManagers{c} } +func (c *FakeAppV1alpha1) ImageManagers() v1alpha1.ImageManagerInterface { + return &FakeImageManagers{c} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeAppV1alpha1) RESTClient() rest.Interface { diff --git a/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/fake/fake_imagemanager.go b/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/fake/fake_imagemanager.go new file mode 100644 index 0000000..0e19694 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/fake/fake_imagemanager.go @@ -0,0 +1,117 @@ +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeImageManagers implements ImageManagerInterface +type FakeImageManagers struct { + Fake *FakeAppV1alpha1 +} + +var imagemanagersResource = schema.GroupVersionResource{Group: "app.bytetrade.io", Version: "v1alpha1", Resource: "imagemanagers"} + +var imagemanagersKind = schema.GroupVersionKind{Group: "app.bytetrade.io", Version: "v1alpha1", Kind: "ImageManager"} + +// Get takes name of the imageManager, and returns the corresponding imageManager object, and an error if there is any. +func (c *FakeImageManagers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ImageManager, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(imagemanagersResource, name), &v1alpha1.ImageManager{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ImageManager), err +} + +// List takes label and field selectors, and returns the list of ImageManagers that match those selectors. +func (c *FakeImageManagers) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ImageManagerList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(imagemanagersResource, imagemanagersKind, opts), &v1alpha1.ImageManagerList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.ImageManagerList{ListMeta: obj.(*v1alpha1.ImageManagerList).ListMeta} + for _, item := range obj.(*v1alpha1.ImageManagerList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested imageManagers. +func (c *FakeImageManagers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(imagemanagersResource, opts)) +} + +// Create takes the representation of a imageManager and creates it. Returns the server's representation of the imageManager, and an error, if there is any. +func (c *FakeImageManagers) Create(ctx context.Context, imageManager *v1alpha1.ImageManager, opts v1.CreateOptions) (result *v1alpha1.ImageManager, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(imagemanagersResource, imageManager), &v1alpha1.ImageManager{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ImageManager), err +} + +// Update takes the representation of a imageManager and updates it. Returns the server's representation of the imageManager, and an error, if there is any. +func (c *FakeImageManagers) Update(ctx context.Context, imageManager *v1alpha1.ImageManager, opts v1.UpdateOptions) (result *v1alpha1.ImageManager, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(imagemanagersResource, imageManager), &v1alpha1.ImageManager{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ImageManager), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeImageManagers) UpdateStatus(ctx context.Context, imageManager *v1alpha1.ImageManager, opts v1.UpdateOptions) (*v1alpha1.ImageManager, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(imagemanagersResource, "status", imageManager), &v1alpha1.ImageManager{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ImageManager), err +} + +// Delete takes name of the imageManager and deletes it. Returns an error if one occurs. +func (c *FakeImageManagers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteActionWithOptions(imagemanagersResource, name, opts), &v1alpha1.ImageManager{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeImageManagers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(imagemanagersResource, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.ImageManagerList{}) + return err +} + +// Patch applies the patch and returns the patched imageManager. +func (c *FakeImageManagers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ImageManager, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(imagemanagersResource, name, pt, data, subresources...), &v1alpha1.ImageManager{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ImageManager), err +} diff --git a/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/generated_expansion.go index c5ed3e0..2157c86 100644 --- a/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/generated_expansion.go +++ b/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/generated_expansion.go @@ -5,3 +5,5 @@ package v1alpha1 type ApplicationExpansion interface{} type ApplicationManagerExpansion interface{} + +type ImageManagerExpansion interface{} diff --git a/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/imagemanager.go b/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/imagemanager.go new file mode 100644 index 0000000..53b3ca6 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/app.bytetrade.io/v1alpha1/imagemanager.go @@ -0,0 +1,168 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1" + scheme "bytetrade.io/web3os/app-service/pkg/generated/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ImageManagersGetter has a method to return a ImageManagerInterface. +// A group's client should implement this interface. +type ImageManagersGetter interface { + ImageManagers() ImageManagerInterface +} + +// ImageManagerInterface has methods to work with ImageManager resources. +type ImageManagerInterface interface { + Create(ctx context.Context, imageManager *v1alpha1.ImageManager, opts v1.CreateOptions) (*v1alpha1.ImageManager, error) + Update(ctx context.Context, imageManager *v1alpha1.ImageManager, opts v1.UpdateOptions) (*v1alpha1.ImageManager, error) + UpdateStatus(ctx context.Context, imageManager *v1alpha1.ImageManager, opts v1.UpdateOptions) (*v1alpha1.ImageManager, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ImageManager, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ImageManagerList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ImageManager, err error) + ImageManagerExpansion +} + +// imageManagers implements ImageManagerInterface +type imageManagers struct { + client rest.Interface +} + +// newImageManagers returns a ImageManagers +func newImageManagers(c *AppV1alpha1Client) *imageManagers { + return &imageManagers{ + client: c.RESTClient(), + } +} + +// Get takes name of the imageManager, and returns the corresponding imageManager object, and an error if there is any. +func (c *imageManagers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ImageManager, err error) { + result = &v1alpha1.ImageManager{} + err = c.client.Get(). + Resource("imagemanagers"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ImageManagers that match those selectors. +func (c *imageManagers) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ImageManagerList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.ImageManagerList{} + err = c.client.Get(). + Resource("imagemanagers"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested imageManagers. +func (c *imageManagers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("imagemanagers"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a imageManager and creates it. Returns the server's representation of the imageManager, and an error, if there is any. +func (c *imageManagers) Create(ctx context.Context, imageManager *v1alpha1.ImageManager, opts v1.CreateOptions) (result *v1alpha1.ImageManager, err error) { + result = &v1alpha1.ImageManager{} + err = c.client.Post(). + Resource("imagemanagers"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(imageManager). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a imageManager and updates it. Returns the server's representation of the imageManager, and an error, if there is any. +func (c *imageManagers) Update(ctx context.Context, imageManager *v1alpha1.ImageManager, opts v1.UpdateOptions) (result *v1alpha1.ImageManager, err error) { + result = &v1alpha1.ImageManager{} + err = c.client.Put(). + Resource("imagemanagers"). + Name(imageManager.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(imageManager). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *imageManagers) UpdateStatus(ctx context.Context, imageManager *v1alpha1.ImageManager, opts v1.UpdateOptions) (result *v1alpha1.ImageManager, err error) { + result = &v1alpha1.ImageManager{} + err = c.client.Put(). + Resource("imagemanagers"). + Name(imageManager.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(imageManager). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the imageManager and deletes it. Returns an error if one occurs. +func (c *imageManagers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Resource("imagemanagers"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *imageManagers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("imagemanagers"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched imageManager. +func (c *imageManagers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ImageManager, err error) { + result = &v1alpha1.ImageManager{} + err = c.client.Patch(pt). + Resource("imagemanagers"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/generated/informers/externalversions/app.bytetrade.io/v1alpha1/imagemanager.go b/pkg/generated/informers/externalversions/app.bytetrade.io/v1alpha1/imagemanager.go new file mode 100644 index 0000000..34d6026 --- /dev/null +++ b/pkg/generated/informers/externalversions/app.bytetrade.io/v1alpha1/imagemanager.go @@ -0,0 +1,73 @@ +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + appbytetradeiov1alpha1 "bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1" + versioned "bytetrade.io/web3os/app-service/pkg/generated/clientset/versioned" + internalinterfaces "bytetrade.io/web3os/app-service/pkg/generated/informers/externalversions/internalinterfaces" + v1alpha1 "bytetrade.io/web3os/app-service/pkg/generated/listers/app.bytetrade.io/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ImageManagerInformer provides access to a shared informer and lister for +// ImageManagers. +type ImageManagerInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.ImageManagerLister +} + +type imageManagerInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewImageManagerInformer constructs a new informer for ImageManager type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewImageManagerInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredImageManagerInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredImageManagerInformer constructs a new informer for ImageManager type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredImageManagerInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AppV1alpha1().ImageManagers().List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AppV1alpha1().ImageManagers().Watch(context.TODO(), options) + }, + }, + &appbytetradeiov1alpha1.ImageManager{}, + resyncPeriod, + indexers, + ) +} + +func (f *imageManagerInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredImageManagerInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *imageManagerInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&appbytetradeiov1alpha1.ImageManager{}, f.defaultInformer) +} + +func (f *imageManagerInformer) Lister() v1alpha1.ImageManagerLister { + return v1alpha1.NewImageManagerLister(f.Informer().GetIndexer()) +} diff --git a/pkg/generated/informers/externalversions/app.bytetrade.io/v1alpha1/interface.go b/pkg/generated/informers/externalversions/app.bytetrade.io/v1alpha1/interface.go index ea433e3..2cff02b 100644 --- a/pkg/generated/informers/externalversions/app.bytetrade.io/v1alpha1/interface.go +++ b/pkg/generated/informers/externalversions/app.bytetrade.io/v1alpha1/interface.go @@ -12,6 +12,8 @@ type Interface interface { Applications() ApplicationInformer // ApplicationManagers returns a ApplicationManagerInformer. ApplicationManagers() ApplicationManagerInformer + // ImageManagers returns a ImageManagerInformer. + ImageManagers() ImageManagerInformer } type version struct { @@ -34,3 +36,8 @@ func (v *version) Applications() ApplicationInformer { func (v *version) ApplicationManagers() ApplicationManagerInformer { return &applicationManagerInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } + +// ImageManagers returns a ImageManagerInformer. +func (v *version) ImageManagers() ImageManagerInformer { + return &imageManagerInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index 6c39b01..1af27d4 100644 --- a/pkg/generated/informers/externalversions/generic.go +++ b/pkg/generated/informers/externalversions/generic.go @@ -41,6 +41,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.App().V1alpha1().Applications().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("applicationmanagers"): return &genericInformer{resource: resource.GroupResource(), informer: f.App().V1alpha1().ApplicationManagers().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("imagemanagers"): + return &genericInformer{resource: resource.GroupResource(), informer: f.App().V1alpha1().ImageManagers().Informer()}, nil } diff --git a/pkg/generated/listers/app.bytetrade.io/v1alpha1/expansion_generated.go b/pkg/generated/listers/app.bytetrade.io/v1alpha1/expansion_generated.go index c16ed84..1d4654d 100644 --- a/pkg/generated/listers/app.bytetrade.io/v1alpha1/expansion_generated.go +++ b/pkg/generated/listers/app.bytetrade.io/v1alpha1/expansion_generated.go @@ -9,3 +9,7 @@ type ApplicationListerExpansion interface{} // ApplicationManagerListerExpansion allows custom methods to be added to // ApplicationManagerLister. type ApplicationManagerListerExpansion interface{} + +// ImageManagerListerExpansion allows custom methods to be added to +// ImageManagerLister. +type ImageManagerListerExpansion interface{} diff --git a/pkg/generated/listers/app.bytetrade.io/v1alpha1/imagemanager.go b/pkg/generated/listers/app.bytetrade.io/v1alpha1/imagemanager.go new file mode 100644 index 0000000..665a0a8 --- /dev/null +++ b/pkg/generated/listers/app.bytetrade.io/v1alpha1/imagemanager.go @@ -0,0 +1,52 @@ +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ImageManagerLister helps list ImageManagers. +// All objects returned here must be treated as read-only. +type ImageManagerLister interface { + // List lists all ImageManagers in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.ImageManager, err error) + // Get retrieves the ImageManager from the index for a given name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.ImageManager, error) + ImageManagerListerExpansion +} + +// imageManagerLister implements the ImageManagerLister interface. +type imageManagerLister struct { + indexer cache.Indexer +} + +// NewImageManagerLister returns a new ImageManagerLister. +func NewImageManagerLister(indexer cache.Indexer) ImageManagerLister { + return &imageManagerLister{indexer: indexer} +} + +// List lists all ImageManagers in the indexer. +func (s *imageManagerLister) List(selector labels.Selector) (ret []*v1alpha1.ImageManager, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ImageManager)) + }) + return ret, err +} + +// Get retrieves the ImageManager from the index for a given name. +func (s *imageManagerLister) Get(name string) (*v1alpha1.ImageManager, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("imagemanager"), name) + } + return obj.(*v1alpha1.ImageManager), nil +} diff --git a/pkg/images/jobs.go b/pkg/images/jobs.go new file mode 100644 index 0000000..6ab1b57 --- /dev/null +++ b/pkg/images/jobs.go @@ -0,0 +1,260 @@ +package images + +import ( + "context" + "os" + "strconv" + "sync" + "time" + + "bytetrade.io/web3os/app-service/pkg/utils" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" + refdocker "github.com/containerd/containerd/reference/docker" + "github.com/containerd/containerd/remotes" + "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/util/retry" + "k8s.io/klog/v2" +) + +// StatusInfo holds the status info for an upload or download +type StatusInfo struct { + Ref string + Status string + Offset int64 + Total int64 + StartedAt time.Time + UpdatedAt time.Time +} + +func showProgress(ctx context.Context, ongoing *jobs, cs content.Store, opts PullOptions) { + var ( + ticker = time.NewTicker(1 * time.Second) + start = time.Now() + statuses = map[string]StatusInfo{} + done bool + ordered []StatusInfo + ) + defer ticker.Stop() + +outer: + for { + select { + case <-ticker.C: + resolved := "resolved" + if !ongoing.isResolved() { + resolved = "resolving" + } + statuses[ongoing.name] = StatusInfo{ + Ref: ongoing.name, + Status: resolved, + } + + keys := []string{ongoing.name} + + activeSeen := map[string]struct{}{} + if !done { + active, err := cs.ListStatuses(ctx, "") + if err != nil { + klog.ErrorS(err, "active check failed") + continue + } + // update status of active entries! + for _, a := range active { + statuses[a.Ref] = StatusInfo{ + Ref: a.Ref, + Status: "downloading", + Offset: a.Offset, + Total: a.Total, + StartedAt: a.StartedAt, + UpdatedAt: a.UpdatedAt, + } + activeSeen[a.Ref] = struct{}{} + } + } + + // now, update the items in jobs that are not in active + for _, j := range ongoing.jobs() { + key := remotes.MakeRefKey(ctx, j) + keys = append(keys, key) + if _, ok := activeSeen[key]; ok { + continue + } + + status, ok := statuses[key] + if !done && (!ok || status.Status == "downloading") { + info, err := cs.Info(ctx, j.Digest) + if err != nil { + if !errdefs.IsNotFound(err) { + klog.Errorf("Failed to get content info err=%v", err) + continue outer + } else { + statuses[key] = StatusInfo{ + Ref: key, + Status: "waiting", + } + } + } else if info.CreatedAt.After(start) { + statuses[key] = StatusInfo{ + Ref: key, + Status: "done", + Offset: info.Size, + Total: info.Size, + UpdatedAt: info.CreatedAt, + } + } else { + statuses[key] = StatusInfo{ + Ref: key, + Status: "exists", + } + } + } else if done { + if ok { + if status.Status != "done" && status.Status != "exists" { + status.Status = "done" + statuses[key] = status + } + } else { + statuses[key] = StatusInfo{ + Ref: key, + Status: "done", + } + } + } + } + + ordered = []StatusInfo{} + for _, key := range keys { + ordered = append(ordered, statuses[key]) + } + klog.Infof("downloading image %v", ongoing.name) + err := updateProgress(ordered, ongoing.name, opts) + if err != nil { + klog.Infof("update progress failed err=%v", err) + + } + //tw.Flush() + + if done { + klog.Infof("progress is done") + return + } + case <-ctx.Done(): + done = true // allow ui to update once more + } + } +} + +func updateProgress(statuses []StatusInfo, imageName string, opts PullOptions) error { + client, err := utils.GetClient() + if err != nil { + return err + } + var offset, size int64 + var progress float64 + klog.Infof("in updateProgress: %#v", statuses) + count := 0 + for _, status := range statuses { + switch status.Status { + case "downloading", "uploading": + size += status.Total + offset += status.Offset + case "resolving", "waiting", "resolved": + //if all status is resolving waiting, or resolved use last progress + count++ + progress = 0 + default: + progress = 100 + } + } + if count == len(statuses) { + + } + if size > 0 { + progress = float64(offset) / float64(size) * float64(100) + } + klog.Infof("download image %s progress=%v", imageName, progress) + + err = retry.RetryOnConflict(retry.DefaultRetry, func() error { + im, err := client.AppV1alpha1().ImageManagers().Get(context.TODO(), utils.FmtAppMgrName(opts.AppName, opts.OwnerName), metav1.GetOptions{}) + if err != nil { + klog.Infof("cannot found image manager err=%v", err) + return err + } + + now := metav1.Now() + imCopy := im.DeepCopy() + status := imCopy.Status + status.StatusTime = &now + status.UpdateTime = &now + for i, c := range status.Conditions { + named, _ := refdocker.ParseDockerRef(c.ImageRef) + if c.NodeName == os.Getenv("NODE_NAME") && named.String() == imageName { + status.Conditions[i].Progress = strconv.FormatFloat(progress, 'f', 2, 64) + } + } + imCopy.Status = status + + _, err = client.AppV1alpha1().ImageManagers().UpdateStatus(context.TODO(), imCopy, metav1.UpdateOptions{}) + if err != nil { + klog.Infof("update imagemanager name=%s status err=%v", imCopy.Name, err) + return err + } + + return nil + }) + if err != nil { + klog.Infof("update status in showprogress error=%v", err) + return err + } + return nil +} + +// jobs provides a way of identifying the download keys for a particular task +// encountering during the pull walk. +// +// This is very minimal and will probably be replaced with something more +// featured. +type jobs struct { + name string + added map[digest.Digest]struct{} + descs []ocispec.Descriptor + mu sync.Mutex + resolved bool +} + +func newJobs(name string) *jobs { + return &jobs{ + name: name, + added: map[digest.Digest]struct{}{}, + } +} + +func (j *jobs) add(desc ocispec.Descriptor) { + j.mu.Lock() + defer j.mu.Unlock() + j.resolved = true + + if _, ok := j.added[desc.Digest]; ok { + return + } + j.descs = append(j.descs, desc) + j.added[desc.Digest] = struct{}{} +} + +func (j *jobs) jobs() []ocispec.Descriptor { + j.mu.Lock() + defer j.mu.Unlock() + + var descs []ocispec.Descriptor + return append(descs, j.descs...) +} + +func (j *jobs) isResolved() bool { + j.mu.Lock() + defer j.mu.Unlock() + return j.resolved +} diff --git a/pkg/images/puller.go b/pkg/images/puller.go new file mode 100644 index 0000000..cb77419 --- /dev/null +++ b/pkg/images/puller.go @@ -0,0 +1,119 @@ +package images + +import ( + "context" + "os" + "time" + + "github.com/containerd/containerd" + "github.com/containerd/containerd/cmd/ctr/commands" + "github.com/containerd/containerd/cmd/ctr/commands/content" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/platforms" + "k8s.io/klog/v2" + + refdocker "github.com/containerd/containerd/reference/docker" + "github.com/containerd/containerd/remotes/docker" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +var sock = "/var/run/containerd/containerd.sock" + +type PullOptions struct { + AppName string + OwnerName string +} + +type ImageService interface { + PullImage(ctx context.Context, ref string, opts PullOptions) (string, error) + Progress(ctx context.Context, ref string, opts PullOptions) (string, error) +} + +type imageService struct { + client *containerd.Client +} + +func NewClientOrDie(ctx context.Context) (*imageService, context.Context, context.CancelFunc) { + _, err := os.Stat(sock) + if err != nil { + panic(err) + } + client, err := containerd.New(sock, containerd.WithDefaultNamespace("k8s.io"), + containerd.WithTimeout(10*time.Second)) + if err != nil { + panic(err) + } + var cancel context.CancelFunc + ctx, cancel = context.WithCancel(ctx) + return &imageService{ + client: client, + }, ctx, cancel +} + +func (is *imageService) PullImage(ctx context.Context, ref string, opts PullOptions) (string, error) { + named, err := refdocker.ParseDockerRef(ref) + if err != nil { + return ref, err + } + ref = named.String() + config := newFetchConfig() + ongoing := newJobs(ref) + + klog.Infof("start to pull image name=%s", ref) + + pctx, stopProgress := context.WithCancel(ctx) + + progress := make(chan struct{}) + go func() { + showProgress(pctx, ongoing, is.client.ContentStore(), opts) + close(progress) + }() + + h := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + if desc.MediaType != images.MediaTypeDockerSchema1Manifest { + ongoing.add(desc) + } + return nil, nil + }) + + labels := commands.LabelArgs(config.Labels) + remoteOpts := []containerd.RemoteOpt{ + containerd.WithPullLabels(labels), + containerd.WithResolver(config.Resolver), + containerd.WithImageHandler(h), + containerd.WithSchema1Conversion, + } + if config.AllMetadata { + remoteOpts = append(remoteOpts, containerd.WithAllMetadata()) + } + if config.PlatformMatcher != nil { + remoteOpts = append(remoteOpts, containerd.WithPlatformMatcher(config.PlatformMatcher)) + } else { + for _, platform := range config.Platforms { + remoteOpts = append(remoteOpts, containerd.WithPlatform(platform)) + } + } + _, err = is.client.Fetch(pctx, ref, remoteOpts...) + stopProgress() + if err != nil { + klog.Infof("fetch image name=%s err=%v", ref, err) + return "", err + } + + <-progress + return ref, nil +} + +func (is *imageService) Progress(ctx context.Context, ref string, opts PullOptions) (string, error) { + return "", nil +} + +func newFetchConfig() *content.FetchConfig { + options := docker.ResolverOptions{} + resolver := docker.NewResolver(options) + config := &content.FetchConfig{ + Resolver: resolver, + PlatformMatcher: platforms.Default(), + } + return config +} diff --git a/pkg/utils/app.go b/pkg/utils/app.go index 86f7946..5d3a846 100644 --- a/pkg/utils/app.go +++ b/pkg/utils/app.go @@ -90,35 +90,42 @@ func UpdateAppMgrStatus(name string, status v1alpha1.ApplicationManagerStatus) ( if err != nil { return nil, err } - appMgr, err := client.AppV1alpha1().ApplicationManagers().Get(context.TODO(), name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - appMgrCopy := appMgr.DeepCopy() + var appMgr *v1alpha1.ApplicationManager - status.OpGeneration = appMgrCopy.Status.OpGeneration + 1 - status.OpRecords = appMgrCopy.Status.OpRecords - if status.State == "" { - status.State = appMgrCopy.Status.State - } - if status.Message == "" { - status.Message = appMgrCopy.Status.Message - } - payload := status.Payload - if payload == nil { - payload = make(map[string]string) - } - for k, v := range appMgrCopy.Status.Payload { - if _, ok := payload[k]; !ok { - payload[k] = v + err = retry.RetryOnConflict(retry.DefaultRetry, func() error { + appMgr, err = client.AppV1alpha1().ApplicationManagers().Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil { + return err } - } - status.Payload = payload + appMgrCopy := appMgr.DeepCopy() - appMgrCopy.Status = status + status.OpGeneration = appMgrCopy.Status.OpGeneration + 1 + status.OpRecords = appMgrCopy.Status.OpRecords + + if status.State == "" { + status.State = appMgrCopy.Status.State + } + if status.Message == "" { + status.Message = appMgrCopy.Status.Message + } + payload := status.Payload + if payload == nil { + payload = make(map[string]string) + } + for k, v := range appMgrCopy.Status.Payload { + if _, ok := payload[k]; !ok { + payload[k] = v + } + } + status.Payload = payload + + appMgrCopy.Status = status + + appMgr, err = client.AppV1alpha1().ApplicationManagers().UpdateStatus(context.TODO(), appMgrCopy, metav1.UpdateOptions{}) + return err + }) - a, err := client.AppV1alpha1().ApplicationManagers().UpdateStatus(context.TODO(), appMgrCopy, metav1.UpdateOptions{}) - return a, err + return appMgr, err } // GetDeployedReleaseVersion check whether app has been deployed and return release chart version @@ -288,6 +295,7 @@ func UpdateStatus(appMgr *v1alpha1.ApplicationManager, state v1alpha1.Applicatio if len(appMgr.Status.OpRecords) > 20 { appMgrCopy.Status.OpRecords = appMgr.Status.OpRecords[:20:20] } + //klog.Infof("utils: UpdateStatus: %v", appMgrCopy.Status.Conditions) _, err = client.AppV1alpha1().ApplicationManagers().UpdateStatus(context.TODO(), appMgrCopy, metav1.UpdateOptions{}) if err != nil { diff --git a/pkg/utils/helm.go b/pkg/utils/helm.go new file mode 100644 index 0000000..ad56cc0 --- /dev/null +++ b/pkg/utils/helm.go @@ -0,0 +1,219 @@ +package utils + +import ( + "bytes" + "context" + "fmt" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + helmLoader "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/downloader" + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/kube" + kubefake "helm.sh/helm/v3/pkg/kube/fake" + "helm.sh/helm/v3/pkg/registry" + "helm.sh/helm/v3/pkg/storage" + "helm.sh/helm/v3/pkg/storage/driver" + "io" + "io/ioutil" + v1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/cli-runtime/pkg/resource" + //"bytetrade.io/web3os/app-service/pkg/generated/clientset/versioned/scheme" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/klog/v2" +) + +func actionConfig() (*action.Configuration, error) { + registryClient, err := registry.NewClient() + if err != nil { + return nil, err + } + configuration := action.Configuration{ + Releases: storage.Init(driver.NewMemory()), + KubeClient: &kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: ioutil.Discard}}, + Capabilities: chartutil.DefaultCapabilities, + RegistryClient: registryClient, + } + return &configuration, nil +} + +func InitAction() (*action.Install, error) { + config, err := actionConfig() + if err != nil { + klog.Infof("actionConfig err=%v", err) + return nil, err + } + instAction := action.NewInstall(config) + instAction.Namespace = "spaced" + instAction.ReleaseName = "test-release" + instAction.DryRun = true + return instAction, nil +} + +func getChart(instAction *action.Install, filepath string) (*chart.Chart, error) { + cp, err := instAction.ChartPathOptions.LocateChart(filepath, &cli.EnvSettings{}) + if err != nil { + klog.Infof("locate chart error: %v", err) + return nil, err + } + p := getter.All(&cli.EnvSettings{}) + chartRequested, err := helmLoader.Load(cp) + if err != nil { + klog.Infof("Load err=%v", err) + return nil, err + } + if req := chartRequested.Metadata.Dependencies; req != nil { + if err := action.CheckDependencies(chartRequested, req); err != nil { + if instAction.DependencyUpdate { + man := &downloader.Manager{ + ChartPath: cp, + Keyring: instAction.ChartPathOptions.Keyring, + SkipUpdate: false, + Getters: p, + } + if err := man.Update(); err != nil { + return nil, err + } + // Reload the chart with the updated Chart.lock file. + if chartRequested, err = helmLoader.Load(cp); err != nil { + return nil, err + } + } else { + + } + } + } + return chartRequested, nil +} + +func GetResourceListFromChart(chartPath string) (resources kube.ResourceList, err error) { + instAction, err := InitAction() + if err != nil { + return nil, err + } + instAction.Namespace = "app-namespace" + chartRequested, err := getChart(instAction, chartPath) + if err != nil { + klog.Infof("getchart err=%v", err) + return nil, err + } + klog.Infof("chartRequested :=%##v", chartRequested) + + // fake values for helm dry run + values := make(map[string]interface{}) + values["bfl"] = map[string]interface{}{ + "username": "bfl-username", + } + values["user"] = map[string]interface{}{ + "zone": "user-zone", + } + values["schedule"] = map[string]interface{}{ + "nodeName": "node", + } + values["userspace"] = map[string]interface{}{ + "appCache": "appcache", + "userData": "userspace/Home", + } + values["os"] = map[string]interface{}{ + "appKey": "appKey", + "appSecret": "appSecret", + } + + values["domain"] = map[string]string{} + values["dep"] = map[string]interface{}{} + values["postgres"] = map[string]interface{}{ + "databases": map[string]interface{}{}, + } + values["redis"] = map[string]interface{}{} + values["mongodb"] = map[string]interface{}{ + "databases": map[string]interface{}{}, + } + values["zinc"] = map[string]interface{}{ + "indexes": map[string]interface{}{}, + } + values["svcs"] = map[string]interface{}{} + + ret, err := instAction.RunWithContext(context.Background(), chartRequested, values) + if err != nil { + return nil, err + } + klog.Infof("################################\n") + klog.Infof("manifest: %s\n", ret.Manifest) + klog.Infof("################################\n") + + var metadataAccessor = meta.NewAccessor() + d := yaml.NewYAMLOrJSONDecoder(bytes.NewBufferString(ret.Manifest), 4096) + for { + ext := runtime.RawExtension{} + if err := d.Decode(&ext); err != nil { + if err == io.EOF { + return resources, nil + } + return nil, fmt.Errorf("error parsing") + } + ext.Raw = bytes.TrimSpace(ext.Raw) + if len(ext.Raw) == 0 || bytes.Equal(ext.Raw, []byte("null")) { + continue + } + obj, _, err := unstructured.UnstructuredJSONScheme.Decode(ext.Raw, nil, nil) + if err != nil { + return nil, err + } + name, _ := metadataAccessor.Name(obj) + namespace, _ := metadataAccessor.Namespace(obj) + info := &resource.Info{ + Namespace: namespace, + Name: name, + Object: obj, + } + resources = append(resources, info) + } +} + +func GetRefFromResourceList(chartPath string) (refs []string, err error) { + resources, err := GetResourceListFromChart(chartPath) + if err != nil { + klog.Infof("get resourcelist from chart err=%v", err) + return refs, err + } + klog.Infof("XXXX: resources: %#v", resources) + for _, r := range resources { + kind := r.Object.GetObjectKind().GroupVersionKind().Kind + klog.Infof("reousrce kind: %v", kind) + + if kind == "Deployment" { + var deployment v1.Deployment + err = scheme.Scheme.Convert(r.Object, &deployment, nil) + if err != nil { + klog.Infof("deployerror =%v\n", err) + return refs, err + } + for _, c := range deployment.Spec.Template.Spec.InitContainers { + refs = append(refs, c.Image) + } + for _, c := range deployment.Spec.Template.Spec.Containers { + refs = append(refs, c.Image) + } + } + if kind == "StatefulSet" { + var sts v1.StatefulSet + err = scheme.Scheme.Convert(r.Object, &sts, nil) + if err != nil { + return refs, err + } + for _, c := range sts.Spec.Template.Spec.InitContainers { + refs = append(refs, c.Image) + } + for _, c := range sts.Spec.Template.Spec.Containers { + refs = append(refs, c.Image) + } + } + } + return refs, nil +}