Initial pull implemented.
Support for proxy repositories by pulling from the proxy and then tagging the image.
This commit is contained in:
parent
965dfbf245
commit
b31e03bc7a
@ -11,6 +11,7 @@ ENV GOTOOLCHAIN=auto
|
|||||||
COPY go.mod go.sum /opt/fetcher/
|
COPY go.mod go.sum /opt/fetcher/
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
COPY cmd /opt/fetcher/cmd/
|
COPY cmd /opt/fetcher/cmd/
|
||||||
|
COPY pkg /opt/fetcher/pkg/
|
||||||
|
|
||||||
RUN go build -o bin ./cmd/...
|
RUN go build -o bin ./cmd/...
|
||||||
RUN find . -type f
|
RUN find . -type f
|
||||||
|
52
TODO.md
Normal file
52
TODO.md
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
|
||||||
|
labels on image manifest
|
||||||
|
|
||||||
|
# Getting the image digest
|
||||||
|
root@baboon:~# ctr -n k8s.io image ls | grep cat.wamblee.org/converge:1.0.0
|
||||||
|
cat.wamblee.org/converge:1.0.0
|
||||||
|
application/vnd.docker.distribution.manifest.v2+json sha256:c08c336462955fcf4a63357dd95b1c5a37f1fbf2ca96e476d38968c39e782b13 39.2 MiB linux/amd64
|
||||||
|
|
||||||
|
# getting the manifest
|
||||||
|
|
||||||
|
# ctr -n k8s.io content get sha256:c08c336462955fcf4a63357dd95b1c5a37f1fbf2ca96e476d38968c39e782b13
|
||||||
|
{
|
||||||
|
"schemaVersion": 2,
|
||||||
|
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||||
|
"config": {
|
||||||
|
"mediaType": "application/vnd.docker.container.image.v1+json",
|
||||||
|
"size": 1573,
|
||||||
|
"digest": "sha256:f73adff49e6fb8d94c55cd4030e5045e854de284418e737493fc22727f6b4e44"
|
||||||
|
},
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||||
|
"size": 7748860,
|
||||||
|
"digest": "sha256:be3c1309dc5ae93749438ca02ae64fae75cc04bf01507672f7ee0fe5dcc06c74"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||||
|
"size": 32034281,
|
||||||
|
"digest": "sha256:72c349c372c463b95dca722c644359550949b2824852f02f1cf12b8b4b03bb0e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||||
|
"size": 1304341,
|
||||||
|
"digest": "sha256:bf7da15860525c2cba9a6639846b5ce958c373cdae44ca6f704af406703e52d5"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# blob label?
|
||||||
|
containerd.io/gc.ref.snapshot.overlayfs=sha256:a7800a8e9dc0e1b7141ee372c7a0bad3d34cd5d5b061a9cc41d928e331578e0e
|
||||||
|
|
||||||
|
|
||||||
|
# ctr -n k8s.io content ls | grep sha256:a7800a8e9dc0e1b7141ee372c7a0bad3d34cd5d5b061a9cc41d928e331578e0e
|
||||||
|
sha256:f73adff49e6fb8d94c55cd4030e5045e854de284418e737493fc22727f6b4e44 1.573kB 5 days containerd.io/gc.ref.snapshot.overlayfs=sha256:a7800a8e9dc0e1b7141ee372c7a0bad3d34cd5d5b061a9cc41d928e331578e0e,containerd.io/distribution.source.cat.wamblee.org=converge
|
||||||
|
|
||||||
|
|
||||||
|
# ctr -n k8s.io content ls | grep f73adff49e6fb8d94c55cd4030e5045e854de284418e737493fc22727f6b4e44
|
||||||
|
sha256:c08c336462955fcf4a63357dd95b1c5a37f1fbf2ca96e476d38968c39e782b13 951B 5 days containerd.io/gc.ref.content.l.2=sha256:bf7da15860525c2cba9a6639846b5ce958c373cdae44ca6f704af406703e52d5,containerd.io/gc.ref.content.l.1=sha256:72c349c372c463b95dca722c644359550949b2824852f02f1cf12b8b4b03bb0e,containerd.io/gc.ref.content.l.0=sha256:be3c1309dc5ae93749438ca02ae64fae75cc04bf01507672f7ee0fe5dcc06c74,containerd.io/gc.ref.content.config=sha256:f73adff49e6fb8d94c55cd4030e5045e854de284418e737493fc22727f6b4e44,containerd.io/distribution.source.cat.wamblee.org=converge
|
||||||
|
sha256:f73adff49e6fb8d94c55cd4030e5045e854de284418e737493fc22727f6b4e44 1.573kB 5 days containerd.io/gc.ref.snapshot.overlayfs=sha256:a7800a8e9dc0e1b7141ee372c7a0bad3d34cd5d5b061a9cc41d928e331578e0e,containerd.io/distribution.source.cat.wamblee.org=converge
|
||||||
|
|
||||||
|
https://www.mo4tech.com/revelation-containerd-image-file-loss-problem-is-caused-by-image-generation.html
|
6
cmd/ctrutil/config.go
Normal file
6
cmd/ctrutil/config.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
SocketPath string
|
||||||
|
ContainerdNamespace string
|
||||||
|
}
|
120
cmd/ctrutil/main.go
Normal file
120
cmd/ctrutil/main.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
goflags "flag"
|
||||||
|
"fmt"
|
||||||
|
"git.wamblee.org/public/kube-fetcher/pkg/ctrd"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"io"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
klogFlags := goflags.NewFlagSet("", goflags.PanicOnError)
|
||||||
|
klog.InitFlags(klogFlags)
|
||||||
|
|
||||||
|
config := &Config{}
|
||||||
|
|
||||||
|
var runtime *ctrd.Containerd
|
||||||
|
var err error
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "ctrutil",
|
||||||
|
Short: "Containerd utility",
|
||||||
|
Long: `
|
||||||
|
Containerd for working with images as they are pulled by the kubelet.`,
|
||||||
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
runtime, err = ctrd.NewContainerd(config.SocketPath,
|
||||||
|
config.ContainerdNamespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pull := &cobra.Command{
|
||||||
|
Use: "pull",
|
||||||
|
Short: "pull image",
|
||||||
|
Long: `
|
||||||
|
Pull image`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
for _, image := range args {
|
||||||
|
klog.Infof("Pulling '%s'", image)
|
||||||
|
err := runtime.Pull(image)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Cannot pull '%s': %v", image, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.AddCommand(pull)
|
||||||
|
|
||||||
|
blob := &cobra.Command{
|
||||||
|
Use: "blob",
|
||||||
|
Short: "blob manipulation",
|
||||||
|
Long: `
|
||||||
|
Containerd for working with images as they are pulled by the kubelet.`,
|
||||||
|
}
|
||||||
|
cmd.AddCommand(blob)
|
||||||
|
|
||||||
|
blobget := &cobra.Command{
|
||||||
|
Use: "get",
|
||||||
|
Short: "get blob data",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return fmt.Errorf("Expected blob digest as argument")
|
||||||
|
}
|
||||||
|
reader, err := runtime.GetBlob(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(os.Stdout, reader)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
blob.AddCommand(blobget)
|
||||||
|
|
||||||
|
blobmeta := &cobra.Command{
|
||||||
|
Use: "meta",
|
||||||
|
Short: "get blob metadata",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return fmt.Errorf("Expected blob digest as argument")
|
||||||
|
}
|
||||||
|
info, err := runtime.GetBlobMeta(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("size: %v\n", info.Size)
|
||||||
|
fmt.Printf("digest: %v\n", info.Digest)
|
||||||
|
fmt.Printf("created: %v\n", info.CreatedAt)
|
||||||
|
fmt.Printf("updated: %v\n", info.UpdatedAt)
|
||||||
|
for k, v := range info.Labels {
|
||||||
|
fmt.Printf("label: %s=%s\n", k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
blob.AddCommand(blobmeta)
|
||||||
|
|
||||||
|
if 1 > 2 {
|
||||||
|
runtime.List()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.PersistentFlags().StringVar(&config.SocketPath, "socket",
|
||||||
|
"/run/runtime/runtime.sock", "Containerd socket")
|
||||||
|
cmd.PersistentFlags().StringVar(&config.ContainerdNamespace, "runtime-namespace",
|
||||||
|
"k8s.io", "Containerd namespace to use")
|
||||||
|
cmd.Flags().AddGoFlagSet(klogFlags)
|
||||||
|
|
||||||
|
err = cmd.Execute()
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Error: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,6 +3,7 @@ package main
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
InitialPullAll bool
|
||||||
PollInterval time.Duration
|
PollInterval time.Duration
|
||||||
KubernetesNamespace string
|
KubernetesNamespace string
|
||||||
SocketPath string
|
SocketPath string
|
||||||
@ -11,4 +12,5 @@ type Config struct {
|
|||||||
ReadyDuration time.Duration
|
ReadyDuration time.Duration
|
||||||
includeControllerNodes bool
|
includeControllerNodes bool
|
||||||
monitoringWindowSize time.Duration
|
monitoringWindowSize time.Duration
|
||||||
|
mirrors map[string]string
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
containerd2 "git.wamblee.org/public/kube-fetcher/pkg/ctrd"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
@ -108,7 +109,7 @@ func (fetcher *Fetcher) getContainers(clientset *kubernetes.Clientset) map[strin
|
|||||||
return containers
|
return containers
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fetcher *Fetcher) pullAndPin() error {
|
func (fetcher *Fetcher) pullAndPin(pullAll bool) error {
|
||||||
|
|
||||||
nodeName := os.Getenv("NODE_NAME")
|
nodeName := os.Getenv("NODE_NAME")
|
||||||
if nodeName == "" {
|
if nodeName == "" {
|
||||||
@ -116,7 +117,7 @@ func (fetcher *Fetcher) pullAndPin() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the image manager
|
// Create the image manager
|
||||||
containerd, err := NewContainerd(fetcher.config.SocketPath, fetcher.config.ContainerdNamespace)
|
containerd, err := containerd2.NewContainerd(fetcher.config.SocketPath, fetcher.config.ContainerdNamespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Fatalf("Failed to create image manager: %v", err)
|
klog.Fatalf("Failed to create image manager: %v", err)
|
||||||
}
|
}
|
||||||
@ -144,12 +145,26 @@ func (fetcher *Fetcher) pullAndPin() error {
|
|||||||
|
|
||||||
// Pull images that are used
|
// Pull images that are used
|
||||||
for container := range containers {
|
for container := range containers {
|
||||||
if _, found := imgs[container]; !found {
|
if _, found := imgs[container]; !found || pullAll {
|
||||||
|
tag := ""
|
||||||
|
for registry, mirror := range fetcher.config.mirrors {
|
||||||
|
if strings.HasPrefix(container, registry+"/") {
|
||||||
|
tag = container
|
||||||
|
container = mirror + container[len(registry):]
|
||||||
|
}
|
||||||
|
}
|
||||||
klog.Infof("%s: Pulling %s\n", nodeName, container)
|
klog.Infof("%s: Pulling %s\n", nodeName, container)
|
||||||
err := containerd.Pull(container)
|
err := containerd.Pull(container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Warningf("error: %v", err)
|
klog.Warningf("error: %v", err)
|
||||||
}
|
}
|
||||||
|
if tag != "" {
|
||||||
|
klog.Infof("%s: Tagging '%s' -> '%s'", nodeName, container, tag)
|
||||||
|
err := containerd.Tag(container, tag)
|
||||||
|
if err != nil {
|
||||||
|
klog.Warningf("Could not tag '%s' -> '%s'", container, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
goflags "flag"
|
goflags "flag"
|
||||||
|
"git.wamblee.org/public/kube-fetcher/pkg/support"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
"os"
|
"os"
|
||||||
@ -12,7 +13,7 @@ func main() {
|
|||||||
klogFlags := goflags.NewFlagSet("", goflags.PanicOnError)
|
klogFlags := goflags.NewFlagSet("", goflags.PanicOnError)
|
||||||
klog.InitFlags(klogFlags)
|
klog.InitFlags(klogFlags)
|
||||||
|
|
||||||
clientset := GetKubernetesConnection()
|
clientset := support.GetKubernetesConnection()
|
||||||
config := &Config{}
|
config := &Config{}
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@ -23,6 +24,7 @@ Queries k8s for all running pods and makes sure that all
|
|||||||
images referenced in pods are made available on the local k8s node and pinned
|
images referenced in pods are made available on the local k8s node and pinned
|
||||||
so they don't get garbage collected'`,
|
so they don't get garbage collected'`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
serializer := make(chan func())
|
serializer := make(chan func())
|
||||||
go func() {
|
go func() {
|
||||||
for action := range serializer {
|
for action := range serializer {
|
||||||
@ -32,13 +34,15 @@ so they don't get garbage collected'`,
|
|||||||
watcher := NewWatcher(clientset, config.monitoringWindowSize, config.KubernetesNamespace, serializer)
|
watcher := NewWatcher(clientset, config.monitoringWindowSize, config.KubernetesNamespace, serializer)
|
||||||
fetcher := NewFetcher(clientset, config, watcher)
|
fetcher := NewFetcher(clientset, config, watcher)
|
||||||
|
|
||||||
|
// TODO config option
|
||||||
|
fetcher.pullAndPin(config.InitialPullAll)
|
||||||
ticker := time.NewTicker(config.PollInterval)
|
ticker := time.NewTicker(config.PollInterval)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
serializer <- func() {
|
serializer <- func() {
|
||||||
klog.V(3).Infof("Fetcher.pullAndPin")
|
klog.V(3).Infof("Fetcher.pullAndPin")
|
||||||
fetcher.pullAndPin()
|
fetcher.pullAndPin(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,6 +65,10 @@ so they don't get garbage collected'`,
|
|||||||
6*time.Hour, "Monitoring window to see what pods were active")
|
6*time.Hour, "Monitoring window to see what pods were active")
|
||||||
cmd.PersistentFlags().DurationVar(&config.PollInterval, "poll-interval",
|
cmd.PersistentFlags().DurationVar(&config.PollInterval, "poll-interval",
|
||||||
1*time.Minute, "Poll interval for checking whether to pull images. ")
|
1*time.Minute, "Poll interval for checking whether to pull images. ")
|
||||||
|
cmd.PersistentFlags().StringToStringVar(&config.mirrors,
|
||||||
|
"mirror", make(map[string]string), "Specify regsitry mirror in the form registrey=mirror, e.g. docker.io=my.mirror. The option can be repeated.")
|
||||||
|
cmd.PersistentFlags().BoolVar(&config.InitialPullAll, "initial-pull-all",
|
||||||
|
false, "Initially pull all images, this can be usefule for populating a caching proxy.")
|
||||||
cmd.Flags().AddGoFlagSet(klogFlags)
|
cmd.Flags().AddGoFlagSet(klogFlags)
|
||||||
|
|
||||||
err := cmd.Execute()
|
err := cmd.Execute()
|
||||||
|
6
go.mod
6
go.mod
@ -4,7 +4,10 @@ go 1.24.0
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/containerd/containerd v1.7.26
|
github.com/containerd/containerd v1.7.26
|
||||||
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
|
github.com/opencontainers/image-spec v1.1.0
|
||||||
github.com/spf13/cobra v1.9.1
|
github.com/spf13/cobra v1.9.1
|
||||||
|
k8s.io/api v0.32.2
|
||||||
k8s.io/apimachinery v0.32.2
|
k8s.io/apimachinery v0.32.2
|
||||||
k8s.io/client-go v0.32.2
|
k8s.io/client-go v0.32.2
|
||||||
k8s.io/klog/v2 v2.130.1
|
k8s.io/klog/v2 v2.130.1
|
||||||
@ -57,8 +60,6 @@ require (
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
|
||||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
|
||||||
github.com/opencontainers/runtime-spec v1.2.1 // indirect
|
github.com/opencontainers/runtime-spec v1.2.1 // indirect
|
||||||
github.com/opencontainers/selinux v1.11.1 // indirect
|
github.com/opencontainers/selinux v1.11.1 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
@ -85,7 +86,6 @@ require (
|
|||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
k8s.io/api v0.32.2 // indirect
|
|
||||||
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
|
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
|
||||||
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
|
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
|
||||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||||
|
@ -27,8 +27,15 @@ spec:
|
|||||||
privileged: true
|
privileged: true
|
||||||
runAsUser: 0
|
runAsUser: 0
|
||||||
args:
|
args:
|
||||||
- --ready-duration=1m
|
- --ready-duration={{ .Values.readyDuration }}
|
||||||
- --v=3
|
- --v={{ .Values.logLevel }}
|
||||||
|
{{- if .Values.initialPullAll }}
|
||||||
|
- --initial-pull-all
|
||||||
|
{{- end }}
|
||||||
|
{{- range $mirror := .Values.mirrors }}
|
||||||
|
- --mirror
|
||||||
|
- {{ $mirror.registry }}={{ $mirror.mirror }}
|
||||||
|
{{- end }}
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- mountPath: /run/containerd/containerd.sock
|
- mountPath: /run/containerd/containerd.sock
|
||||||
name: containerd-sock
|
name: containerd-sock
|
||||||
|
@ -1 +1,19 @@
|
|||||||
|
|
||||||
|
|
||||||
|
logLevel: 2
|
||||||
|
readyDuration: 1m
|
||||||
|
initialPullAll: true
|
||||||
|
|
||||||
|
mirrors:
|
||||||
|
- registry: docker.io
|
||||||
|
mirror: wharf.wamblee.org
|
||||||
|
- registry: gcr.io
|
||||||
|
mirror: wharf.wamblee.org
|
||||||
|
- registry: k8s.gcr.io
|
||||||
|
mirror: wharf.wamblee.org
|
||||||
|
- registry: quay.io
|
||||||
|
mirror: wharf.wamblee.org
|
||||||
|
- registry: ghcr.io
|
||||||
|
mirror: wharf.wamblee.org
|
||||||
|
- registry: registry.k8s.io
|
||||||
|
mirror: wharf.wamblee.org
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package ctrd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -6,10 +6,14 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
|
"github.com/containerd/containerd/content"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/images"
|
"github.com/containerd/containerd/images"
|
||||||
"github.com/containerd/containerd/leases"
|
"github.com/containerd/containerd/leases"
|
||||||
"github.com/containerd/containerd/namespaces"
|
"github.com/containerd/containerd/namespaces"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
"io"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -158,6 +162,28 @@ func (m *Containerd) Pull(imageRef string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Containerd) Tag(imageFrom, imageTo string) error {
|
||||||
|
sourceImage, err := m.client.GetImage(m.ctx, imageFrom)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to get source image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new image with the target name that references the same content
|
||||||
|
newImage := images.Image{
|
||||||
|
Name: imageTo,
|
||||||
|
Target: sourceImage.Target(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// best effort, remove old tag
|
||||||
|
_ = m.Remove(imageTo)
|
||||||
|
|
||||||
|
// Create the new tag
|
||||||
|
if _, err := m.client.ImageService().Create(m.ctx, newImage); err != nil {
|
||||||
|
return fmt.Errorf("Failed to create new image tag: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Remove deletes an image
|
// Remove deletes an image
|
||||||
func (m *Containerd) Remove(imageRef string) error {
|
func (m *Containerd) Remove(imageRef string) error {
|
||||||
// Get the image
|
// Get the image
|
||||||
@ -191,3 +217,24 @@ func generateID(image string) string {
|
|||||||
md5Base64 := base64.StdEncoding.EncodeToString(md5Hash[:])
|
md5Base64 := base64.StdEncoding.EncodeToString(md5Hash[:])
|
||||||
return md5Base64
|
return md5Base64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Containerd) GetBlob(hash string) (io.Reader, error) {
|
||||||
|
store := m.client.ContentStore()
|
||||||
|
descriptor := ocispec.Descriptor{
|
||||||
|
Digest: digest.Digest(hash),
|
||||||
|
}
|
||||||
|
reader, err := store.ReaderAt(m.ctx, descriptor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return content.NewReader(reader), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Containerd) GetBlobMeta(hash string) (content.Info, error) {
|
||||||
|
store := m.client.ContentStore()
|
||||||
|
info, err := store.Info(m.ctx, digest.Digest(hash))
|
||||||
|
if err != nil {
|
||||||
|
return content.Info{}, err
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package runtime
|
||||||
|
|
||||||
// ContainerRuntime defines the interface for managing containerd images
|
// ContainerRuntime defines the interface for managing containerd images
|
||||||
type ContainerRuntime interface {
|
type ContainerRuntime interface {
|
||||||
@ -7,5 +7,6 @@ type ContainerRuntime interface {
|
|||||||
Pin(imageRef string) error
|
Pin(imageRef string) error
|
||||||
Unpin(imageRef string) error
|
Unpin(imageRef string) error
|
||||||
Pull(imageRef string) error
|
Pull(imageRef string) error
|
||||||
|
Tag(imageFrom, imageTo string) error
|
||||||
Remove(imageRef string) error
|
Remove(imageRef string) error
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package support
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
Loading…
Reference in New Issue
Block a user