Skip to content

Commit 49cf1eb

Browse files
authored
Wait for installation status on install/restore (#577)
* add installation wait spinner to kubeutils * don't wait for installation state as that is in the installer now * call from within admin console outro * fix spinner * refactor installation spinner and outro message out of admin console * remove 'finished', do not print admin console message if no license * renaming * comments * wait for installation in old version upgrade test * fix old version upgrade test again * test with Alma, not Rocky, Linux * better installation failure/timeout logs * wording * pass the installation status to a channel * update operator chart * LOGS LOGS LOGS * handle zero case * unsupported overrides chart update * why did this message stop showing up * fix finishing vs finalizing * validating license, not finalizing * debug * ? * add operator * update operator version to v0.30.2 * remove logs * attempt to resolve race * add mutex, because of course that's the solution * combine kotsadm output lines * undo mask changes * match wording in review Additional components are ready/waiting for additional components to be ready * simple suggestions from code review * refactor to remove unneeded channel architecture * close spinner on error * branding configmap question
1 parent 8e8f3e1 commit 49cf1eb

File tree

9 files changed

+177
-97
lines changed

9 files changed

+177
-97
lines changed

e2e/scripts/check-airgap-installation-state.sh

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,6 @@
11
#!/usr/bin/env bash
22
set -euox pipefail
33

4-
wait_for_installation() {
5-
ready=$(kubectl get installations --no-headers | grep -c "Installed" || true)
6-
counter=0
7-
while [ "$ready" -lt "1" ]; do
8-
if [ "$counter" -gt 36 ]; then
9-
echo "installation did not become ready"
10-
kubectl get installations 2>&1 || true
11-
kubectl describe installations 2>&1 || true
12-
kubectl get charts -A
13-
kubectl describe chart -n kube-system k0s-addon-chart-ingress-nginx
14-
kubectl get secrets -A
15-
kubectl describe clusterconfig -A
16-
echo "operator logs:"
17-
kubectl logs -n embedded-cluster -l app.kubernetes.io/name=embedded-cluster-operator --tail=100
18-
return 1
19-
fi
20-
sleep 5
21-
counter=$((counter+1))
22-
echo "Waiting for installation"
23-
ready=$(kubectl get installations --no-headers | grep -c "Installed" || true)
24-
kubectl get installations 2>&1 || true
25-
done
26-
}
27-
284
wait_for_nginx_pods() {
295
ready=$(kubectl get pods -n kotsadm -o jsonpath='{.items[*].metadata.name} {.items[*].status.phase}' | grep "nginx" | grep -c Running || true)
306
counter=0
@@ -86,19 +62,18 @@ ensure_app_not_upgraded() {
8662

8763
main() {
8864
local version="appver-$1"
89-
sleep 30 # wait for kubectl to become available
65+
sleep 10 # wait for kubectl to become available
9066

9167
echo "pods"
9268
kubectl get pods -A
9369

9470
echo "ensure that installation is installed"
95-
wait_for_installation
9671
kubectl get installations --no-headers | grep -q "Installed"
9772

9873
echo "ensure that the admin console branding is available and has the DR label"
9974
if ! kubectl get cm -n kotsadm -l replicated.com/disaster-recovery=infra | grep -q kotsadm-application-metadata; then
10075
echo "kotsadm-application-metadata configmap not found with the DR label"
101-
kubectl get cm -n kotsadm
76+
kubectl get cm -n kotsadm --show-labels
10277
kubectl get cm -n kotsadm kotsadm-application-metadata -o yaml
10378
exit 1
10479
fi

e2e/scripts/check-installation-state.sh

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,22 @@ ensure_app_not_upgraded() {
6868

6969
main() {
7070
local version="$1"
71-
sleep 30 # wait for kubectl to become available
71+
sleep 10 # wait for kubectl to become available
7272

7373
echo "pods"
7474
kubectl get pods -A
7575

7676
echo "ensure that installation is installed"
77-
wait_for_installation
77+
if echo "$version" | grep "pre-minio-removal"; then
78+
echo "waiting for installation as this is a pre-minio-removal embedded-cluster version (and so the installer doesn't wait for the installation to be ready itself)"
79+
wait_for_installation
80+
fi
7881
kubectl get installations --no-headers | grep -q "Installed"
7982

8083
echo "ensure that the admin console branding is available and has the DR label"
8184
if ! kubectl get cm -n kotsadm -l replicated.com/disaster-recovery=infra | grep -q kotsadm-application-metadata; then
8285
echo "kotsadm-application-metadata configmap not found with the DR label"
83-
kubectl get cm -n kotsadm
86+
kubectl get cm -n kotsadm --show-labels
8487
kubectl get cm -n kotsadm kotsadm-application-metadata -o yaml
8588
exit 1
8689
fi

e2e/scripts/single-node-airgap-install.sh

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -124,30 +124,6 @@ check_airgap_pvc() {
124124
fi
125125
}
126126

127-
wait_for_installation() {
128-
ready=$(kubectl get installations --no-headers | grep -c "Installed" || true)
129-
counter=0
130-
while [ "$ready" -lt "1" ]; do
131-
if [ "$counter" -gt 36 ]; then
132-
echo "installation did not become ready"
133-
kubectl get installations 2>&1 || true
134-
kubectl describe installations 2>&1 || true
135-
kubectl get charts -A
136-
kubectl describe chart -n kube-system k0s-addon-chart-ingress-nginx
137-
kubectl get secrets -A
138-
kubectl describe clusterconfig -A
139-
echo "operator logs:"
140-
kubectl logs -n embedded-cluster -l app.kubernetes.io/name=embedded-cluster-operator --tail=100
141-
return 1
142-
fi
143-
sleep 5
144-
counter=$((counter+1))
145-
echo "Waiting for installation"
146-
ready=$(kubectl get installations --no-headers | grep -c "Installed" || true)
147-
kubectl get installations 2>&1 || true
148-
done
149-
}
150-
151127
main() {
152128
if ! embedded-cluster install --no-prompt --license /tmp/license.yaml --airgap-bundle /tmp/release.airgap 2>&1 | tee /tmp/log ; then
153129
echo "Failed to install embedded-cluster"
@@ -192,7 +168,6 @@ main() {
192168
fi
193169

194170
echo "ensure that installation is installed"
195-
wait_for_installation
196171
kubectl get installations --no-headers | grep -q "Installed"
197172
}
198173

e2e/scripts/unsupported-overrides.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ spec:
4646
name: embedded-cluster-operator
4747
namespace: embedded-cluster
4848
order: 2
49-
version: 0.13.0
49+
version: 0.30.1
5050
- chartname: oci://registry.replicated.com/library/admin-console
5151
name: admin-console
5252
namespace: kotsadm

pkg/addons/adminconsole/adminconsole.go

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -249,49 +249,29 @@ func (a *AdminConsole) Outro(ctx context.Context, cli client.Client) error {
249249
return fmt.Errorf("error waiting for admin console: %v", lasterr)
250250
}
251251

252-
loading.Closef("Admin Console is ready!")
253-
if a.licenseFile == "" {
254-
return nil
255-
}
252+
if a.licenseFile != "" {
253+
license, err := helpers.ParseLicense(a.licenseFile)
254+
if err != nil {
255+
loading.CloseWithError()
256+
return fmt.Errorf("unable to parse license: %w", err)
257+
}
256258

257-
license, err := helpers.ParseLicense(a.licenseFile)
258-
if err != nil {
259-
loading.CloseWithError()
260-
return fmt.Errorf("unable to parse license: %w", err)
259+
if err := kotscli.Install(kotscli.InstallOptions{
260+
AppSlug: license.Spec.AppSlug,
261+
LicenseFile: a.licenseFile,
262+
Namespace: a.namespace,
263+
AirgapBundle: a.airgapBundle,
264+
}, loading); err != nil {
265+
loading.CloseWithError()
266+
return err
267+
}
261268
}
262269

263-
if err := kotscli.Install(kotscli.InstallOptions{
264-
AppSlug: license.Spec.AppSlug,
265-
LicenseFile: a.licenseFile,
266-
Namespace: a.namespace,
267-
AirgapBundle: a.airgapBundle,
268-
}); err != nil {
269-
return err
270-
}
270+
loading.Closef("Admin Console is ready!")
271271

272-
a.printSuccessMessage(license.Spec.AppSlug)
273272
return nil
274273
}
275274

276-
// printSuccessMessage prints the success message when the admin console is online.
277-
func (a *AdminConsole) printSuccessMessage(appSlug string) {
278-
successColor := "\033[32m"
279-
colorReset := "\033[0m"
280-
ipaddr := defaults.TryDiscoverPublicIP()
281-
if ipaddr == "" {
282-
var err error
283-
ipaddr, err = defaults.PreferredNodeIPAddress()
284-
if err != nil {
285-
logrus.Errorf("unable to determine node IP address: %v", err)
286-
ipaddr = "NODE-IP-ADDRESS"
287-
}
288-
}
289-
successMessage := fmt.Sprintf("Visit the admin console to configure and install %s: %shttp://%s:%v%s",
290-
appSlug, successColor, ipaddr, DEFAULT_ADMIN_CONSOLE_NODE_PORT, colorReset,
291-
)
292-
logrus.Info(successMessage)
293-
}
294-
295275
// New creates a new AdminConsole object.
296276
func New(ns string, useprompt bool, config v1beta1.ClusterConfig, licenseFile string, airgapBundle string) (*AdminConsole, error) {
297277
return &AdminConsole{

pkg/addons/applier.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import (
1212
k0sconfig "github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1"
1313
embeddedclusterv1beta1 "github.com/replicatedhq/embedded-cluster-kinds/apis/v1beta1"
1414
"github.com/replicatedhq/embedded-cluster-kinds/types"
15+
kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
1516
"github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
17+
"github.com/sirupsen/logrus"
1618
corev1 "k8s.io/api/core/v1"
1719
"sigs.k8s.io/controller-runtime/pkg/client"
1820

@@ -65,6 +67,16 @@ func (a *Applier) Outro(ctx context.Context) error {
6567
return err
6668
}
6769
}
70+
71+
err = spinForInstallation(ctx, kcli)
72+
if err != nil {
73+
return err
74+
}
75+
76+
err = printKotsadmLinkMessage(a.licenseFile)
77+
if err != nil {
78+
return fmt.Errorf("unable to print success message: %w", err)
79+
}
6880
return nil
6981
}
7082

@@ -282,6 +294,57 @@ func (a *Applier) waitForKubernetes(ctx context.Context) error {
282294
}
283295
}
284296

297+
func spinForInstallation(ctx context.Context, cli client.Client) error {
298+
installSpin := spinner.Start()
299+
installSpin.Infof("Waiting for additional components to be ready")
300+
301+
err := kubeutils.WaitForInstallation(ctx, cli, installSpin)
302+
if err != nil {
303+
installSpin.CloseWithError()
304+
return fmt.Errorf("unable to wait for installation to be ready: %w", err)
305+
}
306+
installSpin.Closef("Additional components are ready!")
307+
return nil
308+
}
309+
310+
// printKotsadmLinkMessage prints the success message when the admin console is online.
311+
func printKotsadmLinkMessage(licenseFile string) error {
312+
var err error
313+
license := &kotsv1beta1.License{}
314+
if licenseFile != "" {
315+
license, err = helpers.ParseLicense(licenseFile)
316+
if err != nil {
317+
return fmt.Errorf("unable to parse license: %w", err)
318+
}
319+
}
320+
321+
successColor := "\033[32m"
322+
colorReset := "\033[0m"
323+
ipaddr := defaults.TryDiscoverPublicIP()
324+
if ipaddr == "" {
325+
var err error
326+
ipaddr, err = defaults.PreferredNodeIPAddress()
327+
if err != nil {
328+
logrus.Errorf("unable to determine node IP address: %v", err)
329+
ipaddr = "NODE-IP-ADDRESS"
330+
}
331+
}
332+
var successMessage string
333+
if license != nil {
334+
successMessage = fmt.Sprintf("Visit the admin console to configure and install %s: %shttp://%s:%v%s",
335+
license.Spec.AppSlug, successColor, ipaddr, adminconsole.DEFAULT_ADMIN_CONSOLE_NODE_PORT, colorReset,
336+
)
337+
} else {
338+
successMessage = fmt.Sprintf("Visit the admin console to configure and install your application: %shttp://%s:%v%s",
339+
successColor, ipaddr, adminconsole.DEFAULT_ADMIN_CONSOLE_NODE_PORT, colorReset,
340+
)
341+
342+
}
343+
logrus.Info(successMessage)
344+
345+
return nil
346+
}
347+
285348
// NewApplier creates a new Applier instance with all addons registered.
286349
func NewApplier(opts ...Option) *Applier {
287350
applier := &Applier{

pkg/kotscli/kotscli.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type InstallOptions struct {
2424
AirgapBundle string
2525
}
2626

27-
func Install(opts InstallOptions) error {
27+
func Install(opts InstallOptions, msg *spinner.MessageWriter) error {
2828
kotsBinPath, err := goods.MaterializeInternalBinary("kubectl-kots")
2929
if err != nil {
3030
return fmt.Errorf("unable to materialize kubectl-kots binary: %w", err)
@@ -64,19 +64,21 @@ func Install(opts InstallOptions) error {
6464
lbreakfn = KotsOutputLineBreaker()
6565
}
6666

67-
loading := spinner.Start(spinner.WithMask(maskfn), spinner.WithLineBreaker(lbreakfn))
67+
msg.SetLineBreaker(lbreakfn)
68+
msg.SetMask(maskfn)
69+
defer msg.SetMask(nil)
70+
defer msg.SetLineBreaker(nil)
71+
6872
runCommandOptions := helpers.RunCommandOptions{
69-
Writer: loading,
73+
Writer: msg,
7074
Env: map[string]string{
7175
"EMBEDDED_CLUSTER_ID": metrics.ClusterID().String(),
7276
},
7377
}
7478
if err := helpers.RunCommandWithOptions(runCommandOptions, kotsBinPath, installArgs...); err != nil {
75-
loading.CloseWithError()
7679
return fmt.Errorf("unable to install the application: %w", err)
7780
}
7881

79-
loading.Closef("Finished!")
8082
return nil
8183
}
8284

0 commit comments

Comments
 (0)