github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/ociinstaller/ocidownloader.go (about) 1 package ociinstaller 2 3 import ( 4 "context" 5 "encoding/json" 6 "log" 7 "strings" 8 9 "github.com/containerd/containerd/remotes" 10 "github.com/containerd/containerd/remotes/docker" 11 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 12 "github.com/sirupsen/logrus" 13 "oras.land/oras-go/v2" 14 "oras.land/oras-go/v2/content" 15 "oras.land/oras-go/v2/content/file" 16 "oras.land/oras-go/v2/content/memory" 17 "oras.land/oras-go/v2/registry/remote" 18 "oras.land/oras-go/v2/registry/remote/auth" 19 "oras.land/oras-go/v2/registry/remote/credentials" 20 "oras.land/oras-go/v2/registry/remote/retry" 21 ) 22 23 type ociDownloader struct { 24 resolver remotes.Resolver 25 Images []*SteampipeImage 26 } 27 28 // NewOciDownloader creates and returns a ociDownloader instance 29 func NewOciDownloader() *ociDownloader { 30 // oras uses containerd, which uses logrus and is set up to log 31 // warning and above. Set to ErrrLevel to get rid of unwanted error message 32 logrus.SetLevel(logrus.ErrorLevel) 33 return &ociDownloader{ 34 resolver: docker.NewResolver(docker.ResolverOptions{}), 35 } 36 } 37 38 /* 39 Pull downloads the image from the given `ref` to the supplied `destDir` 40 41 Returns 42 43 imageDescription, configDescription, config, imageLayers, error 44 */ 45 func (o *ociDownloader) Pull(ctx context.Context, ref string, mediaTypes []string, destDir string) (*ocispec.Descriptor, *ocispec.Descriptor, []byte, []ocispec.Descriptor, error) { 46 split := strings.Split(ref, ":") 47 tag := split[len(split)-1] 48 log.Println("[TRACE] ociDownloader.Pull:", "preparing to pull ref", ref, "tag", tag, "destDir", destDir) 49 50 // Create the target file store 51 memoryStore := memory.New() 52 fileStore, err := file.NewWithFallbackStorage(destDir, memoryStore) 53 if err != nil { 54 return nil, nil, nil, nil, err 55 } 56 defer fileStore.Close() 57 58 // Connect to the remote repository 59 repo, err := remote.NewRepository(ref) 60 if err != nil { 61 return nil, nil, nil, nil, err 62 } 63 64 // Get credentials from the docker credentials store 65 storeOpts := credentials.StoreOptions{} 66 credStore, err := credentials.NewStoreFromDocker(storeOpts) 67 if err != nil { 68 return nil, nil, nil, nil, err 69 } 70 71 // Prepare the auth client for the registry and credential store 72 repo.Client = &auth.Client{ 73 Client: retry.DefaultClient, 74 Cache: auth.DefaultCache, 75 Credential: credentials.Credential(credStore), // Use the credential store 76 } 77 78 // Copy from the remote repository to the file store 79 log.Println("[TRACE] ociDownloader.Pull:", "pulling...") 80 81 copyOpt := oras.DefaultCopyOptions 82 manifestDescriptor, err := oras.Copy(ctx, repo, tag, fileStore, tag, copyOpt) 83 if err != nil { 84 log.Println("[TRACE] ociDownloader.Pull:", "failed to pull", ref, err) 85 return nil, nil, nil, nil, err 86 } 87 log.Println("[TRACE] ociDownloader.Pull:", "manifest", manifestDescriptor.Digest, manifestDescriptor.MediaType) 88 89 // FIXME: this seems redundant as oras.Copy() already downloads all artifacts, but that's the only I found 90 // to access the manifest config. Also, it shouldn't be an issue as files are not re-downloaded. 91 manifestJson, err := content.FetchAll(ctx, fileStore, manifestDescriptor) 92 if err != nil { 93 log.Println("[TRACE] ociDownloader.Pull:", "failed to fetch manifest", manifestDescriptor) 94 return nil, nil, nil, nil, err 95 } 96 log.Println("[TRACE] ociDownloader.Pull:", "manifest content", string(manifestJson)) 97 98 // Parse the fetched manifest 99 var manifest ocispec.Manifest 100 err = json.Unmarshal(manifestJson, &manifest) 101 if err != nil { 102 log.Println("[TRACE] ociDownloader.Pull:", "failed to unmarshall manifest", manifestJson) 103 return nil, nil, nil, nil, err 104 } 105 106 // Fetch the config from the file store 107 configData, err := content.FetchAll(ctx, fileStore, manifest.Config) 108 if err != nil { 109 log.Println("[TRACE] ociDownloader.Pull:", "failed to fetch config", manifest.Config.MediaType, err) 110 return nil, nil, nil, nil, err 111 } 112 log.Println("[TRACE] ociDownloader.Pull:", "config", string(configData)) 113 114 return &manifestDescriptor, &manifest.Config, configData, manifest.Layers, err 115 }