github.com/saucelabs/saucectl@v0.175.1/internal/cmd/docker/push.go (about) 1 package docker 2 3 import ( 4 "context" 5 "encoding/base64" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io" 10 "os" 11 "time" 12 13 "github.com/docker/docker/api/types" 14 "github.com/docker/docker/api/types/registry" 15 "github.com/docker/docker/client" 16 cmds "github.com/saucelabs/saucectl/internal/cmd" 17 "github.com/saucelabs/saucectl/internal/segment" 18 "github.com/saucelabs/saucectl/internal/usage" 19 "github.com/spf13/cobra" 20 "golang.org/x/text/cases" 21 "golang.org/x/text/language" 22 ) 23 24 func PushCommand() *cobra.Command { 25 var timeout time.Duration 26 var quiet bool 27 28 cmd := &cobra.Command{ 29 Use: "push <image_name>", 30 Short: "Push a Docker image to the Sauce Labs Container Registry.", 31 SilenceUsage: true, 32 Args: func(cmd *cobra.Command, args []string) error { 33 if len(args) == 0 || args[0] == "" { 34 return errors.New("no docker image specified") 35 } 36 37 return nil 38 }, 39 PreRun: func(cmd *cobra.Command, args []string) { 40 tracker := segment.DefaultTracker 41 42 go func() { 43 tracker.Collect( 44 cases.Title(language.English).String(cmds.FullName(cmd)), 45 usage.Properties{}.SetFlags(cmd.Flags()), 46 ) 47 _ = tracker.Close() 48 }() 49 }, 50 RunE: func(cmd *cobra.Command, args []string) error { 51 image := args[0] 52 auth, err := imageRunnerService.RegistryLogin(context.Background(), image) 53 if err != nil { 54 return fmt.Errorf("failed to fetch auth token: %v", err) 55 } 56 return pushDockerImage(image, auth.Username, auth.Password, timeout, quiet) 57 }, 58 } 59 60 flags := cmd.PersistentFlags() 61 flags.DurationVar(&timeout, "timeout", 5*time.Minute, "Configure the timeout duration for docker push.") 62 flags.BoolVar(&quiet, "quiet", false, "Run silently, suppressing output messages.") 63 64 return cmd 65 } 66 67 func pushDockerImage(imageName, username, password string, timeout time.Duration, quiet bool) error { 68 ctx, cancel := context.WithTimeout(context.Background(), timeout) 69 defer cancel() 70 71 cli, err := client.NewClientWithOpts(client.FromEnv) 72 if err != nil { 73 return fmt.Errorf("failed to create docker client: %v", err) 74 } 75 76 authConfig := registry.AuthConfig{ 77 Username: username, 78 Password: password, 79 } 80 81 authBytes, err := json.Marshal(authConfig) 82 if err != nil { 83 return fmt.Errorf("failed to marshal docker auth: %v", err) 84 } 85 authBase64 := base64.URLEncoding.EncodeToString(authBytes) 86 87 // Push the image to the registry 88 pushOptions := types.ImagePushOptions{RegistryAuth: authBase64} 89 out, err := cli.ImagePush(ctx, imageName, pushOptions) 90 if err != nil { 91 return fmt.Errorf("failed to push image: %v", err) 92 } 93 defer out.Close() 94 95 if quiet { 96 return nil 97 } 98 99 // Print the push output 100 _, err = io.Copy(os.Stdout, out) 101 if err != nil { 102 return fmt.Errorf("docker output: %v", err) 103 } 104 105 return nil 106 }