11package main
22
33import (
4- "bytes"
54 "context"
65 "encoding/base64"
76 "fmt"
87 "os"
9- "os/exec"
10- "path/filepath"
118
129 ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1"
13- "github.com/replicatedhq/embedded-cluster/pkg/defaults"
14- "github.com/replicatedhq/embedded-cluster/pkg/helpers"
1510 "github.com/replicatedhq/embedded-cluster/pkg/kubeutils"
16- "github.com/replicatedhq/embedded-cluster/pkg/tgzutils"
1711 "github.com/sirupsen/logrus"
18- "github.com/urfave/cli/v2"
12+ "github.com/spf13/cobra"
13+ "github.com/spf13/viper"
1914 "k8s.io/apimachinery/pkg/runtime"
2015 "k8s.io/apimachinery/pkg/runtime/serializer"
2116 "sigs.k8s.io/controller-runtime/pkg/client"
@@ -32,189 +27,31 @@ const (
3227// kubecli holds a global reference to a Kubernetes client.
3328var kubecli client.Client
3429
35- // pullCommand pulls artifacts from the registry running in the cluster and stores
36- // them locally. This command is used during cluster upgrades when we want to fetch
37- // the most up to date images, binaries and helm charts.
38- var pullCommand = & cli.Command {
39- Name : "pull" ,
40- Usage : "Pull artifacts for an airgap installation" ,
41- Before : func (c * cli.Context ) (err error ) {
42- if kubecli , err = kubeutils .KubeClient (); err != nil {
43- return fmt .Errorf ("unable to create kube client: %w" , err )
44- }
45- return nil
46- },
47- Subcommands : []* cli.Command {binariesCommand , imagesCommand , helmChartsCommand },
48- }
49-
50- // imagesCommand pulls images from the registry running in the cluster and stores
51- // them locally. This command is used during cluster upgrades when we want to fetch
52- // the most up to date images. Images are stored in a tarball file in the default
53- // location.
54- var imagesCommand = & cli.Command {
55- Name : "images" ,
56- Usage : "Pull image artifacts for an airgap installation" ,
57- UsageText : `embedded-cluster-operator pull images <installation-name>` ,
58- Before : func (c * cli.Context ) error {
59- if os .Getuid () != 0 {
60- return fmt .Errorf ("pull images command must be run as root" )
61- }
62- if len (c .Args ().Slice ()) != 1 {
63- return fmt .Errorf ("expected installation name as argument" )
64- }
65- return nil
66- },
67- Flags : []cli.Flag {
68- getPullDataDirFlag (),
69- },
70- Action : func (c * cli.Context ) error {
71- provider := defaults .NewProvider (c .String ("data-dir" ))
72- os .Setenv ("TMPDIR" , provider .EmbeddedClusterTmpSubDir ())
73-
74- in , err := fetchAndValidateInstallation (c .Context , c .Args ().First ())
75- if err != nil {
76- return err
77- }
78-
79- from := in .Spec .Artifacts .Images
80- logrus .Infof ("fetching images artifact from %s" , from )
81- location , err := pullArtifact (c .Context , from )
82- if err != nil {
83- return fmt .Errorf ("unable to fetch artifact: %w" , err )
84- }
85- defer func () {
86- logrus .Infof ("removing temporary directory %s" , location )
87- os .RemoveAll (location )
88- }()
89-
90- dst := filepath .Join (provider .EmbeddedClusterImagesSubDir (), ImagesDstArtifactName )
91- src := filepath .Join (location , ImagesSrcArtifactName )
92- logrus .Infof ("%s > %s" , src , dst )
93- if err := helpers .MoveFile (src , dst ); err != nil {
94- return fmt .Errorf ("unable to move images bundle: %w" , err )
95- }
96-
97- logrus .Infof ("images materialized under %s" , dst )
98- return nil
99- },
100- }
101-
102- // helmChartsCommand pulls helm charts from the registry running in the cluster and
103- // stores them locally. This command is used during cluster upgrades when we want to
104- // fetch the most up to date helm charts. Helm charts are stored in a tarball file
105- // in the default location.
106- var helmChartsCommand = & cli.Command {
107- Name : "helmcharts" ,
108- Usage : "Pull Helm chart artifacts for an airgap installation" ,
109- UsageText : `embedded-cluster-operator pull helmcharts <installation-name>` ,
110- Before : func (c * cli.Context ) error {
111- if os .Getuid () != 0 {
112- return fmt .Errorf ("pull helmcharts command must be run as root" )
113- }
114- if len (c .Args ().Slice ()) != 1 {
115- return fmt .Errorf ("expected installation name as argument" )
116- }
117- return nil
118- },
119- Flags : []cli.Flag {
120- getPullDataDirFlag (),
121- },
122- Action : func (c * cli.Context ) error {
123- provider := defaults .NewProvider (c .String ("data-dir" ))
124- os .Setenv ("TMPDIR" , provider .EmbeddedClusterTmpSubDir ())
125-
126- in , err := fetchAndValidateInstallation (c .Context , c .Args ().First ())
127- if err != nil {
128- return err
129- }
130-
131- from := in .Spec .Artifacts .HelmCharts
132- logrus .Infof ("fetching helm charts artifact from %s" , from )
133- location , err := pullArtifact (c .Context , from )
134- if err != nil {
135- return fmt .Errorf ("unable to fetch artifact: %w" , err )
136- }
137- defer func () {
138- logrus .Infof ("removing temporary directory %s" , location )
139- os .RemoveAll (location )
140- }()
141-
142- dst := provider .EmbeddedClusterChartsSubDir ()
143- src := filepath .Join (location , HelmChartsArtifactName )
144- logrus .Infof ("uncompressing %s" , src )
145- if err := tgzutils .Decompress (src , dst ); err != nil {
146- return fmt .Errorf ("unable to uncompress helm charts: %w" , err )
147- }
148-
149- logrus .Infof ("helm charts materialized under %s" , dst )
150- return nil
151- },
152- }
153-
154- // binariesCommands pulls the binary artifact from the registry running in the cluster and stores
155- // it locally. This command is used during cluster upgrades when we want to fetch the most up to
156- // date binaries. The binaries are stored in the /usr/local/bin directory and they overwrite the
157- // existing binaries.
158- var binariesCommand = & cli.Command {
159- Name : "binaries" ,
160- Usage : "Pull binaries artifacts for an airgap installation" ,
161- UsageText : `embedded-cluster-operator pull binaries <installation-name>` ,
162- Before : func (c * cli.Context ) error {
163- if os .Getuid () != 0 {
164- return fmt .Errorf ("pull binaries command must be run as root" )
165- }
166- if len (c .Args ().Slice ()) != 1 {
167- return fmt .Errorf ("expected installation name as argument" )
168- }
169- return nil
170- },
171- Flags : []cli.Flag {
172- getPullDataDirFlag (),
173- },
174- Action : func (c * cli.Context ) error {
175- provider := defaults .NewProvider (c .String ("data-dir" ))
176- os .Setenv ("TMPDIR" , provider .EmbeddedClusterTmpSubDir ())
177-
178- in , err := fetchAndValidateInstallation (c .Context , c .Args ().First ())
179- if err != nil {
180- return err
181- }
182-
183- from := in .Spec .Artifacts .EmbeddedClusterBinary
184- logrus .Infof ("fetching embedded cluster binary artifact from %s" , from )
185- location , err := pullArtifact (c .Context , from )
186- if err != nil {
187- return fmt .Errorf ("unable to fetch artifact: %w" , err )
188- }
189- defer func () {
190- logrus .Infof ("removing temporary directory %s" , location )
191- os .RemoveAll (location )
192- }()
193- bin := filepath .Join (location , EmbeddedClusterBinaryArtifactName )
194- namedBin := filepath .Join (location , in .Spec .BinaryName )
195- if err := os .Rename (bin , namedBin ); err != nil {
196- return fmt .Errorf ("unable to rename binary: %w" , err )
197- }
198-
199- if err := os .Chmod (namedBin , 0755 ); err != nil {
200- return fmt .Errorf ("unable to change permissions on %s: %w" , bin , err )
201- }
202-
203- out := bytes .NewBuffer (nil )
204- cmd := exec .Command (namedBin , "materialize" , "--data-dir" , provider .EmbeddedClusterHomeDirectory ())
205- cmd .Stdout = out
206- cmd .Stderr = out
207-
208- logrus .Infof ("running command: %s materialize" , bin )
209- if err := cmd .Run (); err != nil {
210- logrus .Error (out .String ())
211- return err
212- }
30+ func PullCmd (ctx context.Context , v * viper.Viper ) * cobra.Command {
31+ cmd := & cobra.Command {
32+ Use : "pull" ,
33+ Short : "Pull artifacts for an airgap installation" ,
34+ PersistentPreRunE : func (cmd * cobra.Command , args []string ) error {
35+ kc , err := kubeutils .KubeClient ()
36+ if err != nil {
37+ return fmt .Errorf ("unable to create kube client: %w" , err )
38+ }
39+ kubecli = kc
40+
41+ return nil
42+ },
43+ RunE : func (cmd * cobra.Command , args []string ) error {
44+ cmd .Help ()
45+ os .Exit (1 )
46+ return nil
47+ },
48+ }
21349
214- logrus .Infof ("embedded cluster binaries materialized" )
50+ cmd .AddCommand (PullBinariesCmd (ctx , v ))
51+ cmd .AddCommand (PullImagesCmd (ctx , v ))
52+ cmd .AddCommand (PullHelmChartsCmd (ctx , v ))
21553
216- return nil
217- },
54+ return cmd
21855}
21956
22057// fetchAndValidateInstallation fetches an Installation object from its name or directly decodes it
@@ -265,13 +102,3 @@ func decodeInstallation(ctx context.Context, data string) (*ecv1beta1.Installati
265102
266103 return in , nil
267104}
268-
269- func getPullDataDirFlag () cli.Flag {
270- return & cli.StringFlag {
271- Name : "data-dir" ,
272- Usage : "Path to the data directory" ,
273- Value : ecv1beta1 .DefaultDataDir ,
274- EnvVars : []string {"LOCAL_ARTIFACT_MIRROR_DATA_DIR" },
275- Required : true ,
276- }
277- }
0 commit comments