2023-05-16 15:59:04 +00:00
package utils
import (
"context"
2023-06-20 06:43:43 +00:00
"errors"
2023-05-16 15:59:04 +00:00
"fmt"
2023-12-05 10:09:10 +00:00
"io"
"net/http"
"runtime"
"strings"
"syscall"
"time"
2023-05-16 15:59:04 +00:00
"github.com/containerd/containerd/archive"
2024-06-05 07:23:51 +00:00
registrytypes "github.com/docker/docker/api/types/registry"
2023-05-16 15:59:04 +00:00
"github.com/google/go-containerregistry/pkg/authn"
2023-06-20 06:43:43 +00:00
"github.com/google/go-containerregistry/pkg/logs"
2023-05-16 15:59:04 +00:00
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/daemon"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/remote"
2023-06-20 06:43:43 +00:00
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
2023-05-16 15:59:04 +00:00
)
2024-06-05 07:23:51 +00:00
// referrence: https://github.com/mudler/luet/blob/master/pkg/helpers/docker/docker.go#L117
type staticAuth struct {
auth * registrytypes . AuthConfig
}
func ( s staticAuth ) Authorization ( ) ( * authn . AuthConfig , error ) {
if s . auth == nil {
return nil , nil
}
return & authn . AuthConfig {
Username : s . auth . Username ,
Password : s . auth . Password ,
Auth : s . auth . Auth ,
IdentityToken : s . auth . IdentityToken ,
RegistryToken : s . auth . RegistryToken ,
} , nil
}
2023-06-20 06:43:43 +00:00
var defaultRetryBackoff = remote . Backoff {
Duration : 1.0 * time . Second ,
Factor : 3.0 ,
Jitter : 0.1 ,
Steps : 3 ,
}
var defaultRetryPredicate = func ( err error ) bool {
if err == nil {
return false
}
if errors . Is ( err , io . ErrUnexpectedEOF ) || errors . Is ( err , io . EOF ) || errors . Is ( err , syscall . EPIPE ) || errors . Is ( err , syscall . ECONNRESET ) || strings . Contains ( err . Error ( ) , "connection refused" ) {
logs . Warn . Printf ( "retrying %v" , err )
return true
}
return false
}
// ExtractOCIImage will extract a given targetImage into a given targetDestination
2023-12-05 10:09:10 +00:00
func ExtractOCIImage ( img v1 . Image , targetDestination string ) error {
2023-05-16 15:59:04 +00:00
reader := mutate . Extract ( img )
2023-12-05 10:09:10 +00:00
_ , err := archive . Apply ( context . Background ( ) , targetDestination , reader )
return err
2023-05-16 15:59:04 +00:00
}
2023-12-05 10:09:10 +00:00
// GetImage if returns the proper image to pull with transport and auth
2023-06-20 06:43:43 +00:00
// tries local daemon first and then fallbacks into remote
2024-06-05 07:23:51 +00:00
// if auth is nil, it will try to use the default keychain https://github.com/google/go-containerregistry/tree/main/pkg/authn#tldr-for-consumers-of-this-package
func GetImage ( targetImage , targetPlatform string , auth * registrytypes . AuthConfig ) ( v1 . Image , error ) {
2023-08-07 14:40:31 +00:00
var platform * v1 . Platform
2023-06-20 06:43:43 +00:00
var image v1 . Image
var err error
2023-08-07 14:40:31 +00:00
if targetPlatform != "" {
platform , err = v1 . ParsePlatform ( targetPlatform )
if err != nil {
return image , err
}
} else {
platform , err = v1 . ParsePlatform ( fmt . Sprintf ( "%s/%s" , runtime . GOOS , runtime . GOARCH ) )
if err != nil {
return image , err
}
}
ref , err := name . ParseReference ( targetImage )
if err != nil {
return image , err
}
2023-06-20 06:43:43 +00:00
tr := transport . NewRetry ( http . DefaultTransport ,
transport . WithRetryBackoff ( defaultRetryBackoff ) ,
transport . WithRetryPredicate ( defaultRetryPredicate ) ,
)
image , err = daemon . Image ( ref )
if err != nil {
2024-06-05 07:23:51 +00:00
opts := [ ] remote . Option {
2023-06-20 06:43:43 +00:00
remote . WithTransport ( tr ) ,
2023-08-07 14:40:31 +00:00
remote . WithPlatform ( * platform ) ,
2024-06-05 07:23:51 +00:00
}
if auth != nil {
opts = append ( opts , remote . WithAuth ( staticAuth { auth } ) )
} else {
opts = append ( opts , remote . WithAuthFromKeychain ( authn . DefaultKeychain ) )
}
image , err = remote . Image ( ref , opts ... )
2023-05-16 15:59:04 +00:00
}
2023-06-20 06:43:43 +00:00
return image , err
2023-05-16 15:59:04 +00:00
}
2023-08-07 14:40:31 +00:00
2024-06-05 07:23:51 +00:00
func GetOCIImageSize ( targetImage , targetPlatform string , auth * registrytypes . AuthConfig ) ( int64 , error ) {
2023-08-07 14:40:31 +00:00
var size int64
var img v1 . Image
var err error
2024-06-05 07:23:51 +00:00
img , err = GetImage ( targetImage , targetPlatform , auth )
2023-08-07 14:40:31 +00:00
if err != nil {
return size , err
}
2023-08-17 10:45:07 +00:00
layers , _ := img . Layers ( )
for _ , layer := range layers {
s , _ := layer . Size ( )
size += s
2023-08-07 14:40:31 +00:00
}
return size , nil
}