Skip to content

Commit 17ea9d3

Browse files
authored
Add flag to ignore app preflights for V2 installs and update success message (#2711)
* Add CLI flag ignore-app-preflights for install * Update success message for headless install * Add dry-run test for ignore-app-preflights flag
1 parent 43b02c3 commit 17ea9d3

File tree

3 files changed

+175
-4
lines changed

3 files changed

+175
-4
lines changed

cmd/installer/cli/install.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ type InstallCmdFlags struct {
7575
localArtifactMirrorPort int
7676
skipHostPreflights bool
7777
ignoreHostPreflights bool
78+
ignoreAppPreflights bool
7879
networkInterface string
7980

8081
// kubernetes flags
@@ -276,8 +277,9 @@ func newLinuxInstallFlags(flags *InstallCmdFlags, enableV3 bool) *pflag.FlagSet
276277
defaultDataDir := ecv1beta1.DefaultDataDir
277278
if enableV3 {
278279
defaultDataDir = filepath.Join("/var/lib", runtimeconfig.AppSlug())
280+
} else {
281+
flagSet.BoolVar(&flags.ignoreAppPreflights, "ignore-app-preflights", false, "Allow bypassing app preflight failures")
279282
}
280-
281283
flagSet.StringVar(&flags.dataDir, "data-dir", defaultDataDir, "Path to the data directory")
282284
flagSet.IntVar(&flags.localArtifactMirrorPort, "local-artifact-mirror-port", ecv1beta1.DefaultLocalArtifactMirrorPort, "Port on which the Local Artifact Mirror will be served")
283285
flagSet.StringVar(&flags.networkInterface, "network-interface", "", "The network interface to use for the cluster")
@@ -827,7 +829,9 @@ func runInstall(ctx context.Context, flags InstallCmdFlags, rc runtimeconfig.Run
827829
logrus.Warnf("Unable to create host support bundle: %v", err)
828830
}
829831

830-
printSuccessMessage(flags.license, flags.hostname, flags.networkInterface, rc)
832+
isHeadlessInstall := flags.configValues != "" && flags.adminConsolePassword != ""
833+
834+
printSuccessMessage(flags.license, flags.hostname, flags.networkInterface, rc, isHeadlessInstall)
831835

832836
return nil
833837
}
@@ -875,6 +879,7 @@ func getAddonInstallOpts(flags InstallCmdFlags, rc runtimeconfig.RuntimeConfig,
875879
AirgapBundle: flags.airgapBundle,
876880
ConfigValuesFile: flags.configValues,
877881
ReplicatedAppEndpoint: replicatedAppURL(),
882+
SkipPreflights: flags.ignoreAppPreflights,
878883
Stdout: *loading,
879884
}
880885
return kotscli.Install(opts)
@@ -1415,11 +1420,16 @@ func normalizeNoPromptToYes(f *pflag.FlagSet, name string) pflag.NormalizedName
14151420
return pflag.NormalizedName(name)
14161421
}
14171422

