Skip to content

Commit 0e6d623

Browse files
authored
chore: fix quickstart script to join members (kubefleet-dev#542)
* Updated maintainers and few other minor tweaks. Signed-off-by: Simon Waight <simon.waight@microsoft.com> * FIx up join member clusters quickstart script (*nix). Signed-off-by: Simon Waight <simon.waight@microsoft.com> * Refactor to put version first. Signed-off-by: Simon Waight <simon.waight@microsoft.com> * Add PowerShell version of join script for Windows users. Signed-off-by: Simon Waight <simon.waight@microsoft.com> * Fix broken API server lookup from kubeconfig Signed-off-by: Simon Waight <simon.waight@microsoft.com> * Update join-member-clusters.ps1 to require hub control plane URL as an argument Signed-off-by: Simon Waight <simon.waight@microsoft.com> * Update join-member-clusters.sh to require hub control plane URL as an argument Signed-off-by: Simon Waight <simon.waight@microsoft.com> * Tweaks per PR feedback. Signed-off-by: Simon Waight <simon.waight@microsoft.com> --------- Signed-off-by: Simon Waight <simon.waight@microsoft.com>
1 parent f061d92 commit 0e6d623

2 files changed

Lines changed: 367 additions & 0 deletions

File tree

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
# Note: you must have at least one hub cluster and one member cluster.
2+
#
3+
# The reason we require the hub cluster API URL as an argument is that, in some environments (e.g. kind), the URL cannot be derived from kubeconfig and needs to be explicitly provided by users.
4+
#
5+
# For example, using Docker, you can get the right IP address for member clusters to use:
6+
# docker inspect local-hub-01-control-plane --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
7+
#
8+
# You can assume port 6443 unless you have explicitly changed the API server port for your hub cluster. Don't use the mapped Docker port (i.e. 50063)
9+
#
10+
# Example usage:
11+
# ./join-member-clusters.ps1 0.2.2 demo-hub-01 https://172.18.0.2:6443 member-cluster-1 member-cluster-2
12+
13+
[CmdletBinding()]
14+
param(
15+
[Parameter(Position = 0)]
16+
[string]$KubefleetVersion,
17+
18+
[Parameter(Position = 1)]
19+
[string]$HubClusterName,
20+
21+
[Parameter(Position = 2)]
22+
[string]$HubControlPlaneURL,
23+
24+
[Parameter(Position = 3, ValueFromRemainingArguments = $true)]
25+
[string[]]$MemberClusterNames,
26+
27+
[switch]$Help
28+
)
29+
30+
function Show-Usage {
31+
@'
32+
Usage:
33+
./join-member-clusters.ps1 <kubefleet-version> <hub-cluster-name> <hub-control-plane-url> <member-cluster-name-1> [<member-cluster-name-2> ...]
34+
35+
Example:
36+
./join-member-clusters.ps1 0.2.2 demo-hub-01 https://172.18.0.2:6443 member-cluster-1 member-cluster-2
37+
38+
Requirements:
39+
- PowerShell 7, kubectl, and helm must be installed
40+
- hub and member cluster names must exist in your kubeconfig
41+
'@
42+
}
43+
44+
function Fail-WithHelp {
45+
param(
46+
[Parameter(Mandatory = $true)]
47+
[string]$Message
48+
)
49+
50+
Write-Error $Message
51+
Write-Host ""
52+
Show-Usage
53+
exit 1
54+
}
55+
56+
function Test-CommandExists {
57+
param(
58+
[Parameter(Mandatory = $true)]
59+
[string]$Name
60+
)
61+
62+
return $null -ne (Get-Command $Name -ErrorAction SilentlyContinue)
63+
}
64+
65+
function Test-ClusterExists {
66+
param(
67+
[Parameter(Mandatory = $true)]
68+
[string]$Name
69+
)
70+
71+
$clusters = @(kubectl config get-clusters 2>$null)
72+
if ($LASTEXITCODE -ne 0) {
73+
return $false
74+
}
75+
76+
return $clusters | Select-Object -Skip 1 | Where-Object { $_.Trim() -eq $Name } | ForEach-Object { $true } | Select-Object -First 1
77+
}
78+
79+
if ($Help) {
80+
Show-Usage
81+
exit 0
82+
}
83+
84+
if ([string]::IsNullOrWhiteSpace($KubefleetVersion) -or [string]::IsNullOrWhiteSpace($HubClusterName) -or [string]::IsNullOrWhiteSpace($HubControlPlaneURL) -or $null -eq $MemberClusterNames -or $MemberClusterNames.Count -lt 1) {
85+
$argumentCount = 0
86+
if (-not [string]::IsNullOrWhiteSpace($KubefleetVersion)) {
87+
$argumentCount++
88+
}
89+
if (-not [string]::IsNullOrWhiteSpace($HubClusterName)) {
90+
$argumentCount++
91+
}
92+
if (-not [string]::IsNullOrWhiteSpace($HubControlPlaneURL)) {
93+
$argumentCount++
94+
}
95+
if ($null -ne $MemberClusterNames) {
96+
$argumentCount += $MemberClusterNames.Count
97+
}
98+
99+
Fail-WithHelp "expected at least 4 arguments, got $argumentCount"
100+
}
101+
102+
if (-not (Test-CommandExists -Name kubectl)) {
103+
Fail-WithHelp "kubectl is not installed or not available in PATH"
104+
}
105+
106+
if (-not (Test-CommandExists -Name helm)) {
107+
Fail-WithHelp "helm is not installed or not available in PATH"
108+
}
109+
110+
if (-not (Test-ClusterExists -Name $HubClusterName)) {
111+
Fail-WithHelp "hub cluster '$HubClusterName' was not found in kubeconfig"
112+
}
113+
114+
[System.Uri]$parsedHubURL = $null
115+
if (-not [System.Uri]::TryCreate($HubControlPlaneURL, [System.UriKind]::Absolute, [ref]$parsedHubURL)) {
116+
Fail-WithHelp "hub control plane URL '$HubControlPlaneURL' is not a valid absolute URL"
117+
}
118+
119+
if ($parsedHubURL.Scheme -ne "https") {
120+
Fail-WithHelp "hub control plane URL must use https"
121+
}
122+
123+
foreach ($memberClusterName in $MemberClusterNames) {
124+
if ([string]::IsNullOrWhiteSpace($memberClusterName)) {
125+
Fail-WithHelp "member cluster name cannot be empty"
126+
}
127+
128+
if ($memberClusterName -eq $HubClusterName) {
129+
Fail-WithHelp "member cluster '$memberClusterName' cannot be the same as hub cluster"
130+
}
131+
132+
if (-not (Test-ClusterExists -Name $memberClusterName)) {
133+
Fail-WithHelp "member cluster '$memberClusterName' was not found in kubeconfig"
134+
}
135+
}
136+
137+
foreach ($memberClusterName in $MemberClusterNames) {
138+
# Note that Fleet will recognize your cluster with this name once it joins.
139+
$serviceAccount = "$memberClusterName-hub-cluster-access"
140+
$serviceAccountSecret = "$memberClusterName-hub-cluster-access-token"
141+
142+
Write-Host "Switching into hub cluster context..."
143+
kubectl config use-context $HubClusterName
144+
145+
# The service account can, in theory, be created in any namespace; for simplicity reasons,
146+
# here you will use the namespace reserved by Fleet installation, `fleet-system`.
147+
#
148+
# Note that if you choose a different value, commands in some steps below need to be
149+
# modified accordingly.
150+
Write-Host "Creating member service account..."
151+
kubectl create serviceaccount $serviceAccount -n fleet-system
152+
153+
Write-Host "Creating member service account secret..."
154+
@"
155+
apiVersion: v1
156+
kind: Secret
157+
metadata:
158+
name: $serviceAccountSecret
159+
namespace: fleet-system
160+
annotations:
161+
kubernetes.io/service-account.name: $serviceAccount
162+
type: kubernetes.io/service-account-token
163+
"@ | kubectl apply -f -
164+
165+
Write-Host "Creating member cluster custom resource on hub cluster..."
166+
$encodedToken = kubectl get secret $serviceAccountSecret -n fleet-system -o "jsonpath={.data.token}"
167+
$token = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($encodedToken))
168+
169+
@"
170+
apiVersion: cluster.kubernetes-fleet.io/v1
171+
kind: MemberCluster
172+
metadata:
173+
name: $memberClusterName
174+
spec:
175+
identity:
176+
name: $memberClusterName-hub-cluster-access
177+
kind: ServiceAccount
178+
namespace: fleet-system
179+
apiGroup: ""
180+
heartbeatPeriodSeconds: 15
181+
"@ | kubectl apply -f -
182+
183+
# Install the member agent helm chart on the member cluster.
184+
Write-Host "Switching to member cluster context..."
185+
kubectl config use-context $memberClusterName
186+
187+
# Create the secret with the token extracted previously for member agent to use.
188+
Write-Host "Creating secret..."
189+
kubectl delete secret hub-kubeconfig-secret
190+
kubectl create secret generic hub-kubeconfig-secret --from-literal="token=$token"
191+
192+
Write-Host "Uninstalling any existing member-agent instances..."
193+
helm uninstall member-agent -n fleet-system --wait
194+
195+
Write-Host "Installing member-agent..."
196+
helm install member-agent oci://ghcr.io/kubefleet-dev/kubefleet/charts/member-agent `
197+
--version $KubefleetVersion `
198+
--set "config.hubURL=$HubControlPlaneURL" `
199+
--set "config.memberClusterName=$memberClusterName" `
200+
--set logFileMaxSize=100000 `
201+
--namespace fleet-system `
202+
--create-namespace
203+
204+
kubectl get pods -A
205+
kubectl config use-context $HubClusterName
206+
kubectl get membercluster $memberClusterName
207+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
#!/bin/bash
2+
# Note: you must have at least one hub cluster and one member cluster.
3+
#
4+
# The reason we require the hub cluster API URL as an argument is that, in some environments (e.g. kind), the URL cannot be derived from kubeconfig and needs to be explicitly provided by users.
5+
#
6+
# For example, using Docker, you can get the right IP address for member clusters to use:
7+
# docker inspect local-hub-01-control-plane --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
8+
#
9+
# You can assume port 6443 unless you have explicitly changed the API server port for your hub cluster. Don't use the mapped Docker port (i.e. 50063)
10+
#
11+
# Example usage: ./join-member-clusters.sh 0.2.2 demo-hub-01 https://172.18.0.2:6443 member-cluster-1 [<member-cluster-name-2> ...]
12+
13+
usage() {
14+
cat <<'EOF'
15+
Usage:
16+
./join-member-clusters.sh <kubefleet-version> <hub-cluster-name> <hub-control-plane-url> <member-cluster-name-1> [<member-cluster-name-2> ...]
17+
18+
Example:
19+
./join-member-clusters.sh 0.2.2 demo-hub-01 https://172.18.0.2:6443 member-cluster-1 member-cluster-2
20+
21+
Requirements:
22+
- kubectl and helm must be installed
23+
- hub and member cluster names must exist in your kubeconfig
24+
EOF
25+
}
26+
27+
fail_with_help() {
28+
echo "Error: $1" >&2
29+
echo >&2
30+
usage >&2
31+
exit 1
32+
}
33+
34+
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
35+
usage
36+
exit 0
37+
fi
38+
39+
if [ "$#" -lt 4 ]; then
40+
fail_with_help "expected at least 4 arguments, got $#"
41+
fi
42+
43+
if ! command -v kubectl >/dev/null 2>&1; then
44+
fail_with_help "kubectl is not installed or not available in PATH"
45+
fi
46+
47+
if ! command -v helm >/dev/null 2>&1; then
48+
fail_with_help "helm is not installed or not available in PATH"
49+
fi
50+
51+
export KUBEFLEET_VERSION="$1"
52+
export HUB_CLUSTER_NAME="$2"
53+
export HUB_CONTROL_PLANE_URL="$3"
54+
55+
if [ -z "$KUBEFLEET_VERSION" ]; then
56+
fail_with_help "kubefleet version cannot be empty"
57+
fi
58+
59+
if [ -z "$HUB_CLUSTER_NAME" ]; then
60+
fail_with_help "hub cluster name cannot be empty"
61+
fi
62+
63+
if [ -z "$HUB_CONTROL_PLANE_URL" ]; then
64+
fail_with_help "hub control plane URL cannot be empty"
65+
fi
66+
67+
case "$HUB_CONTROL_PLANE_URL" in
68+
https://*) ;;
69+
*) fail_with_help "hub control plane URL must use https" ;;
70+
esac
71+
72+
if ! kubectl config get-clusters 2>/dev/null | grep -Fxq "$HUB_CLUSTER_NAME"; then
73+
fail_with_help "hub cluster '$HUB_CLUSTER_NAME' was not found in kubeconfig"
74+
fi
75+
76+
for MC in "${@:4}"; do
77+
if [ -z "$MC" ]; then
78+
fail_with_help "member cluster name cannot be empty"
79+
fi
80+
81+
if [ "$MC" = "$HUB_CLUSTER_NAME" ]; then
82+
fail_with_help "member cluster '$MC' cannot be the same as hub cluster"
83+
fi
84+
85+
if ! kubectl config get-clusters 2>/dev/null | grep -Fxq "$MC"; then
86+
fail_with_help "member cluster '$MC' was not found in kubeconfig"
87+
fi
88+
done
89+
90+
for MC in "${@:4}"; do
91+
92+
# Note that Fleet will recognize your cluster with this name once it joins.
93+
export MEMBER_CLUSTER_NAME=$MC
94+
export SERVICE_ACCOUNT="$MEMBER_CLUSTER_NAME-hub-cluster-access"
95+
96+
echo "Switching into hub cluster context..."
97+
kubectl config use $HUB_CLUSTER_NAME
98+
# The service account can, in theory, be created in any namespace; for simplicity reasons,
99+
# here you will use the namespace reserved by Fleet installation, `fleet-system`.
100+
#
101+
# Note that if you choose a different value, commands in some steps below need to be
102+
# modified accordingly.
103+
echo "Creating member service account..."
104+
kubectl create serviceaccount $SERVICE_ACCOUNT -n fleet-system
105+
106+
echo "Creating member service account secret..."
107+
export SERVICE_ACCOUNT_SECRET="$MEMBER_CLUSTER_NAME-hub-cluster-access-token"
108+
cat <<EOF | kubectl apply -f -
109+
apiVersion: v1
110+
kind: Secret
111+
metadata:
112+
name: $SERVICE_ACCOUNT_SECRET
113+
namespace: fleet-system
114+
annotations:
115+
kubernetes.io/service-account.name: $SERVICE_ACCOUNT
116+
type: kubernetes.io/service-account-token
117+
EOF
118+
119+
echo "Creating member cluster custom resource on hub cluster..."
120+
export TOKEN="$(kubectl get secret $SERVICE_ACCOUNT_SECRET -n fleet-system -o jsonpath='{.data.token}' | base64 --decode)"
121+
cat <<EOF | kubectl apply -f -
122+
apiVersion: cluster.kubernetes-fleet.io/v1
123+
kind: MemberCluster
124+
metadata:
125+
name: $MEMBER_CLUSTER_NAME
126+
spec:
127+
identity:
128+
name: $MEMBER_CLUSTER_NAME-hub-cluster-access
129+
kind: ServiceAccount
130+
namespace: fleet-system
131+
apiGroup: ""
132+
heartbeatPeriodSeconds: 15
133+
EOF
134+
135+
# Install the member agent helm chart on the member cluster.
136+
137+
echo "Switching to member cluster context.."
138+
kubectl config use $MEMBER_CLUSTER_NAME
139+
140+
# Create the secret with the token extracted previously for member agent to use.
141+
echo "Creating secret..."
142+
kubectl delete secret hub-kubeconfig-secret
143+
kubectl create secret generic hub-kubeconfig-secret --from-literal=token=$TOKEN
144+
145+
echo "Uninstalling any existing member-agent instances..."
146+
helm uninstall member-agent -n fleet-system --wait
147+
148+
echo "Installing member-agent..."
149+
helm install member-agent oci://ghcr.io/kubefleet-dev/kubefleet/charts/member-agent \
150+
--version $KUBEFLEET_VERSION \
151+
--set config.hubURL=$HUB_CONTROL_PLANE_URL \
152+
--set config.memberClusterName=$MEMBER_CLUSTER_NAME \
153+
--set logFileMaxSize=100000 \
154+
--namespace fleet-system \
155+
--create-namespace
156+
157+
kubectl get pods -A
158+
kubectl config use $HUB_CLUSTER_NAME
159+
kubectl get membercluster $MEMBER_CLUSTER_NAME
160+
done

0 commit comments

Comments
 (0)