Skip to content

Commit 7f94395

Browse files
committed
Merge remote-tracking branch 'origin/main' into k0s-1-29
2 parents 093d955 + 5f8a747 commit 7f94395

File tree

24 files changed

+1165
-427
lines changed

24 files changed

+1165
-427
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,3 +350,7 @@ delete-node%:
350350
.PHONY: %-down
351351
%-down:
352352
@dev/scripts/down.sh $*
353+
354+
.PHONY: test-lam-e2e
355+
test-lam-e2e: pkg/goods/bins/local-artifact-mirror
356+
sudo go test ./cmd/local-artifact-mirror/e2e/... -v

cmd/local-artifact-mirror/main.go

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,57 @@ import (
66
"os"
77
"os/signal"
88
"path"
9+
"strings"
910
"syscall"
1011

11-
"github.com/urfave/cli/v2"
12+
"github.com/spf13/cobra"
13+
"github.com/spf13/viper"
1214
)
1315

1416
func main() {
15-
ctx, cancel := signal.NotifyContext(
16-
context.Background(),
17-
syscall.SIGINT,
18-
syscall.SIGTERM,
19-
)
17+
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
2018
defer cancel()
19+
2120
name := path.Base(os.Args[0])
22-
var app = &cli.App{
23-
Name: name,
24-
Usage: "Run or pull data for the local artifact mirror",
25-
Commands: []*cli.Command{serveCommand, pullCommand},
21+
22+
InitAndExecute(ctx, name)
23+
}
24+
25+
func RootCmd(ctx context.Context, v *viper.Viper, name string) *cobra.Command {
26+
cmd := &cobra.Command{
27+
Use: name,
28+
Short: "Run or pull data for the local artifact mirror",
29+
PersistentPreRun: func(cmd *cobra.Command, args []string) {
30+
v.BindPFlags(cmd.Flags())
31+
},
32+
RunE: func(cmd *cobra.Command, args []string) error {
33+
cmd.Help()
34+
os.Exit(1)
35+
return nil
36+
},
2637
}
27-
if err := app.RunContext(ctx, os.Args); err != nil {
38+
39+
cobra.OnInitialize(func() {
40+
initConfig(v)
41+
})
42+
43+
cmd.AddCommand(ServeCmd(ctx, v))
44+
cmd.AddCommand(PullCmd(ctx, v))
45+
46+
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
47+
48+
return cmd
49+
}
50+
51+
func InitAndExecute(ctx context.Context, name string) {
52+
v := viper.GetViper()
53+
if err := RootCmd(ctx, v, name).Execute(); err != nil {
2854
fmt.Println(err)
2955
os.Exit(1)
3056
}
3157
}
58+
59+
func initConfig(v *viper.Viper) {
60+
v.SetEnvPrefix("REPLICATED")
61+
v.AutomaticEnv()
62+
}

cmd/local-artifact-mirror/pull.go

Lines changed: 25 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
package main
22

33
import (
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.
3328
var 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

Comments
 (0)