Skip to content

Commit

Permalink
Properly format windows env-vars, redeploy windows plans if they fail…
Browse files Browse the repository at this point in the history
… up to 5 times
  • Loading branch information
HarrisonWAffel committed Oct 10, 2024
1 parent bf39f57 commit 18eb246
Show file tree
Hide file tree
Showing 10 changed files with 228 additions and 28 deletions.
51 changes: 51 additions & 0 deletions pkg/capr/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -674,3 +674,54 @@ func PreBootstrap(mgmtCluster *v3.Cluster) bool {

return !v3.ClusterConditionPreBootstrapped.IsTrue(mgmtCluster)
}

// FormatWindowsEnvVar accepts a corev1.EnvVar and returns a string to be used in either
// a Powershell script or the Rancher planner, indicated by the isPlanVariable parameter.
// This function automatically configures the '$env:' prefix for a given environment variable,
// automatically prefixes boolean values with '$', and surrounds string variables with double quotes as
// needed. If provided variable name incorrectly uses either '$env:' or '$' with the given isPlanVariable
// value, it will be removed.
func FormatWindowsEnvVar(envVar corev1.EnvVar, isPlanVariable bool) string {
lowerValue := strings.ToLower(envVar.Value)
isBool := lowerValue == "$true" || lowerValue == "$false" ||
lowerValue == "true" || lowerValue == "false"

// remove any user provided prefixes and suffixes
if strings.HasPrefix(envVar.Name, "$env:") {
envVar.Name = strings.TrimPrefix(envVar.Name, "$env:")
}

if strings.HasPrefix(envVar.Value, "\"") {
envVar.Value = strings.TrimPrefix(envVar.Value, "\"")
}

if strings.HasSuffix(envVar.Value, "\"") {
envVar.Value = strings.TrimSuffix(envVar.Value, "\"")
}

if !isBool {
format := ""
if isPlanVariable {
format = "%s=%s"
} else {
// None boolean variables are always treated as strings,
// even numbers
format = "$env:%s=\"%s\""
}
return fmt.Sprintf(format, envVar.Name, envVar.Value)
}

if !strings.HasPrefix(envVar.Value, "$") && !isPlanVariable {
envVar.Value = "$" + envVar.Value
}

if strings.HasPrefix(envVar.Value, "$") && isPlanVariable {
envVar.Value = strings.TrimPrefix(envVar.Value, "$")
}

if isPlanVariable {
return fmt.Sprintf("%s=%s", envVar.Name, envVar.Value)
}

return fmt.Sprintf("$env:%s=%s", envVar.Name, envVar.Value)
}
74 changes: 74 additions & 0 deletions pkg/capr/common_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package capr

import (
corev1 "k8s.io/api/core/v1"
"reflect"
"testing"

Expand Down Expand Up @@ -313,3 +314,76 @@ func TestCompressInterface(t *testing.T) {
})
}
}

func TestFormatWindowsEnvVar(t *testing.T) {
tests := []struct {
Name string
EnvVar corev1.EnvVar
IsPlanVar bool
ExpectedString string
}{
{
Name: "Basic String",
EnvVar: corev1.EnvVar{
Name: "BASIC_STRING",
Value: "ABC123",
},
IsPlanVar: false,
ExpectedString: "$env:BASIC_STRING=\"ABC123\"",
},
{
Name: "Basic Bool",
EnvVar: corev1.EnvVar{
Name: "BASIC_BOOL",
Value: "true",
},
IsPlanVar: false,
ExpectedString: "$env:BASIC_BOOL=$true",
},
{
Name: "Basic Plan String",
EnvVar: corev1.EnvVar{
Name: "PLAN_STRING",
Value: "VALUE",
},
IsPlanVar: true,
ExpectedString: "PLAN_STRING=VALUE",
},
{
Name: "Basic Plan Bool",
EnvVar: corev1.EnvVar{
Name: "PLAN_BOOL",
Value: "true",
},
IsPlanVar: true,
ExpectedString: "PLAN_BOOL=true",
},
{
Name: "Plan name Mistakenly includes $env:",
EnvVar: corev1.EnvVar{
Name: "$env:PLAN_BOOL",
Value: "true",
},
IsPlanVar: true,
ExpectedString: "PLAN_BOOL=true",
},
{
Name: "Plan value Mistakenly Includes $",
EnvVar: corev1.EnvVar{
Name: "PLAN_BOOL",
Value: "$true",
},
IsPlanVar: true,
ExpectedString: "PLAN_BOOL=true",
},
}

for _, tc := range tests {
t.Run(tc.Name, func(t *testing.T) {
out := FormatWindowsEnvVar(tc.EnvVar, tc.IsPlanVar)
if out != tc.ExpectedString {
t.Fatalf("Expected %s, got %s", tc.ExpectedString, out)
}
})
}
}
38 changes: 31 additions & 7 deletions pkg/capr/installer/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os"
"strings"