1418-
func printSuccessMessage(license *kotsv1beta1.License, hostname string, networkInterface string, rc runtimeconfig.RuntimeConfig) {
1423+
func printSuccessMessage(license *kotsv1beta1.License, hostname string, networkInterface string, rc runtimeconfig.RuntimeConfig, isHeadlessInstall bool) {
14191424
adminConsoleURL := getAdminConsoleURL(hostname, networkInterface, rc.AdminConsolePort())
14201425

14211426
// Create the message content
1422-
message := fmt.Sprintf("Visit the Admin Console to configure and install %s:", license.Spec.AppSlug)
1427+
var message string
1428+
if isHeadlessInstall {
1429+
message = fmt.Sprintf("The Admin Console for %s is available at:", license.Spec.AppSlug)
1430+
} else {
1431+
message = fmt.Sprintf("Visit the Admin Console to configure and install %s:", license.Spec.AppSlug)
1432+
}
14231433

14241434
// Determine the length of the longest line
14251435
longestLine := len(message)

cmd/installer/cli/install_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,122 @@ func boolPtr(b bool) *bool {
704704
return &b
705705
}
706706

707+
func Test_ignoreAppPreflights_FlagVisibility(t *testing.T) {
708+
tests := []struct {
709+
name string
710+
enableV3EnvVar string
711+
expectedFlagShouldBeVisible bool
712+
}{
713+
{
714+
name: "ENABLE_V3 not set - flag should be visible",
715+
enableV3EnvVar: "",
716+
expectedFlagShouldBeVisible: true,
717+
},
718+
{
719+
name: "ENABLE_V3 set to 1 - flag should be hidden",
720+
enableV3EnvVar: "1",
721+
expectedFlagShouldBeVisible: false,
722+
},
723+
}
724+
725+
for _, tt := range tests {
726+
t.Run(tt.name, func(t *testing.T) {
727+
// Clean environment
728+
os.Unsetenv("ENABLE_V3")
729+
730+
// Set environment variable if specified
731+
if tt.enableV3EnvVar != "" {
732+
t.Setenv("ENABLE_V3", tt.enableV3EnvVar)
733+
}
734+
735+
flags := &InstallCmdFlags{}
736+
enableV3 := isV3Enabled()
737+
flagSet := newLinuxInstallFlags(flags, enableV3)
738+
739+
// Check if the flag exists
740+
flag := flagSet.Lookup("ignore-app-preflights")
741+
flagExists := flag != nil
742+
743+
assert.Equal(t, tt.expectedFlagShouldBeVisible, flagExists, "Flag visibility should match expected")
744+
745+
if flagExists {
746+
// Test flag properties
747+
assert.Equal(t, "ignore-app-preflights", flag.Name)
748+
assert.Equal(t, "false", flag.DefValue) // Default should be false
749+
assert.Equal(t, "Allow bypassing app preflight failures", flag.Usage)
750+
assert.Equal(t, "bool", flag.Value.Type())
751+
752+
// Test flag targets - should be Linux only
753+
targetAnnotation := flag.Annotations[flagAnnotationTarget]
754+
require.NotNil(t, targetAnnotation, "Flag should have target annotation")
755+
assert.Contains(t, targetAnnotation, flagAnnotationTargetValueLinux)
756+
}
757+
})
758+
}
759+
}
760+
761+
func Test_ignoreAppPreflights_FlagParsing(t *testing.T) {
762+
tests := []struct {
763+
name string
764+
args []string
765+
enableV3 bool
766+
expectedIgnorePreflights bool
767+
expectError bool
768+
}{
769+
{
770+
name: "flag not provided, V3 disabled",
771+
args: []string{},
772+
enableV3: false,
773+
expectedIgnorePreflights: false,
774+
expectError: false,
775+
},
776+
{
777+
name: "flag set to true, V3 disabled",
778+
args: []string{"--ignore-app-preflights"},
779+
enableV3: false,
780+
expectedIgnorePreflights: true,
781+
expectError: false,
782+
},
783+
{
784+
name: "flag set but V3 enabled - should error",
785+
args: []string{"--ignore-app-preflights"},
786+
enableV3: true,
787+
expectedIgnorePreflights: false,
788+
expectError: true,
789+
},
790+
}
791+
792+
for _, tt := range tests {
793+
t.Run(tt.name, func(t *testing.T) {
794+
// Set environment variable for V3 testing
795+
if tt.enableV3 {
796+
t.Setenv("ENABLE_V3", "1")
797+
}
798+
799+
// Create a flagset similar to how newLinuxInstallFlags works
800+
flags := &InstallCmdFlags{}
801+
flagSet := newLinuxInstallFlags(flags, tt.enableV3)
802+
803+
// Create a command to test flag parsing
804+
cmd := &cobra.Command{
805+
Use: "test",
806+
Run: func(cmd *cobra.Command, args []string) {},
807+
}
808+
cmd.Flags().AddFlagSet(flagSet)
809+
810+
// Try to parse the arguments
811+
err := cmd.Flags().Parse(tt.args)
812+
if tt.expectError {
813+
assert.Error(t, err, "Flag parsing should fail when flag doesn't exist")
814+
} else {
815+
assert.NoError(t, err, "Flag parsing should succeed")
816+
// Check the flag value only if parsing succeeded
817+
assert.Equal(t, tt.expectedIgnorePreflights, flags.ignoreAppPreflights)
818+
}
819+
})
820+
}
821+
}
822+
707823
func Test_processTLSConfig(t *testing.T) {
708824
// Create a temporary directory for test certificates
709825
tmpdir := t.TempDir()

tests/dryrun/install_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,3 +752,48 @@ oxhVqyhpk86rf0rT5DcD/sBw
752752

753753
t.Logf("%s: test complete", time.Now().Format(time.RFC3339))
754754
}
755+
756+
func TestIgnoreAppPreflightsInstallation(t *testing.T) {
757+
hcli := &helm.MockClient{}
758+
759+
mock.InOrder(
760+
// 4 addons
761+
hcli.On("Install", mock.Anything, mock.Anything).Times(4).Return(nil, nil),
762+
hcli.On("Close").Once().Return(nil),
763+
)
764+
765+
dr := dryrunInstall(t,
766+
&dryrun.Client{HelmClient: hcli},
767+
"--ignore-app-preflights",
768+
)
769+
770+
// --- validate commands --- //
771+
assertCommands(t, dr.Commands,
772+
[]any{
773+
regexp.MustCompile(`install fake-app-slug/fake-channel-slug .* --skip-preflights`),
774+
},
775+
false,
776+
)
777+
778+
// --- validate metrics --- //
779+
assertMetrics(t, dr.Metrics, []struct {
780+
title string
781+
validate func(string)
782+
}{
783+
{
784+
title: "InstallationStarted",
785+
validate: func(payload string) {
786+
assert.Contains(t, payload, "--ignore-app-preflights")
787+
},
788+
},
789+
{
790+
title: "GenericEvent",
791+
validate: func(payload string) {
792+
assert.Contains(t, payload, `"isExitEvent":true`)
793+
assert.Contains(t, payload, `"eventType":"InstallationSucceeded"`)
794+
},
795+
},
796+
})
797+
798+
t.Logf("%s: test complete", time.Now().Format(time.RFC3339))
799+
}

0 commit comments

Comments
 (0)