kubeagi / arcadia

A diverse, simple, and secure one-stop LLMOps platform
http://www.kubeagi.com/
Apache License 2.0
63 stars 20 forks source link

refactor: save app icon to oss #976

Closed 0xff-dev closed 3 months ago

0xff-dev commented 3 months ago

What type of PR is this?

/kind feature

What this PR does / why we need it

refactor: save app icon to oss

Which issue(s) this PR fixes

Fix https://github.com/kubeagi/arcadia/issues/960

Special notes for your reviewer

package main

import (
    "bytes"
    "context"
    "crypto/tls"
    "encoding/base64"
    "flag"
    "fmt"
    "net/http"
    "path/filepath"
    "sync"

    v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    "k8s.io/apimachinery/pkg/runtime/schema"
    "k8s.io/client-go/dynamic"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/util/homedir"

    "github.com/minio/minio-go/v7"
    "github.com/minio/minio-go/v7/pkg/credentials"
)

var (
    endpoint = flag.String("api", "minio-api.172.22.96.167.nip.io", "minio api service address, such as minio-api.172.22.96.167.nip.io")
    username = flag.String("u", "admin", "username")
    password = flag.String("p", "", "password")
)

func main() {
    var kubeconfig *string
    if home := homedir.HomeDir(); home != "" {
        kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
    } else {
        kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
    }
    flag.Parse()

    tp := http.Transport{
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: true,
        },
    }
    minioClient, err := minio.New(*endpoint, &minio.Options{
        Creds:     credentials.NewStaticV4(*username, *password, ""),
        Secure:    true,
        Transport: &tp,
    })
    if err != nil {
        panic(err)
    }
    cfg, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
    if err != nil {
        panic(err)
    }

    dynamicClient := dynamic.NewForConfigOrDie(cfg)

    namespaceList, err := dynamicClient.Resource(schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}).List(context.TODO(), v1.ListOptions{})
    if err != nil {
        panic(err)
    }

    gvr := schema.GroupVersionResource{
        Group:    "arcadia.kubeagi.k8s.com.cn",
        Version:  "v1alpha1",
        Resource: "applications",
    }

    var wg sync.WaitGroup
    for _, i := range namespaceList.Items {
        wg.Add(1)
        go func(namespace string) {
            defer wg.Done()
            appList, err := dynamicClient.Resource(gvr).List(context.TODO(), v1.ListOptions{})
            if err != nil {
                fmt.Printf("[Error] in namespace %s, failed to list apps %s\n", namespace, err)
                return
            }

            for _, app := range appList.Items {
                icon, found, err := unstructured.NestedString(app.Object, "spec", "icon")
                if err != nil || !found {
                    fmt.Printf("[Error] %s/%s not found path spec.icon or error %s\n", app.GetNamespace(), app.GetName(), err)
                    continue
                }
                bs, err := ParseBase64ImageBytes(icon)
                if err != nil {
                    fmt.Printf("[Error] %s/%s failed to parse base64 image\n", app.GetNamespace(), app.GetName())
                    continue
                }
                name := fmt.Sprintf("application/%s/icon/%s", app.GetName(), app.GetName())
                _, err = minioClient.PutObject(context.TODO(), namespace, name, bytes.NewReader(bs), int64(len(bs)), minio.PutObjectOptions{})
                if err != nil {
                    fmt.Printf("[Error] %s/%s failed to upload icon to minio error %s\n", app.GetNamespace(), app.GetName(), err)
                    continue
                }
                fmt.Printf("[Success] %s/%s migration successful\n\n", app.GetNamespace(), app.GetName())
            }
        }(i.GetName())
    }
    wg.Wait()
    fmt.Println("Done")
}

func ParseBase64ImageBytes(img string) ([]byte, error) {
    i := 0
    for ; i < len(img) && img[i] != ','; i++ {
    }
    cut := img[i+1:]
    return base64.StdEncoding.DecodeString(cut)
}
bjwswang commented 3 months ago

@0xff-dev Please make sure the gptstore support this icon link.