plexsystems / sinker

A tool to sync images from one container registry to another
MIT License
609 stars 53 forks source link

Sync from k8s directly #20

Closed pathcl closed 4 years ago

pathcl commented 4 years ago

First thank you for the project! I'd like to make a suggestion:

Use case: you want to retrieve all images from one cluster to make it air-gapped.

Let's assume you're already running a cluster. Probably its convenient to get all images from it and then generate the config needed for sinker:

Something like this will get all the images running:

package main

import (
    "context"
    "flag"
    "fmt"
    "os"
    "path/filepath"

    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
)

func AppendIfMissing(slice []string, i string) []string {
    for _, ele := range slice {
        if ele == i {
            return slice
        }
    }
    return append(slice, i)
}

func homeDir() string {
    if h := os.Getenv("HOME"); h != "" {
        return h
    }
    return os.Getenv("USERPROFILE") 
}

func main() {
    var kubeconfig *string
    if home := 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()

    // use the current context in kubeconfig
    config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
    if err != nil {
        panic(err.Error())
    }

    // create the clientset
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        panic(err.Error())
    }

    pods, err := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        panic(err.Error())
    }
    s := []string{}

    for _, p := range pods.Items {
        for _, c := range p.Spec.Containers {
            s = AppendIfMissing(s, c.Image)
        }
    }

    for _, image := range s {
        fmt.Println(image)
    }

}

From there we'd only need to generate the yaml for sinker and/or filter it. Thoughts?

jpreese commented 4 years ago

No problem @pathcl.

I think the use case makes complete sense. Though this should be achievable today:

Get a list of all of the images running in the cluster (https://kubernetes.io/docs/tasks/access-application-cluster/list-all-running-container-images/)

$ kubectl get pods --all-namespaces -o go-template --template="{{range .items}}{{range .spec.containers}}{{.image}} {{end}}{{end}}"

Then either set the SINKER_IMAGES environment variable to that list, or pass that list to the command line with --images. Include --target to set where you want them to go.

$ export SINKER_IMAGES=imageOne:v1,imageTwo:v1
$ export SINKER_TARGET=internal.com/repo
$ sinker push

--or--

$ sinker push -i imageOne:v1,imageTwo:v1 -t internal.com/repo

Would that work?

edit: If you would like to save that list for future use, I do think it makes sense to have the create command take similar arguments and create a manifest that way.

jpreese commented 4 years ago

@pathcl a fair number of changes have been added to the main branch, but not quite released yet. Standard input is now supported which may solve your request.

$ kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" | ./sinker create - --target repo
jpreese commented 4 years ago

Standard input has been released in 0.11.0

https://github.com/plexsystems/sinker/releases/tag/v0.11.0