/
up.go
230 lines (197 loc) · 5.64 KB
/
up.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
package command
import (
"bufio"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"os"
"os/signal"
"runtime"
"strings"
"time"
"github.com/docker/libcompose/docker"
"github.com/docker/libcompose/project"
"github.com/hashicorp/logutils"
"github.com/samalba/dockerclient"
"github.com/tcnksm/boot2kubernetes/config"
)
const (
// CheckInterval is how often check k8s container is ready
CheckInterval = 3 * time.Second
// CheckTimeout is timeout for waiting k8s container is ready
CheckTimeOut = 300 * time.Second
)
type UpCommand struct {
Meta
}
func (c *UpCommand) Run(args []string) int {
var insecure bool
var logLevel string
flags := flag.NewFlagSet("up", flag.ContinueOnError)
flags.BoolVar(&insecure, "insecure", false, "")
flags.StringVar(&logLevel, "log-level", "info", "")
flags.Usage = func() { c.Ui.Error(c.Help()) }
errR, errW := io.Pipe()
errScanner := bufio.NewScanner(errR)
go func() {
for errScanner.Scan() {
c.Ui.Error(errScanner.Text())
}
}()
flags.SetOutput(errW)
if err := flags.Parse(args); err != nil {
return 1
}
compose, err := config.Asset("k8s.yml")
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Failed to read k8s.yml: %s", err))
return 1
}
// Set up docker client
clientFactory, err := docker.NewDefaultClientFactory(
docker.ClientOpts{
TLS: !insecure,
},
)
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Failed to construct Docker client: %s", err))
return 1
}
// Setup new docker-compose project
context := &docker.Context{
Context: project.Context{
Log: false,
ComposeBytes: compose,
ProjectName: "boot2k8s",
},
ClientFactory: clientFactory,
}
// Setup new docker-compose project
project, err := docker.NewProject(context)
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Failed to setup project: %s", err))
return 1
}
c.Ui.Output("Start kubernetes cluster!")
upErrCh := make(chan error)
go func() {
if err := project.Up(); err != nil {
upErrCh <- err
}
}()
client := clientFactory.Create(nil)
sigCh := make(chan os.Signal)
signal.Notify(sigCh, os.Interrupt)
select {
case <-afterContainerReady(client):
c.Ui.Info("Successfully start kubernetes cluster")
case err := <-upErrCh:
c.Ui.Error("")
c.Ui.Error(fmt.Sprintf("Failed to start containers: %s", err))
c.Ui.Error("Check docker daemon is working")
return 1
case <-sigCh:
c.Ui.Error("")
c.Ui.Error("Interrupted!")
c.Ui.Error("It's ambiguous that boot2kubernetes could correctly start containers.")
c.Ui.Error("So request to kubelet may be failed. Check the containers are working")
c.Ui.Error("with `docker ps` command by yourself.")
return 1
case <-time.After(CheckTimeOut):
c.Ui.Error("")
c.Ui.Error("Timeout happened while waiting cluster containers are ready.")
c.Ui.Error("It's ambiguous that boot2kubernetes could correctly start containers.")
c.Ui.Error("So request to kubelet may be failed. Check the containers are working")
c.Ui.Error("with `docker ps` command by yourself.")
return 1
}
// If docker runs on boot2docker, port forwarding is needed.
if runtime.GOOS == "darwin" {
c.Ui.Output("")
c.Ui.Output("==> WARNING: You're running docker on boot2docker!")
c.Ui.Output(" To connect to master api server from local environment,")
c.Ui.Output(" port forwarding is needed. boot2kubernetes starts ")
c.Ui.Output(" server for that. To stop server, use ^C (Interrupt).\n")
// Create logger with Log level
logger := log.New(&logutils.LevelFilter{
Levels: []logutils.LogLevel{"DEBUG", "INFO", "WARN", "ERROR"},
MinLevel: (logutils.LogLevel)(strings.ToUpper(logLevel)),
Writer: os.Stderr,
}, "", log.LstdFlags)
logger.Printf("[DEBUG] LogLevel: %s", logLevel)
// Setup port forward server
server := &PortForwardServer{
Logger: logger,
LocalServer: DefaultLocalServer,
RemoteServer: DefaultRemoteServer,
}
doneCh, errCh, err := server.Start()
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Failed to start port forwarding server: %s", err))
return 1
}
sigCh := make(chan os.Signal)
signal.Notify(sigCh, os.Interrupt)
select {
case err := <-errCh:
c.Ui.Error(fmt.Sprintf(
"Error while running port forwarding server: %s", err))
close(doneCh)
return 1
case <-sigCh:
c.Ui.Error("\nInterrupted!")
close(doneCh)
// Need some time for closing work...
time.Sleep(ClosingTime)
}
}
return 0
}
func (c *UpCommand) Synopsis() string {
return "Up kubernetes cluster"
}
func (c *UpCommand) Help() string {
helpText := `Up kubernetes cluster
Options:
-insecure Allow insecure non-TLS connection to docker client.
`
return strings.TrimSpace(helpText)
}
// afterContainerReady waits for the cluster ready and then sends the struct{}
// on the returned channel. Detection of cluster ready is very heuristic way,
// just checking number of container which is needed for running cluster.
func afterContainerReady(c dockerclient.Client) chan struct{} {
doneCh := make(chan struct{})
// Marshaling to post filter as API request
filterLocalMasterStr, err := json.Marshal(FilterLocalMaster)
if err != nil {
// Should not reach here....
panic(fmt.Sprintf(
"Failed to marshal FilterLocalMaster: %s", err))
}
ticker := time.NewTicker(CheckInterval)
go func() {
fmt.Fprintf(os.Stderr, "Wait until containers are ready")
for _ = range ticker.C {
fmt.Fprintf(os.Stderr, ".")
// Get Container info from daemon based on filter
localMasters, err := c.ListContainers(true, false, (string)(filterLocalMasterStr))
if err != nil {
// Just ignore error
continue
}
if len(localMasters) > 3 {
fmt.Fprintf(os.Stderr, "\n")
doneCh <- struct{}{}
ticker.Stop()
}
}
}()
return doneCh
}