Skip to content

feat: preflight storage needed to perform airgap install #2336

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0b06490
Add airgap bundle size to Installation CRD
banjoh Jun 18, 2025
e270d49
Generate operator CRDs
banjoh Jun 18, 2025
d54331b
Create airgapinfo package to parse airgap bundle metadata
banjoh Jun 18, 2025
2143149
Store airgap bundle size in Installation custom resource
banjoh Jun 19, 2025
668b930
Add airgap storage space to host preflights for controller nodes
banjoh Jun 19, 2025
473f5e1
Add controller airgap storage space calculation
banjoh Jun 19, 2025
46ca2d3
Separate airgap storage space preflight for controller and worker nodes
banjoh Jun 19, 2025
b88f435
Extract airgap.yaml once
banjoh Jun 19, 2025
5e53eca
Fix failing tests
banjoh Jun 19, 2025
9911048
Add airgap bundle size to Installation CRD
banjoh Jun 18, 2025
3fff57f
Generate operator CRDs
banjoh Jun 18, 2025
a1e8816
Create airgapinfo package to parse airgap bundle metadata
banjoh Jun 18, 2025
a3d3847
Store airgap bundle size in Installation custom resource
banjoh Jun 19, 2025
367c093
Add airgap storage space to host preflights for controller nodes
banjoh Jun 19, 2025
69038ae
Add controller airgap storage space calculation
banjoh Jun 19, 2025
a1e0ee4
Separate airgap storage space preflight for controller and worker nodes
banjoh Jun 19, 2025
ee7964f
Extract airgap.yaml once
banjoh Jun 19, 2025
d93f5cf
Fix failing tests
banjoh Jun 19, 2025
8691b79
Merge branch 'evansmungai/sc-123579/take-airgap-bundle-size-into-acco…
banjoh Jun 19, 2025
fb4f1b5
Comment out CI jobs
banjoh Jun 19, 2025
05f7910
Fix imports
banjoh Jun 19, 2025
fc96cf4
Revert ci.yaml
banjoh Jun 19, 2025
30e78de
Pass airgap info to the api
banjoh Jun 20, 2025
f72e05c
Merge remote-tracking branch 'origin/main' into evansmungai/sc-123579…
banjoh Jun 23, 2025
5206aab
Move airgapInfo to preRunInstall
banjoh Jun 24, 2025
5235538
Merge remote-tracking branch 'origin/main' into evansmungai/sc-123579…
banjoh Jun 24, 2025
00f00dc
A few more refactorings
banjoh Jun 24, 2025
9f3562b
Pass airgap info to infra manager
banjoh Jun 24, 2025
a8f86ba
Merge remote-tracking branch 'origin/main' into evansmungai/sc-123579…
banjoh Jun 26, 2025
b263856
Commit missing changes
banjoh Jun 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/replicatedhq/embedded-cluster/pkg/metrics"
"github.com/replicatedhq/embedded-cluster/pkg/release"
"github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig"
kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
"github.com/sirupsen/logrus"
httpSwagger "github.com/swaggo/http-swagger/v2"
)
Expand Down Expand Up @@ -50,6 +51,7 @@ type API struct {
tlsConfig types.TLSConfig
license []byte
airgapBundle string
airgapInfo *kotsv1beta1.Airgap
configValues string
endUserConfig *ecv1beta1.Config
logger logrus.FieldLogger
Expand Down Expand Up @@ -125,6 +127,12 @@ func WithAirgapBundle(airgapBundle string) APIOption {
}
}

func WithAirgapInfo(airgapInfo *kotsv1beta1.Airgap) APIOption {
return func(a *API) {
a.airgapInfo = airgapInfo
}
}

