Skip to content

Commit e08fd66

Browse files
feat: allow materialize with a prefix (#635)
allow for materialize subcommand to accept a prefix so all materialized binaries will be placed under the provided directory.
1 parent 0ba543f commit e08fd66

File tree

3 files changed

+236
-187
lines changed

3 files changed

+236
-187
lines changed

cmd/embedded-cluster/materialize.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,22 @@ var materializeCommand = &cli.Command{
1313
Name: "materialize",
1414
Usage: "Materialize embedded assets on /var/lib/embedded-cluster",
1515
Hidden: true,
16+
Flags: []cli.Flag{
17+
&cli.StringFlag{
18+
Name: "basedir",
19+
Usage: "Base directory to materialize assets",
20+
Value: "",
21+
},
22+
},
1623
Before: func(c *cli.Context) error {
1724
if os.Getuid() != 0 {
1825
return fmt.Errorf("materialize command must be run as root")
1926
}
2027
return nil
2128
},
2229
Action: func(c *cli.Context) error {
23-
if err := goods.Materialize(); err != nil {
30+
materializer := goods.NewMaterializer(c.String("basedir"))
31+
if err := materializer.Materialize(); err != nil {
2432
return fmt.Errorf("unable to materialize: %v", err)
2533
}
2634
return nil

pkg/goods/goods.go

Lines changed: 21 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -3,208 +3,43 @@
33
package goods
44

55
import (
6-
"crypto/sha256"
76
"embed"
8-
"encoding/hex"
9-
"fmt"
10-
"io"
11-
"os"
7+
)
128

13-
"github.com/replicatedhq/embedded-cluster/pkg/defaults"
9+
var (
10+
// materializer is our default instace of the artifact materializer.
11+
materializer = NewMaterializer("")
12+
//go:embed bins/*
13+
binfs embed.FS
14+
//go:embed support/*
15+
supportfs embed.FS
16+
//go:embed systemd/*
17+
systemdfs embed.FS
18+
//go:embed internal/bins/*
19+
internalBinfs embed.FS
1420
)
1521

1622
// K0sBinarySHA256 returns the SHA256 checksum of the embedded k0s binary.
1723
func K0sBinarySHA256() (string, error) {
18-
fp, err := binfs.Open("bins/k0s")
19-
if err != nil {
20-
return "", fmt.Errorf("unable to open embedded k0s binary: %w", err)
21-
}
22-
defer fp.Close()
23-
hasher := sha256.New()
24-
if _, err := io.Copy(hasher, fp); err != nil {
25-
return "", fmt.Errorf("unable to copy embedded k0s binary: %w", err)
26-
}
27-
return hex.EncodeToString(hasher.Sum(nil)), nil
28-
}
29-
30-
//go:embed bins/*
31-
var binfs embed.FS
32-
33-
// materializeOurselves makes a copy of the embedded-cluster binary into the PathToEmbeddedClusterBinary()
34-
// directory. We are doing this copy for three reasons: 1. We make sure we have it in a standard location
35-
// across all installations. 2. We can overwrite it during cluster upgrades. 3. we can serve a copy of the
36-
// binary through the local-artifact-mirror daemon.
37-
func materializeOurselves() error {
38-
srcpath, err := os.Executable()
39-
if err != nil {
40-
return fmt.Errorf("unable to get our own executable path: %w", err)
41-
}
42-
43-
dstpath := defaults.PathToEmbeddedClusterBinary(defaults.BinaryName())
44-
if srcpath == dstpath {
45-
return nil
46-
}
47-
48-
if _, err := os.Stat(dstpath); err == nil {
49-
tmp := fmt.Sprintf("%s.bkp", dstpath)
50-
if err := os.Rename(dstpath, tmp); err != nil {
51-
return fmt.Errorf("unable to rename %s to %s: %w", dstpath, tmp, err)
52-
}
53-
defer os.Remove(tmp)
54-
}
55-
56-
src, err := os.Open(srcpath)
57-
if err != nil {
58-
return fmt.Errorf("unable to open source file: %w", err)
59-
}
60-
defer src.Close()
61-
62-
opts := os.O_CREATE | os.O_WRONLY | os.O_TRUNC
63-
dst, err := os.OpenFile(dstpath, opts, 0755)
64-
if err != nil {
65-
return fmt.Errorf("unable to open destination file: %w", err)
66-
}
67-
defer dst.Close()
68-
69-
if _, err := io.Copy(dst, src); err != nil {
70-
return fmt.Errorf("unable to write file: %w", err)
71-
}
72-
return nil
73-
}
74-
75-
// materializeBinaries materializes all binary files from inside bins directory. If the
76-
// file already exists a copy of it is made first before overwriting it, this is done
77-
// because we can't overwrite a running binary. Copies are removed. This function also
78-
// creates a copy of this binary into the PathToEmbeddedClusterBinary() directory.
79-
func materializeBinaries() error {
80-
entries, err := binfs.ReadDir("bins")
81-
if err != nil {
82-
return fmt.Errorf("unable to read embedded-cluster bins dir: %w", err)
83-
}
84-
85-
if err := materializeOurselves(); err != nil {
86-
return fmt.Errorf("unable to materialize ourselves: %w", err)
87-
}
88-
89-
var remove []string
90-
defer func() {
91-
for _, f := range remove {
92-
os.Remove(f)
93-
}
94-
}()
95-
96-
for _, entry := range entries {
97-
srcpath := fmt.Sprintf("bins/%s", entry.Name())
98-
srcfile, err := binfs.ReadFile(srcpath)
99-
if err != nil {
100-
return fmt.Errorf("unable to read asset: %w", err)
101-
}
102-
103-
dstpath := defaults.PathToEmbeddedClusterBinary(entry.Name())
104-
if _, err := os.Stat(dstpath); err == nil {
105-
tmp := fmt.Sprintf("%s.bkp", dstpath)
106-
if err := os.Rename(dstpath, tmp); err != nil {
107-
return fmt.Errorf("unable to rename %s to %s: %w", dstpath, tmp, err)
108-
}
109-
remove = append(remove, tmp)
110-
}
111-
112-
if err := os.WriteFile(dstpath, srcfile, 0755); err != nil {
113-
return fmt.Errorf("unable to write file: %w", err)
114-
}
115-
}
116-
117-
return nil
24+
return materializer.K0sBinarySHA256()
11825
}
11926

120-
//go:embed support/*
121-
var supportfs embed.FS
122-
123-
// materializeSupportFiles materializes all support files from inside support directory.
124-
func materializeSupportFiles() error {
125-
entries, err := supportfs.ReadDir("support")
126-
if err != nil {
127-
return fmt.Errorf("unable to read embedded-cluster support dir: %w", err)
128-
}
129-
for _, entry := range entries {
130-
srcpath := fmt.Sprintf("support/%s", entry.Name())
131-
srcfile, err := supportfs.ReadFile(srcpath)
132-
if err != nil {
133-
return fmt.Errorf("unable to read asset: %w", err)
134-
}
135-
dstpath := defaults.PathToEmbeddedClusterSupportFile(entry.Name())
136-
if err := os.WriteFile(dstpath, srcfile, 0700); err != nil {
137-
return fmt.Errorf("unable to write file: %w", err)
138-
}
139-
}
140-
return nil
141-
}
142-
143-
// Materialize writes to disk all embedded assets.
27+
// Materialize writes to disk all embedded assets using the default materializer.
14428
func Materialize() error {
145-
if err := materializeBinaries(); err != nil {
146-
return fmt.Errorf("unable to materialize embedded binaries: %w", err)
147-
}
148-
if err := materializeSupportFiles(); err != nil {
149-
return fmt.Errorf("unable to materialize embedded support files: %w", err)
150-
}
151-
return nil
29+
return materializer.Materialize()
15230
}
15331

154-
//go:embed systemd/*
155-
var systemdfs embed.FS
156-
157-
// MaterializeCalicoNetworkManagerConfig materializes a configuration file for the network manager.
158-
// This configuration file instructs the network manager to ignore any interface being managed by
159-
// the calico network cni.
32+
// MaterializeCalicoNetworkManagerConfig is a helper function that uses the default materializer.
16033
func MaterializeCalicoNetworkManagerConfig() error {
161-
content, err := systemdfs.ReadFile("systemd/calico-network-manager.conf")
162-
if err != nil {
163-
return fmt.Errorf("unable to open network manager config file: %w", err)
164-
}
165-
dstpath := "/etc/NetworkManager/conf.d/embedded-cluster.conf"
166-
if err := os.WriteFile(dstpath, content, 0644); err != nil {
167-
return fmt.Errorf("unable to write file: %w", err)
168-
}
169-
return nil
34+
return materializer.CalicoNetworkManagerConfig()
17035
}
17136

172-
// MaterializeLocalArtifactMirrorUnitFile writes to disk the local-artifact-mirror systemd unit file.
37+
// MaterializeLocalArtifactMirrorUnitFile is a helper function that uses the default materializer.
17338
func MaterializeLocalArtifactMirrorUnitFile() error {
174-
content, err := systemdfs.ReadFile("systemd/local-artifact-mirror.service")
175-
if err != nil {
176-
return fmt.Errorf("unable to open unit file: %w", err)
177-
}
178-
dstpath := "/etc/systemd/system/local-artifact-mirror.service"
179-
if err := os.WriteFile(dstpath, content, 0644); err != nil {
180-
return fmt.Errorf("unable to write file: %w", err)
181-
}
182-
return nil
39+
return materializer.LocalArtifactMirrorUnitFile()
18340
}
18441

185-
//go:embed internal/bins/*
186-
var internalBinfs embed.FS
187-
188-
// MaterializeInternalBinary materializes an internal binary from inside internal/bins directory
189-
// and writes it to a tmp file. It returns the path to the materialized binary.
190-
// The binary should be deleted after it is used.
191-
// This is used for binaries that are not meant to be exposed to the user.
42+
// MaterializeInternalBinary is a helper for the default materializer.
19243
func MaterializeInternalBinary(name string) (string, error) {
193-
srcpath := fmt.Sprintf("internal/bins/%s", name)
194-
srcfile, err := internalBinfs.ReadFile(srcpath)
195-
if err != nil {
196-
return "", fmt.Errorf("unable to read asset: %w", err)
197-
}
198-
dstpath, err := os.CreateTemp("", fmt.Sprintf("embedded-cluster-%s-bin-", name))
199-
if err != nil {
200-
return "", fmt.Errorf("unable to create temp file: %w", err)
201-
}
202-
defer dstpath.Close()
203-
if _, err := dstpath.Write(srcfile); err != nil {
204-
return "", fmt.Errorf("unable to write file: %w", err)
205-
}
206-
if err := dstpath.Chmod(0755); err != nil {
207-
return "", fmt.Errorf("unable to set executable permissions: %w", err)
208-
}
209-
return dstpath.Name(), nil
44+
return materializer.InternalBinary(name)
21045
}

0 commit comments

Comments
 (0)