github.com/nektos/act@v0.2.63/pkg/container/docker_pull.go (about) 1 //go:build !(WITHOUT_DOCKER || !(linux || darwin || windows || netbsd)) 2 3 package container 4 5 import ( 6 "context" 7 "encoding/base64" 8 "encoding/json" 9 "fmt" 10 "strings" 11 12 "github.com/docker/distribution/reference" 13 "github.com/docker/docker/api/types" 14 "github.com/docker/docker/api/types/registry" 15 16 "github.com/nektos/act/pkg/common" 17 ) 18 19 // NewDockerPullExecutor function to create a run executor for the container 20 func NewDockerPullExecutor(input NewDockerPullExecutorInput) common.Executor { 21 return func(ctx context.Context) error { 22 logger := common.Logger(ctx) 23 logger.Debugf("%sdocker pull %v", logPrefix, input.Image) 24 25 if common.Dryrun(ctx) { 26 return nil 27 } 28 29 pull := input.ForcePull 30 if !pull { 31 imageExists, err := ImageExistsLocally(ctx, input.Image, input.Platform) 32 logger.Debugf("Image exists? %v", imageExists) 33 if err != nil { 34 return fmt.Errorf("unable to determine if image already exists for image '%s' (%s): %w", input.Image, input.Platform, err) 35 } 36 37 if !imageExists { 38 pull = true 39 } 40 } 41 42 if !pull { 43 return nil 44 } 45 46 imageRef := cleanImage(ctx, input.Image) 47 logger.Debugf("pulling image '%v' (%s)", imageRef, input.Platform) 48 49 cli, err := GetDockerClient(ctx) 50 if err != nil { 51 return err 52 } 53 defer cli.Close() 54 55 imagePullOptions, err := getImagePullOptions(ctx, input) 56 if err != nil { 57 return err 58 } 59 60 reader, err := cli.ImagePull(ctx, imageRef, imagePullOptions) 61 62 _ = logDockerResponse(logger, reader, err != nil) 63 if err != nil { 64 if imagePullOptions.RegistryAuth != "" && strings.Contains(err.Error(), "unauthorized") { 65 logger.Errorf("pulling image '%v' (%s) failed with credentials %s retrying without them, please check for stale docker config files", imageRef, input.Platform, err.Error()) 66 imagePullOptions.RegistryAuth = "" 67 reader, err = cli.ImagePull(ctx, imageRef, imagePullOptions) 68 69 _ = logDockerResponse(logger, reader, err != nil) 70 } 71 return err 72 } 73 return nil 74 } 75 } 76 77 func getImagePullOptions(ctx context.Context, input NewDockerPullExecutorInput) (types.ImagePullOptions, error) { 78 imagePullOptions := types.ImagePullOptions{ 79 Platform: input.Platform, 80 } 81 logger := common.Logger(ctx) 82 83 if input.Username != "" && input.Password != "" { 84 logger.Debugf("using authentication for docker pull") 85 86 authConfig := registry.AuthConfig{ 87 Username: input.Username, 88 Password: input.Password, 89 } 90 91 encodedJSON, err := json.Marshal(authConfig) 92 if err != nil { 93 return imagePullOptions, err 94 } 95 96 imagePullOptions.RegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON) 97 } else { 98 authConfig, err := LoadDockerAuthConfig(ctx, input.Image) 99 if err != nil { 100 return imagePullOptions, err 101 } 102 if authConfig.Username == "" && authConfig.Password == "" { 103 return imagePullOptions, nil 104 } 105 logger.Info("using DockerAuthConfig authentication for docker pull") 106 107 encodedJSON, err := json.Marshal(authConfig) 108 if err != nil { 109 return imagePullOptions, err 110 } 111 112 imagePullOptions.RegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON) 113 } 114 115 return imagePullOptions, nil 116 } 117 118 func cleanImage(ctx context.Context, image string) string { 119 ref, err := reference.ParseAnyReference(image) 120 if err != nil { 121 common.Logger(ctx).Error(err) 122 return "" 123 } 124 125 return ref.String() 126 }