"github.com/rancher/rancher/pkg/capr"
"github.com/rancher/rancher/pkg/settings"
"github.com/rancher/rancher/pkg/systemtemplate"
"github.com/rancher/rancher/pkg/tls"
Expand Down Expand Up @@ -135,9 +136,15 @@ func WindowsInstallScript(ctx context.Context, token string, envVars []corev1.En
binaryURL := ""
if settings.WinsAgentVersion.Get() != "" {
if settings.ServerURL.Get() != "" {
binaryURL = fmt.Sprintf("$env:CATTLE_AGENT_BINARY_BASE_URL=\"%s/assets\"", settings.ServerURL.Get())
binaryURL = capr.FormatWindowsEnvVar(corev1.EnvVar{
Name: "CATTLE_AGENT_BINARY_BASE_URL",
Value: fmt.Sprintf("%s/assets", settings.ServerURL.Get()),
}, false)
} else if defaultHost != "" {
binaryURL = fmt.Sprintf("$env:CATTLE_AGENT_BINARY_BASE_URL=\"https://%s/assets\"", defaultHost)
binaryURL = capr.FormatWindowsEnvVar(corev1.EnvVar{
Name: "CATTLE_AGENT_BINARY_BASE_URL",
Value: fmt.Sprintf("https://%s/assets", defaultHost),
}, false)
}
}

Expand All @@ -156,22 +163,38 @@ func WindowsInstallScript(ctx context.Context, token string, envVars []corev1.En
if v, ok := ctx.Value(tls.InternalAPI).(bool); ok && v {
ca = systemtemplate.InternalCAChecksum()
}

if ca != "" {
ca = "$env:CATTLE_CA_CHECKSUM=\"" + ca + "\""
ca = capr.FormatWindowsEnvVar(corev1.EnvVar{
Name: "CATTLE_CA_CHECKSUM",
Value: ca,
}, false)
}

var tokenEnvVar string
if token != "" {
token = "$env:CATTLE_ROLE_NONE=\"true\"\n$env:CATTLE_TOKEN=\"" + token + "\""
tokenEnvVar = capr.FormatWindowsEnvVar(corev1.EnvVar{
Name: "CATTLE_TOKEN",
Value: token,
}, false)
}

envVarBuf := &strings.Builder{}
for _, envVar := range envVars {
if envVar.Value == "" {
continue
}
envVarBuf.WriteString(fmt.Sprintf("$env:%s=\"%s\"\n", envVar.Name, envVar.Value))
envVarBuf.WriteString(capr.FormatWindowsEnvVar(corev1.EnvVar{
Name: envVar.Name,
Value: envVar.Value,
}, false))
}
server := ""
if settings.ServerURL.Get() != "" {
server = fmt.Sprintf("$env:CATTLE_SERVER=\"%s\"", settings.ServerURL.Get())
server = capr.FormatWindowsEnvVar(corev1.EnvVar{
Name: "CATTLE_SERVER",
Value: settings.ServerURL.Get(),
}, false)
}

strictVerify := "false"
Expand All @@ -192,8 +215,9 @@ $env:CSI_PROXY_URL = "%s"
$env:CSI_PROXY_VERSION = "%s"
$env:CSI_PROXY_KUBELET_PATH = "C:%s/bin/kubelet.exe"
$env:STRICT_VERIFY = "%s"
$env:CATTLE_ROLE_NONE = "true"
Invoke-WinsInstaller @PSBoundParameters
exit 0
`, data, envVarBuf.String(), binaryURL, server, ca, token, csiProxyURL, csiProxyVersion, dataDir, strictVerify)), nil
`, data, envVarBuf.String(), binaryURL, server, ca, tokenEnvVar, csiProxyURL, csiProxyVersion, dataDir, strictVerify)), nil
}
2 changes: 1 addition & 1 deletion pkg/capr/planner/etcdcreate.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (p *Planner) runEtcdSnapshotManagementServiceStart(controlPlane *rkev1.RKEC
// Generate and deliver desired plan for the bootstrap/init node first.
if err := p.reconcile(controlPlane, tokensSecret, clusterPlan, true, bootstrapTier, isEtcd, isNotInitNodeOrIsDeleting,
"1", "",
controlPlane.Spec.UpgradeStrategy.ControlPlaneDrainOptions); err != nil {
controlPlane.Spec.UpgradeStrategy.ControlPlaneDrainOptions, -1, 1); err != nil {
return err
}

Expand Down
26 changes: 22 additions & 4 deletions pkg/capr/planner/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
rkev1 "github.com/rancher/rancher/pkg/apis/rke.cattle.io/v1"
"github.com/rancher/rancher/pkg/apis/rke.cattle.io/v1/plan"
"github.com/rancher/rancher/pkg/capr"
corev1 "k8s.io/api/core/v1"
)

