Skip to content

Commit a1e8816

Browse files
committed
Create airgapinfo package to parse airgap bundle metadata
Signed-off-by: Evans Mungai <evans@replicated.com>
1 parent 3fff57f commit a1e8816

File tree

10 files changed

+130
-95
lines changed

10 files changed

+130
-95
lines changed

cmd/installer/cli/install.go

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,16 @@ func InstallCmd(ctx context.Context, name string) *cobra.Command {
9595
cancel() // Cancel context when command completes
9696
},
9797
RunE: func(cmd *cobra.Command, args []string) error {
98-
if err := verifyAndPrompt(ctx, name, flags, prompts.New()); err != nil {
98+
var airgapInfo *kotsv1beta1.Airgap
99+
if flags.airgapBundle != "" {
100+
var err error
101+
airgapInfo, err = airgap.AirgapInfoFromPath(flags.airgapBundle)
102+
if err != nil {
103+
return fmt.Errorf("failed to get airgap info: %w", err)
104+
}
105+
}
106+
107+
if err := verifyAndPrompt(ctx, name, flags, prompts.New(), airgapInfo); err != nil {
99108
return err
100109
}
101110
if err := preRunInstall(cmd, &flags, rc); err != nil {
@@ -571,7 +580,7 @@ func getAddonInstallOpts(flags InstallCmdFlags, rc runtimeconfig.RuntimeConfig,
571580
return opts, nil
572581
}
573582

574-
func verifyAndPrompt(ctx context.Context, name string, flags InstallCmdFlags, prompt prompts.Prompt) error {
583+
func verifyAndPrompt(ctx context.Context, name string, flags InstallCmdFlags, prompt prompts.Prompt, airgapInfo *kotsv1beta1.Airgap) error {
575584
logrus.Debugf("checking if k0s is already installed")
576585
err := verifyNoInstallation(name, "reinstall")
577586
if err != nil {
@@ -588,9 +597,9 @@ func verifyAndPrompt(ctx context.Context, name string, flags InstallCmdFlags, pr
588597
if err != nil {
589598
return err
590599
}
591-
if flags.isAirgap {
600+
if airgapInfo != nil {
592601
logrus.Debugf("checking airgap bundle matches binary")
593-
if err := checkAirgapMatches(flags.airgapBundle); err != nil {
602+
if err := checkAirgapMatches(airgapInfo); err != nil {
594603
return err // we want the user to see the error message without a prefix
595604
}
596605
}
@@ -885,23 +894,15 @@ func installExtensions(ctx context.Context, hcli helm.Client) error {
885894
return nil
886895
}
887896

888-
func checkAirgapMatches(airgapBundle string) error {
897+
func checkAirgapMatches(airgapInfo *kotsv1beta1.Airgap) error {
889898
rel := release.GetChannelRelease()
890899
if rel == nil {
891900
return fmt.Errorf("airgap bundle provided but no release was found in binary, please rerun without the airgap-bundle flag")
892901
}
893902

894-
// read file from path
895-
rawfile, err := os.Open(airgapBundle)
896-
if err != nil {
897-
return fmt.Errorf("failed to open airgap file: %w", err)
898-
}
899-
defer rawfile.Close()
900-
901-
appSlug, channelID, airgapVersion, err := airgap.ChannelReleaseMetadata(rawfile)
902-
if err != nil {
903-
return fmt.Errorf("failed to get airgap bundle versions: %w", err)
904-
}
903+
appSlug := airgapInfo.Spec.AppSlug
904+
channelID := airgapInfo.Spec.ChannelID
905+
airgapVersion := airgapInfo.Spec.VersionLabel
905906

906907
// Check if the airgap bundle matches the application version data
907908
if rel.AppSlug != appSlug {

cmd/installer/cli/install_runpreflights.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import (
77

88
"github.com/replicatedhq/embedded-cluster/pkg-new/hostutils"
99
"github.com/replicatedhq/embedded-cluster/pkg-new/preflights"
10+
"github.com/replicatedhq/embedded-cluster/pkg/airgap"
1011
"github.com/replicatedhq/embedded-cluster/pkg/metrics"
1112
"github.com/replicatedhq/embedded-cluster/pkg/netutils"
1213
"github.com/replicatedhq/embedded-cluster/pkg/prompts"
1314
"github.com/replicatedhq/embedded-cluster/pkg/release"
1415
"github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig"
16+
kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
1517
"github.com/sirupsen/logrus"
1618
"github.com/spf13/cobra"
1719
)
@@ -41,7 +43,16 @@ func InstallRunPreflightsCmd(ctx context.Context, name string) *cobra.Command {
4143
rc.Cleanup()
4244
},
4345
RunE: func(cmd *cobra.Command, args []string) error {
44-
if err := runInstallRunPreflights(cmd.Context(), name, flags, rc); err != nil {
46+
var airgapInfo *kotsv1beta1.Airgap
47+
if flags.airgapBundle != "" {
48+
var err error
49+
airgapInfo, err = airgap.AirgapInfoFromPath(flags.airgapBundle)
50+
if err != nil {
51+
return fmt.Errorf("failed to get airgap info: %w", err)
52+
}
53+
}
54+
55+
if err := runInstallRunPreflights(cmd.Context(), name, flags, rc, airgapInfo); err != nil {
4556
return err
4657
}
4758

@@ -59,8 +70,8 @@ func InstallRunPreflightsCmd(ctx context.Context, name string) *cobra.Command {
5970
return cmd
6071
}
6172

62-
func runInstallRunPreflights(ctx context.Context, name string, flags InstallCmdFlags, rc runtimeconfig.RuntimeConfig) error {
63-
if err := verifyAndPrompt(ctx, name, flags, prompts.New()); err != nil {
73+
func runInstallRunPreflights(ctx context.Context, name string, flags InstallCmdFlags, rc runtimeconfig.RuntimeConfig, airgapInfo *kotsv1beta1.Airgap) error {
74+
if err := verifyAndPrompt(ctx, name, flags, prompts.New(), airgapInfo); err != nil {
6475
return err
6576
}
6677

cmd/installer/cli/restore.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,14 @@ func runRestore(ctx context.Context, name string, flags InstallCmdFlags, rc runt
135135

136136
if flags.isAirgap {
137137
logrus.Debugf("checking airgap bundle matches binary")
138-
if err := checkAirgapMatches(flags.airgapBundle); err != nil {
138+
139+
// read file from path
140+
airgapInfo, err := airgap.AirgapInfoFromPath(flags.airgapBundle)
141+
if err != nil {
142+
return fmt.Errorf("failed to get airgap bundle versions: %w", err)
143+
}
144+
145+
if err := checkAirgapMatches(airgapInfo); err != nil {
139146
return err // we want the user to see the error message without a prefix
140147
}
141148
}

cmd/installer/cli/update.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"os"
77

88
"github.com/replicatedhq/embedded-cluster/cmd/installer/kotscli"
9+
"github.com/replicatedhq/embedded-cluster/pkg/airgap"
910
"github.com/replicatedhq/embedded-cluster/pkg/release"
1011
"github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig"
1112
rcutil "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig/util"
@@ -42,7 +43,14 @@ func UpdateCmd(ctx context.Context, name string) *cobra.Command {
4243
RunE: func(cmd *cobra.Command, args []string) error {
4344
if airgapBundle != "" {
4445
logrus.Debugf("checking airgap bundle matches binary")
45-
if err := checkAirgapMatches(airgapBundle); err != nil {
46+
47+
// read file from path
48+
airgapInfo, err := airgap.AirgapInfoFromPath(airgapBundle)
49+
if err != nil {
50+
return fmt.Errorf("failed to get airgap bundle versions: %w", err)
51+
}
52+
53+
if err := checkAirgapMatches(airgapInfo); err != nil {
4654
return err // we want the user to see the error message without a prefix
4755
}
4856
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ require (
3232
github.com/onsi/gomega v1.37.0
3333
github.com/replicatedhq/embedded-cluster/kinds v0.0.0
3434
github.com/replicatedhq/embedded-cluster/utils v0.0.0
35-
github.com/replicatedhq/kotskinds v0.0.0-20250411153224-089dbeb7ba2a
35+
github.com/replicatedhq/kotskinds v0.0.0-20250609144916-baa60600998c
3636
github.com/replicatedhq/troubleshoot v0.119.1
3737
github.com/sirupsen/logrus v1.9.3
3838
github.com/spf13/cobra v1.9.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,8 +1464,8 @@ github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnA
14641464
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
14651465
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
14661466
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
1467-
github.com/replicatedhq/kotskinds v0.0.0-20250411153224-089dbeb7ba2a h1:aNZ7qcuEmPGIUIIfxF7c0sdKR2+zL2vc5r2V8j8a49I=
1468-
github.com/replicatedhq/kotskinds v0.0.0-20250411153224-089dbeb7ba2a/go.mod h1:QjhIUu3+OmHZ09u09j3FCoTt8F3BYtQglS+OLmftu9I=
1467+
github.com/replicatedhq/kotskinds v0.0.0-20250609144916-baa60600998c h1:lnCL/wYi2BFTnxOP/lmo9WJwVPG3fk/plgJ/9NrMFw4=
1468+
github.com/replicatedhq/kotskinds v0.0.0-20250609144916-baa60600998c/go.mod h1:QjhIUu3+OmHZ09u09j3FCoTt8F3BYtQglS+OLmftu9I=
14691469
github.com/replicatedhq/troubleshoot v0.119.1 h1:AroLbVdtkPMv32va4z2lNdOcShFbT357oPZeECP2aOA=
14701470
github.com/replicatedhq/troubleshoot v0.119.1/go.mod h1:50okSHbWEqlbNQY1kCilXKphjGPBQ6JewezFOu47l8c=
14711471
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=

pkg/airgap/airgapinfo.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package airgap
2+
3+
import (
4+
"archive/tar"
5+
"compress/gzip"
6+
"fmt"
7+
"io"
8+
"os"
9+
10+
"github.com/pkg/errors"
11+
kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
12+
"sigs.k8s.io/yaml"
13+
)
14+
15+
// AirgapInfoFromReader extracts the airgap metadata from the airgap file and returns it
16+
func AirgapInfoFromReader(reader io.Reader) (metadata *kotsv1beta1.Airgap, err error) {
17+
// decompress tarball
18+
ungzip, err := gzip.NewReader(reader)
19+
if err != nil {
20+
return nil, fmt.Errorf("failed to decompress airgap file: %w", err)
21+
}
22+
defer ungzip.Close()
23+
24+
// iterate through tarball
25+
tarreader := tar.NewReader(ungzip)
26+
var nextFile *tar.Header
27+
for {
28+
nextFile, err = tarreader.Next()
29+
if err != nil {
30+
if err == io.EOF {
31+
return nil, errors.Wrapf(err, "airgap.yaml not found in airgap file")
32+
}
33+
return nil, errors.Wrapf(err, "failed to read airgap file")
34+
}
35+
36+
if nextFile.Name == "airgap.yaml" {
37+
var contents []byte
38+
contents, err = io.ReadAll(tarreader)
39+
if err != nil {
40+
return nil, errors.Wrapf(err, "failed to read airgap.yaml file within airgap file")
41+
}
42+
parsed := kotsv1beta1.Airgap{}
43+
44+
err := yaml.Unmarshal(contents, &parsed)
45+
if err != nil {
46+
return nil, errors.Wrapf(err, "failed to unmarshal airgap.yaml file within airgap file")
47+
}
48+
return &parsed, nil
49+
}
50+
}
51+
}
52+
53+
func AirgapInfoFromPath(path string) (metadata *kotsv1beta1.Airgap, err error) {
54+
reader, err := os.Open(path)
55+
if err != nil {
56+
return nil, fmt.Errorf("failed to open airgap file: %w", err)
57+
}
58+
defer reader.Close()
59+
60+
return AirgapInfoFromReader(reader)
61+
}

pkg/airgap/version_test.go renamed to pkg/airgap/airgapinfo_test.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,28 @@ func TestAirgapBundleVersions(t *testing.T) {
3636
t.Logf("Current working directory: %s", dir)
3737
airgapReader := createTarballFromDir(filepath.Join(dir, "testfiles", tt.airgapDir), nil)
3838

39-
appSlug, channelID, versionLabel, err := ChannelReleaseMetadata(airgapReader)
39+
airgapInfo, err := AirgapInfoFromReader(airgapReader)
4040
req.NoError(err)
41-
req.Equal(tt.wantAppslug, appSlug)
42-
req.Equal(tt.wantChannelid, channelID)
43-
req.Equal(tt.wantVersionlabel, versionLabel)
41+
req.Equal(tt.wantAppslug, airgapInfo.Spec.AppSlug)
42+
req.Equal(tt.wantChannelid, airgapInfo.Spec.ChannelID)
43+
req.Equal(tt.wantVersionlabel, airgapInfo.Spec.VersionLabel)
4444
})
4545
}
4646
}
4747

48+
func TestAirgapBundleSize(t *testing.T) {
49+
req := require.New(t)
50+
51+
dir, err := os.Getwd()
52+
req.NoError(err)
53+
t.Logf("Current working directory: %s", dir)
54+
airgapReader := createTarballFromDir(filepath.Join(dir, "testfiles", "tiny-airgap-noimages"), nil)
55+
56+
airgapInfo, err := AirgapInfoFromReader(airgapReader)
57+
req.NoError(err)
58+
req.Equal(int64(1234567890), airgapInfo.Spec.UncompressedSize)
59+
}
60+
4861
func createTarballFromDir(rootPath string, additionalFiles map[string][]byte) io.Reader {
4962
appTarReader, appWriter := io.Pipe()
5063
gWriter := gzip.NewWriter(appWriter)

pkg/airgap/testfiles/tiny-airgap-noimages/airgap.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ spec:
1515
signature: PQ4Zs4e4g1sgrd1lYog2i23+ixbDXX3NancOcDdK+JqD1S4elmkHhsGIUazIl15rL4YuJQdzeem0geK14PKADN+0YLzvEVm9Gw1Coq+y3ZDpUn2+On7caK4k1vckAEbomUDw7Cm5AGxYDFPiz4iC+OnKmFYdfU8FqSNT0iMUxjTvBLdlIf9VOh5wsbi5J511UCEv2Ht9UexcNGobgolrC5AUWKAvbH4mGxnSXVRSHjXtsAJaIw/Aw0RdQ830AIaTEz+L+qbgwasQG7lExkaQz2dEbywPP2nL9ZiyCOIAsjaKiclGx8HzKDCk7Womt4+WOfusqyk6mUFe/EflX/l2TA==
1616
updateCursor: "1"
1717
versionLabel: 0.1.0
18+
uncompressedSize: 1234567890
1819
status: {}

pkg/airgap/version.go

Lines changed: 0 additions & 67 deletions
This file was deleted.

0 commit comments

Comments
 (0)