@@ -2,6 +2,7 @@ package main
2
2
3
3
import (
4
4
"context"
5
+ "fmt"
5
6
"io/ioutil"
6
7
"os/exec"
7
8
"path/filepath"
@@ -16,8 +17,10 @@ import (
16
17
"k8s.io/apimachinery/pkg/util/wait"
17
18
"k8s.io/apimachinery/pkg/watch"
18
19
"k8s.io/client-go/kubernetes"
20
+ "k8s.io/client-go/rest"
19
21
"k8s.io/client-go/tools/clientcmd"
20
22
23
+ routeclient "github.com/openshift/client-go/route/clientset/versioned"
21
24
"github.com/openshift/installer/pkg/asset"
22
25
"github.com/openshift/installer/pkg/asset/cluster"
23
26
"github.com/openshift/installer/pkg/asset/ignition/bootstrap"
@@ -89,11 +92,22 @@ var (
89
92
// FIXME: add longer descriptions for our commands with examples for better UX.
90
93
// Long: "",
91
94
PostRunE : func (_ * cobra.Command , _ []string ) error {
92
- err := destroyBootstrap (context .Background (), rootOpts .dir )
95
+ ctx := context .Background ()
96
+ config , err := clientcmd .BuildConfigFromFlags ("" , filepath .Join (rootOpts .dir , "auth" , "kubeconfig" ))
97
+ if err != nil {
98
+ return errors .Wrap (err , "loading kubeconfig" )
99
+ }
100
+
101
+ err = destroyBootstrap (ctx , config , rootOpts .dir )
102
+ if err != nil {
103
+ return err
104
+ }
105
+ consoleURL , err := waitForConsole (ctx , config , rootOpts .dir )
93
106
if err != nil {
94
107
return err
95
108
}
96
- return logComplete (rootOpts .dir )
109
+
110
+ return logComplete (rootOpts .dir , consoleURL )
97
111
},
98
112
},
99
113
assets : []asset.WritableAsset {& cluster.TerraformVariables {}, & kubeconfig.Admin {}, & cluster.Cluster {}},
@@ -160,18 +174,13 @@ func runTargetCmd(targets ...asset.WritableAsset) func(cmd *cobra.Command, args
160
174
161
175
// FIXME: pulling the kubeconfig and metadata out of the root
162
176
// directory is a bit cludgy when we already have them in memory.
163
- func destroyBootstrap (ctx context.Context , directory string ) (err error ) {
177
+ func destroyBootstrap (ctx context.Context , config * rest. Config , directory string ) (err error ) {
164
178
cleanup , err := setupFileHook (rootOpts .dir )
165
179
if err != nil {
166
180
return errors .Wrap (err , "failed to setup logging hook" )
167
181
}
168
182
defer cleanup ()
169
183
170
- config , err := clientcmd .BuildConfigFromFlags ("" , filepath .Join (directory , "auth" , "kubeconfig" ))
171
- if err != nil {
172
- return errors .Wrap (err , "loading kubeconfig" )
173
- }
174
-
175
184
client , err := kubernetes .NewForConfig (config )
176
185
if err != nil {
177
186
return errors .Wrap (err , "creating a Kubernetes client" )
@@ -262,8 +271,62 @@ func destroyBootstrap(ctx context.Context, directory string) (err error) {
262
271
return destroybootstrap .Destroy (rootOpts .dir )
263
272
}
264
273
274
+ // waitForconsole returns the console URL from the route 'console' in namespace openshift-console
275
+ func waitForConsole (ctx context.Context , config * rest.Config , directory string ) (string , error ) {
276
+ url := ""
277
+ // Need to keep these updated if they change
278
+ consoleNamespace := "openshift-console"
279
+ consoleRouteName := "console"
280
+ rc , err := routeclient .NewForConfig (config )
281
+ if err != nil {
282
+ return "" , errors .Wrap (err , "creating a route client" )
283
+ }
284
+
285
+ consoleRouteTimeout := 10 * time .Minute
286
+ logrus .Infof ("Waiting %v for the openshift-console route to be created..." , consoleRouteTimeout )
287
+ consoleRouteContext , cancel := context .WithTimeout (ctx , consoleRouteTimeout )
288
+ defer cancel ()
289
+ // Poll quickly but only log when the response
290
+ // when we've seen 15 of the same errors or output of
291
+ // no route in a row (to show we're still alive).
292
+ logDownsample := 15
293
+ silenceRemaining := logDownsample
294
+ wait .Until (func () {
295
+ consoleRoutes , err := rc .RouteV1 ().Routes (consoleNamespace ).List (metav1.ListOptions {})
296
+ if err == nil && len (consoleRoutes .Items ) > 0 {
297
+ for _ , route := range consoleRoutes .Items {
298
+ logrus .Debugf ("Route found in openshift-console namespace: %s\n " , route .Name )
299
+ if route .Name == consoleRouteName {
300
+ url = fmt .Sprintf ("https://%s" , route .Spec .Host )
301
+ }
302
+ }
303
+ logrus .Debug ("OpenShift console route is created" )
304
+ cancel ()
305
+ } else if err != nil {
306
+ silenceRemaining --
307
+ if silenceRemaining == 0 {
308
+ logrus .Debugf ("Still waiting for the console route: %v" , err )
309
+ silenceRemaining = logDownsample
310
+ }
311
+ } else if len (consoleRoutes .Items ) == 0 {
312
+ silenceRemaining --
313
+ if silenceRemaining == 0 {
314
+ logrus .Debug ("Still waiting for the console route..." )
315
+ silenceRemaining = logDownsample
316
+ }
317
+ }
318
+ }, 2 * time .Second , consoleRouteContext .Done ())
319
+ if err != nil {
320
+ return "" , errors .Wrap (err , "waiting for console route to be created" )
321
+ }
322
+ if url == "" {
323
+ return url , errors .Wrap (err , "could not obtain openshift-console URL from route" )
324
+ }
325
+ return url , nil
326
+ }
327
+
265
328
// logComplete prints info upon completion
266
- func logComplete (directory string ) error {
329
+ func logComplete (directory , consoleURL string ) error {
267
330
absDir , err := filepath .Abs (directory )
268
331
if err != nil {
269
332
return err
@@ -274,7 +337,10 @@ func logComplete(directory string) error {
274
337
if err != nil {
275
338
return err
276
339
}
277
- logrus .Infof ("kubeadmin user password: %s" , pw )
278
- logrus .Infof ("Install complete! The kubeconfig is located here: %s" , kubeconfig )
340
+ logrus .Info ("Install complete!" )
341
+ logrus .Infof ("Run 'export KUBECONFIG=%s' to manage the cluster with 'oc', the OpenShift CLI." , kubeconfig )
342
+ logrus .Infof ("The cluster is ready when 'oc login -u kubeadmin -p %s' succeeds (wait a few minutes)." , pw )
343
+ logrus .Infof ("Access the OpenShift web-console here: %s" , consoleURL )
344
+ logrus .Infof ("Login to the console with user: kubeadmin, password: %s" , pw )
279
345
return nil
280
346
}
0 commit comments