const (
Expand All @@ -26,14 +27,22 @@ func (p *Planner) generateInstallInstruction(controlPlane *rkev1.RKEControlPlane
}
switch cattleOS {
case capr.WindowsMachineOS:
env = append(env, fmt.Sprintf("$env:%s=\"%s\"", arg.Name, arg.Value))
env = append(env, capr.FormatWindowsEnvVar(corev1.EnvVar{
Name: arg.Name,
Value: arg.Value,
}, true))
default:
env = append(env, fmt.Sprintf("%s=%s", arg.Name, arg.Value))
}
}
switch cattleOS {
case capr.WindowsMachineOS:
// TODO: Properly format the data dir when adding full support for Windows nodes
env = append(env, fmt.Sprintf("$env:%s_DATA_DIR=\"c:%s\"", strings.ToUpper(capr.GetRuntime(controlPlane.Spec.KubernetesVersion)), capr.GetDistroDataDir(controlPlane)))
env = append(env, capr.FormatWindowsEnvVar(corev1.EnvVar{
Name: "INSTALL_RKE2_VERSION",
Value: strings.ReplaceAll(controlPlane.Spec.KubernetesVersion, "+", "-"),
}, true))
default:
env = append(env, fmt.Sprintf("%s_DATA_DIR=%s", strings.ToUpper(capr.GetRuntime(controlPlane.Spec.KubernetesVersion)), capr.GetDistroDataDir(controlPlane)))
}
Expand All @@ -60,7 +69,10 @@ func (p *Planner) generateInstallInstruction(controlPlane *rkev1.RKEControlPlane
if isOnlyWorker(entry) {
switch cattleOS {
case capr.WindowsMachineOS:
instruction.Env = append(instruction.Env, fmt.Sprintf("$env:INSTALL_%s_EXEC=\"agent\"", capr.GetRuntimeEnv(controlPlane.Spec.KubernetesVersion)))
instruction.Env = append(instruction.Env, capr.FormatWindowsEnvVar(corev1.EnvVar{
Name: fmt.Sprintf("INSTALL_%s_EXEC", capr.GetRuntimeEnv(controlPlane.Spec.KubernetesVersion)),
Value: "agent",
}, true))
default:
instruction.Env = append(instruction.Env, fmt.Sprintf("INSTALL_%s_EXEC=agent", capr.GetRuntimeEnv(controlPlane.Spec.KubernetesVersion)))
}
Expand All @@ -77,7 +89,10 @@ func (p *Planner) addInstallInstructionWithRestartStamp(nodePlan plan.NodePlan,
stamp := restartStamp(nodePlan, controlPlane, p.getInstallerImage(controlPlane))
switch entry.Metadata.Labels[capr.CattleOSLabel] {
case capr.WindowsMachineOS:
restartStampEnv = "$env:RESTART_STAMP=\"" + stamp + "\""
restartStampEnv = capr.FormatWindowsEnvVar(corev1.EnvVar{
Name: "WINS_RESTART_STAMP",
Value: stamp,
}, true)
default:
restartStampEnv = "RESTART_STAMP=" + stamp
}
Expand All @@ -93,7 +108,10 @@ func (p *Planner) generateInstallInstructionWithSkipStart(controlPlane *rkev1.RK
var skipStartEnv string
switch entry.Metadata.Labels[capr.CattleOSLabel] {
case capr.WindowsMachineOS:
skipStartEnv = fmt.Sprintf("$env:INSTALL_%s_SKIP_START=\"true\"", strings.ToUpper(capr.GetRuntime(controlPlane.Spec.KubernetesVersion)))
skipStartEnv = capr.FormatWindowsEnvVar(corev1.EnvVar{
Name: fmt.Sprintf("INSTALL_%s_SKIP_START", strings.ToUpper(capr.GetRuntime(controlPlane.Spec.KubernetesVersion))),
Value: "true",
}, true)
default:
skipStartEnv = fmt.Sprintf("INSTALL_%s_SKIP_START=true", strings.ToUpper(capr.GetRuntime(controlPlane.Spec.KubernetesVersion)))
}
Expand Down
8 changes: 8 additions & 0 deletions pkg/capr/planner/planentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ func isOnlyWorker(entry *planEntry) bool {
return !isEtcd(entry) && !isControlPlane(entry) && isWorker(entry)
}

func isOnlyWindowsWorker(entry *planEntry) bool {
return isOnlyWorker(entry) && windows(entry)
}

func isOnlyLinuxWorker(entry *planEntry) bool {
return isOnlyWorker(entry) && !windows(entry)
}

func windows(entry *planEntry) bool {
if entry == nil || entry.Metadata == nil {
return false
Expand Down
Loading

0 comments on commit 18eb246

Please sign in to comment.