github.com/AliyunContainerService/cli@v0.0.0-20181009023821-814ced4b30d0/cli/command/stack/kubernetes/deploy.go (about) 1 package kubernetes 2 3 import ( 4 "fmt" 5 "io" 6 7 "github.com/docker/cli/cli/command" 8 "github.com/docker/cli/cli/command/stack/options" 9 composetypes "github.com/docker/cli/cli/compose/types" 10 "github.com/morikuni/aec" 11 ) 12 13 // RunDeploy is the kubernetes implementation of docker stack deploy 14 func RunDeploy(dockerCli *KubeCli, opts options.Deploy, cfg *composetypes.Config) error { 15 cmdOut := dockerCli.Out() 16 17 // Initialize clients 18 composeClient, err := dockerCli.composeClient() 19 if err != nil { 20 return err 21 } 22 stacks, err := composeClient.Stacks(false) 23 if err != nil { 24 return err 25 } 26 27 stack, err := stacks.FromCompose(dockerCli.Err(), opts.Namespace, cfg) 28 if err != nil { 29 return err 30 } 31 32 configMaps := composeClient.ConfigMaps() 33 secrets := composeClient.Secrets() 34 services := composeClient.Services() 35 36 if err := stacks.IsColliding(services, stack); err != nil { 37 return err 38 } 39 40 if err := stack.createFileBasedConfigMaps(configMaps); err != nil { 41 return err 42 } 43 44 if err := stack.createFileBasedSecrets(secrets); err != nil { 45 return err 46 } 47 48 if err = stacks.CreateOrUpdate(stack); err != nil { 49 return err 50 } 51 52 fmt.Fprintln(cmdOut, "Waiting for the stack to be stable and running...") 53 v1beta1Cli, err := dockerCli.stacksv1beta1() 54 if err != nil { 55 return err 56 } 57 58 pods := composeClient.Pods() 59 watcher := &deployWatcher{ 60 stacks: v1beta1Cli, 61 pods: pods, 62 } 63 statusUpdates := make(chan serviceStatus) 64 displayDone := make(chan struct{}) 65 go func() { 66 defer close(displayDone) 67 display := newStatusDisplay(dockerCli.Out()) 68 for status := range statusUpdates { 69 display.OnStatus(status) 70 } 71 }() 72 73 err = watcher.Watch(stack.Name, stack.getServices(), statusUpdates) 74 close(statusUpdates) 75 <-displayDone 76 if err != nil { 77 return err 78 } 79 fmt.Fprintf(cmdOut, "\nStack %s is stable and running\n\n", stack.Name) 80 return nil 81 82 } 83 84 type statusDisplay interface { 85 OnStatus(serviceStatus) 86 } 87 type metaServiceState string 88 89 const ( 90 metaServiceStateReady = metaServiceState("Ready") 91 metaServiceStatePending = metaServiceState("Pending") 92 metaServiceStateFailed = metaServiceState("Failed") 93 ) 94 95 func metaStateFromStatus(status serviceStatus) metaServiceState { 96 switch { 97 case status.podsReady > 0: 98 return metaServiceStateReady 99 case status.podsPending > 0: 100 return metaServiceStatePending 101 default: 102 return metaServiceStateFailed 103 } 104 } 105 106 type forwardOnlyStatusDisplay struct { 107 o *command.OutStream 108 states map[string]metaServiceState 109 } 110 111 func (d *forwardOnlyStatusDisplay) OnStatus(status serviceStatus) { 112 state := metaStateFromStatus(status) 113 if d.states[status.name] != state { 114 d.states[status.name] = state 115 fmt.Fprintf(d.o, "%s: %s\n", status.name, state) 116 } 117 } 118 119 type interactiveStatusDisplay struct { 120 o *command.OutStream 121 statuses []serviceStatus 122 } 123 124 func (d *interactiveStatusDisplay) OnStatus(status serviceStatus) { 125 b := aec.EmptyBuilder 126 for ix := 0; ix < len(d.statuses); ix++ { 127 b = b.Up(1).EraseLine(aec.EraseModes.All) 128 } 129 b = b.Column(0) 130 fmt.Fprint(d.o, b.ANSI) 131 updated := false 132 for ix, s := range d.statuses { 133 if s.name == status.name { 134 d.statuses[ix] = status 135 s = status 136 updated = true 137 } 138 displayInteractiveServiceStatus(s, d.o) 139 } 140 if !updated { 141 d.statuses = append(d.statuses, status) 142 displayInteractiveServiceStatus(status, d.o) 143 } 144 } 145 146 func displayInteractiveServiceStatus(status serviceStatus, o io.Writer) { 147 state := metaStateFromStatus(status) 148 totalFailed := status.podsFailed + status.podsSucceeded + status.podsUnknown 149 fmt.Fprintf(o, "%[1]s: %[2]s\t\t[pod status: %[3]d/%[6]d ready, %[4]d/%[6]d pending, %[5]d/%[6]d failed]\n", status.name, state, 150 status.podsReady, status.podsPending, totalFailed, status.podsTotal) 151 } 152 153 func newStatusDisplay(o *command.OutStream) statusDisplay { 154 if !o.IsTerminal() { 155 return &forwardOnlyStatusDisplay{o: o, states: map[string]metaServiceState{}} 156 } 157 return &interactiveStatusDisplay{o: o} 158 }