func WithConfigValues(configValues string) APIOption {
return func(a *API) {
a.configValues = configValues
Expand Down Expand Up @@ -190,6 +198,7 @@ func New(password string, opts ...APIOption) (*API, error) {
install.WithTLSConfig(api.tlsConfig),
install.WithLicense(api.license),
install.WithAirgapBundle(api.airgapBundle),
install.WithAirgapInfo(api.airgapInfo),
install.WithConfigValues(api.configValues),
install.WithEndUserConfig(api.endUserConfig),
)
Expand Down
8 changes: 8 additions & 0 deletions api/controllers/install/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/replicatedhq/embedded-cluster/pkg/metrics"
"github.com/replicatedhq/embedded-cluster/pkg/release"
"github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig"
kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
"github.com/sirupsen/logrus"
)

Expand Down Expand Up @@ -52,6 +53,7 @@ type InstallController struct {
tlsConfig types.TLSConfig
license []byte
airgapBundle string
airgapInfo *kotsv1beta1.Airgap
configValues string
endUserConfig *ecv1beta1.Config

Expand Down Expand Up @@ -125,6 +127,12 @@ func WithAirgapBundle(airgapBundle string) InstallControllerOption {
}
}

func WithAirgapInfo(airgapInfo *kotsv1beta1.Airgap) InstallControllerOption {
return func(c *InstallController) {
c.airgapInfo = airgapInfo
}
}

func WithConfigValues(configValues string) InstallControllerOption {
return func(c *InstallController) {
c.configValues = configValues
Expand Down
20 changes: 14 additions & 6 deletions api/controllers/install/hostpreflight.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/replicatedhq/embedded-cluster/api/internal/managers/preflight"
"github.com/replicatedhq/embedded-cluster/api/pkg/utils"
"github.com/replicatedhq/embedded-cluster/api/types"
"github.com/replicatedhq/embedded-cluster/pkg-new/preflights"
"github.com/replicatedhq/embedded-cluster/pkg/netutils"
)

Expand All @@ -33,14 +34,21 @@ func (c *InstallController) RunHostPreflights(ctx context.Context, opts RunHostP
// Get the configured custom domains
ecDomains := utils.GetDomains(c.releaseData)

// Calculate airgap storage space requirement (2x uncompressed size for controller nodes)
var controllerAirgapStorageSpace string
if c.airgapInfo != nil {
controllerAirgapStorageSpace = preflights.CalculateAirgapStorageSpace(c.airgapInfo.Spec.UncompressedSize, true)
}

// Prepare host preflights
hpf, err := c.hostPreflightManager.PrepareHostPreflights(ctx, c.rc, preflight.PrepareHostPreflightOptions{
ReplicatedAppURL: netutils.MaybeAddHTTPS(ecDomains.ReplicatedAppDomain),
ProxyRegistryURL: netutils.MaybeAddHTTPS(ecDomains.ProxyRegistryDomain),
HostPreflightSpec: c.releaseData.HostPreflights,
EmbeddedClusterConfig: c.releaseData.EmbeddedClusterConfig,
IsAirgap: c.airgapBundle != "",
IsUI: opts.IsUI,
ReplicatedAppURL: netutils.MaybeAddHTTPS(ecDomains.ReplicatedAppDomain),
ProxyRegistryURL: netutils.MaybeAddHTTPS(ecDomains.ProxyRegistryDomain),
HostPreflightSpec: c.releaseData.HostPreflights,
EmbeddedClusterConfig: c.releaseData.EmbeddedClusterConfig,
IsAirgap: c.airgapBundle != "",
IsUI: opts.IsUI,
ControllerAirgapStorageSpace: controllerAirgapStorageSpace,
})
if err != nil {
return fmt.Errorf("failed to prepare host preflights: %w", err)
Expand Down
33 changes: 25 additions & 8 deletions api/internal/managers/infra/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/replicatedhq/embedded-cluster/pkg/addons"
"github.com/replicatedhq/embedded-cluster/pkg/addons/registry"
addontypes "github.com/replicatedhq/embedded-cluster/pkg/addons/types"
"github.com/replicatedhq/embedded-cluster/pkg/airgap"
"github.com/replicatedhq/embedded-cluster/pkg/extensions"
"github.com/replicatedhq/embedded-cluster/pkg/helm"
"github.com/replicatedhq/embedded-cluster/pkg/kubeutils"
Expand Down Expand Up @@ -98,6 +99,15 @@ func (m *infraManager) install(ctx context.Context, rc runtimeconfig.RuntimeConf
return fmt.Errorf("parse license: %w", err)
}

var airgapInfo *kotsv1beta1.Airgap
if m.airgapBundle != "" {
var err error
airgapInfo, err = airgap.AirgapInfoFromPath(m.airgapBundle)
if err != nil {
return fmt.Errorf("failed to get airgap info: %w", err)
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you follow the same pattern described here where you make airgapInfo a property of the manager and pass it with the function WithAirgapInfo?

if err := m.initComponentsList(license, rc); err != nil {
return fmt.Errorf("init components: %w", err)
}
Expand All @@ -123,7 +133,7 @@ func (m *infraManager) install(ctx context.Context, rc runtimeconfig.RuntimeConf
}
defer hcli.Close()

in, err := m.recordInstallation(ctx, kcli, license, rc)
in, err := m.recordInstallation(ctx, kcli, license, rc, airgapInfo)
if err != nil {
return fmt.Errorf("record installation: %w", err)
}
Expand Down Expand Up @@ -218,21 +228,28 @@ func (m *infraManager) installK0s(ctx context.Context, rc runtimeconfig.RuntimeC
return k0sCfg, nil
}

func (m *infraManager) recordInstallation(ctx context.Context, kcli client.Client, license *kotsv1beta1.License, rc runtimeconfig.RuntimeConfig) (*ecv1beta1.Installation, error) {
func (m *infraManager) recordInstallation(ctx context.Context, kcli client.Client, license *kotsv1beta1.License, rc runtimeconfig.RuntimeConfig, airgapInfo *kotsv1beta1.Airgap) (*ecv1beta1.Installation, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will no longer need to be an argument as infraManager will have a property airgapInfo

logFn := m.logFn("metadata")

// get the configured custom domains
ecDomains := utils.GetDomains(m.releaseData)

// extract airgap uncompressed size if airgap info is provided
var airgapUncompressedSize int64
if airgapInfo != nil {
airgapUncompressedSize = airgapInfo.Spec.UncompressedSize
}

// record the installation
logFn("recording installation")
in, err := kubeutils.RecordInstallation(ctx, kcli, kubeutils.RecordInstallationOptions{
IsAirgap: m.airgapBundle != "",
License: license,
ConfigSpec: m.getECConfigSpec(),
MetricsBaseURL: netutils.MaybeAddHTTPS(ecDomains.ReplicatedAppDomain),
RuntimeConfig: rc.Get(),
EndUserConfig: m.endUserConfig,
IsAirgap: m.airgapBundle != "",
License: license,
ConfigSpec: m.getECConfigSpec(),
MetricsBaseURL: netutils.MaybeAddHTTPS(ecDomains.ReplicatedAppDomain),
RuntimeConfig: rc.Get(),
EndUserConfig: m.endUserConfig,
AirgapUncompressedSize: airgapUncompressedSize,
})
if err != nil {
return nil, fmt.Errorf("record installation: %w", err)
Expand Down
52 changes: 28 additions & 24 deletions api/internal/managers/preflight/hostpreflight.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ import (
)

type PrepareHostPreflightOptions struct {
ReplicatedAppURL string
ProxyRegistryURL string
HostPreflightSpec *troubleshootv1beta2.HostPreflightSpec
EmbeddedClusterConfig *ecv1beta1.Config
TCPConnectionsRequired []string
IsAirgap bool
IsJoin bool
IsUI bool
ReplicatedAppURL string
ProxyRegistryURL string
HostPreflightSpec *troubleshootv1beta2.HostPreflightSpec
EmbeddedClusterConfig *ecv1beta1.Config
TCPConnectionsRequired []string
IsAirgap bool
IsJoin bool
IsUI bool
ControllerAirgapStorageSpace string
WorkerAirgapStorageSpace string
}

type RunHostPreflightOptions struct {
Expand All @@ -38,22 +40,24 @@ func (m *hostPreflightManager) PrepareHostPreflights(ctx context.Context, rc run

// Use the shared Prepare function to prepare host preflights
prepareOpts := preflights.PrepareOptions{
HostPreflightSpec: opts.HostPreflightSpec,
ReplicatedAppURL: opts.ReplicatedAppURL,
ProxyRegistryURL: opts.ProxyRegistryURL,
AdminConsolePort: rc.AdminConsolePort(),
LocalArtifactMirrorPort: rc.LocalArtifactMirrorPort(),
DataDir: rc.EmbeddedClusterHomeDirectory(),
K0sDataDir: rc.EmbeddedClusterK0sSubDir(),
OpenEBSDataDir: rc.EmbeddedClusterOpenEBSLocalSubDir(),
Proxy: rc.ProxySpec(),
PodCIDR: rc.PodCIDR(),
ServiceCIDR: rc.ServiceCIDR(),
NodeIP: nodeIP,
IsAirgap: opts.IsAirgap,
TCPConnectionsRequired: opts.TCPConnectionsRequired,
IsJoin: opts.IsJoin,
IsUI: opts.IsUI,
HostPreflightSpec: opts.HostPreflightSpec,
ReplicatedAppURL: opts.ReplicatedAppURL,
ProxyRegistryURL: opts.ProxyRegistryURL,
AdminConsolePort: rc.AdminConsolePort(),
LocalArtifactMirrorPort: rc.LocalArtifactMirrorPort(),
DataDir: rc.EmbeddedClusterHomeDirectory(),
K0sDataDir: rc.EmbeddedClusterK0sSubDir(),
OpenEBSDataDir: rc.EmbeddedClusterOpenEBSLocalSubDir(),
Proxy: rc.ProxySpec(),
PodCIDR: rc.PodCIDR(),
ServiceCIDR: rc.ServiceCIDR(),
NodeIP: nodeIP,
IsAirgap: opts.IsAirgap,
TCPConnectionsRequired: opts.TCPConnectionsRequired,
IsJoin: opts.IsJoin,
IsUI: opts.IsUI,
ControllerAirgapStorageSpace: opts.ControllerAirgapStorageSpace,
WorkerAirgapStorageSpace: opts.WorkerAirgapStorageSpace,
}
if cidr := rc.GlobalCIDR(); cidr != "" {
prepareOpts.GlobalCIDR = &cidr
Expand Down
3 changes: 3 additions & 0 deletions cmd/installer/cli/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/replicatedhq/embedded-cluster/pkg/release"
"github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig"
"github.com/replicatedhq/embedded-cluster/web"
kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
"github.com/sirupsen/logrus"
)

Expand All @@ -37,6 +38,7 @@ type apiConfig struct {
ManagerPort int
License []byte
AirgapBundle string
AirgapInfo *kotsv1beta1.Airgap
ConfigValues string
ReleaseData *release.ReleaseData
EndUserConfig *ecv1beta1.Config
Expand Down Expand Up @@ -89,6 +91,7 @@ func serveAPI(ctx context.Context, listener net.Listener, cert tls.Certificate,
api.WithTLSConfig(config.TLSConfig),
api.WithLicense(config.License),
api.WithAirgapBundle(config.AirgapBundle),
api.WithAirgapInfo(config.AirgapInfo),
api.WithConfigValues(config.ConfigValues),
api.WithEndUserConfig(config.EndUserConfig),
)
Expand Down
Loading
Loading