forked from openshift/origin
/
router.go
303 lines (264 loc) · 10.1 KB
/
router.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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
package router
import (
"fmt"
"io"
"io/ioutil"
"os"
"strings"
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
kclientcmd "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
kutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/golang/glog"
"github.com/spf13/cobra"
"github.com/openshift/origin/pkg/cmd/util/clientcmd"
"github.com/openshift/origin/pkg/cmd/util/variable"
configcmd "github.com/openshift/origin/pkg/config/cmd"
dapi "github.com/openshift/origin/pkg/deploy/api"
"github.com/openshift/origin/pkg/generate/app"
)
const (
router_long = `Install or configure an OpenShift router
This command helps to setup an OpenShift router to take edge traffic and balance it to
your application. With no arguments, the command will check for an existing router
service called 'router' and create one if it does not exist. If you want to test whether
a router has already been created add the --dry-run flag and the command will exit with
1 if the registry does not exist.
If a router does not exist with the given name, the --create flag can be passed to
create a deployment configuration and service that will run the router. If you are
running your router in production, you should pass --replicas=2 or higher to ensure
you have failover protection.
ALPHA: This command is currently being actively developed. It is intended to simplify
the tasks of setting up routers in a new installation. `
router_example = ` // Check the default router ("router")
$ %[1]s %[2]s --dry-run
// See what the router would look like if created
$ %[1]s %[2]s -o json
// Create a router if it does not exist
$ %[1]s %[2]s router-west --create --replicas=2
// Use a different router image and see the router configuration
$ %[1]s %[2]s region-west -o yaml --images=myrepo/somerouter:mytag`
)
type RouterConfig struct {
Type string
ImageTemplate variable.ImageTemplate
Ports string
Replicas int
Labels string
DryRun bool
Credentials string
DefaultCertificate string
Selector string
}
var errExit = fmt.Errorf("exit")
const defaultLabel = "router=<name>"
func NewCmdRouter(f *clientcmd.Factory, parentName, name string, out io.Writer) *cobra.Command {
cfg := &RouterConfig{
ImageTemplate: variable.NewDefaultImageTemplate(),
Labels: defaultLabel,
Ports: "80:80,443:443,1936:1936",
Replicas: 1,
}
cmd := &cobra.Command{
Use: fmt.Sprintf("%s [NAME]", name),
Short: "Install an OpenShift router",
Long: router_long,
Example: fmt.Sprintf(router_example, parentName, name),
Run: func(cmd *cobra.Command, args []string) {
err := RunCmdRouter(f, cmd, out, cfg, args)
if err != errExit {
cmdutil.CheckErr(err)
}
},
}
cmd.Flags().StringVar(&cfg.Type, "type", "haproxy-router", "The type of router to use - if you specify --images this flag may be ignored.")
cmd.Flags().StringVar(&cfg.ImageTemplate.Format, "images", cfg.ImageTemplate.Format, "The image to base this router on - ${component} will be replaced with --type")
cmd.Flags().BoolVar(&cfg.ImageTemplate.Latest, "latest-images", cfg.ImageTemplate.Latest, "If true, attempt to use the latest images for the router instead of the latest release.")
cmd.Flags().StringVar(&cfg.Ports, "ports", cfg.Ports, "A comma delimited list of ports or port pairs to expose on the router pod. The default is set for HAProxy.")
cmd.Flags().IntVar(&cfg.Replicas, "replicas", cfg.Replicas, "The replication factor of the router; commonly 2 when high availability is desired.")
cmd.Flags().StringVar(&cfg.Labels, "labels", cfg.Labels, "A set of labels to uniquely identify the router and its components.")
cmd.Flags().BoolVar(&cfg.DryRun, "dry-run", cfg.DryRun, "Exit with code 1 if the specified router does not exist.")
cmd.Flags().Bool("create", false, "deprecated; this is now the default behavior")
cmd.Flags().StringVar(&cfg.Credentials, "credentials", "", "Path to a .kubeconfig file that will contain the credentials the router should use to contact the master.")
cmd.Flags().StringVar(&cfg.DefaultCertificate, "default-cert", cfg.DefaultCertificate, "Optional path to a certificate file that be used as the default certificate. The file should contain the cert, key, and any CA certs necessary for the router to serve the certificate.")
cmd.Flags().StringVar(&cfg.Selector, "selector", cfg.Selector, "Selector used to filter nodes on deployment. Used to run routers on a specific set of nodes.")
cmdutil.AddPrinterFlags(cmd)
return cmd
}
func loadDefaultCert(file string) (string, error) {
if len(file) == 0 {
return "", nil
}
bytes, err := ioutil.ReadFile(file)
if err != nil {
return "", err
}
return string(bytes), err
}
func RunCmdRouter(f *clientcmd.Factory, cmd *cobra.Command, out io.Writer, cfg *RouterConfig, args []string) error {
var name string
switch len(args) {
case 0:
name = "router"
case 1:
name = args[0]
default:
return cmdutil.UsageError(cmd, "You may pass zero or one arguments to provide a name for the router")
}
ports, err := app.ContainerPortsFromString(cfg.Ports)
if err != nil {
glog.Fatal(err)
}
label := map[string]string{"router": name}
if cfg.Labels != defaultLabel {
valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Labels, ","))
if err != nil {
glog.Fatal(err)
}
if len(remove) > 0 {
return cmdutil.UsageError(cmd, "You may not pass negative labels in %q", cfg.Labels)
}
label = valid
}
nodeSelector := map[string]string{}
if len(cfg.Selector) > 0 {
valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Selector, ","))
if err != nil {
glog.Fatal(err)
}
if len(remove) > 0 {
return cmdutil.UsageError(cmd, "You may not pass negative labels in selector %q", cfg.Selector)
}
nodeSelector = valid
}
image := cfg.ImageTemplate.ExpandOrDie(cfg.Type)
namespace, err := f.OpenShiftClientConfig.Namespace()
if err != nil {
return fmt.Errorf("error getting client: %v", err)
}
_, kClient, err := f.Clients()
if err != nil {
return fmt.Errorf("error getting client: %v", err)
}
p, output, err := cmdutil.PrinterForCommand(cmd)
if err != nil {
return fmt.Errorf("unable to configure printer: %v", err)
}
generate := output
if !generate {
_, err = kClient.Services(namespace).Get(name)
if err != nil {
if !errors.IsNotFound(err) {
return fmt.Errorf("can't check for existing router %q: %v", name, err)
}
generate = true
}
}
if generate {
if cfg.DryRun && !output {
return fmt.Errorf("router %q does not exist (no service)", name)
}
// create new router
if len(cfg.Credentials) == 0 {
return fmt.Errorf("router could not be created; you must specify a .kubeconfig file path containing credentials for connecting the router to the master with --credentials")
}
clientConfigLoadingRules := &kclientcmd.ClientConfigLoadingRules{ExplicitPath: cfg.Credentials, Precedence: []string{}}
credentials, err := clientConfigLoadingRules.Load()
if err != nil {
return fmt.Errorf("router could not be created; the provided credentials %q could not be loaded: %v", cfg.Credentials, err)
}
config, err := kclientcmd.NewDefaultClientConfig(*credentials, &kclientcmd.ConfigOverrides{}).ClientConfig()
if err != nil {
return fmt.Errorf("router could not be created; the provided credentials %q could not be used: %v", cfg.Credentials, err)
}
if err := kclient.LoadTLSFiles(config); err != nil {
return fmt.Errorf("router could not be created; the provided credentials %q could not load certificate info: %v", cfg.Credentials, err)
}
insecure := "false"
if config.Insecure {
insecure = "true"
}
defaultCert, err := loadDefaultCert(cfg.DefaultCertificate)
if err != nil {
return fmt.Errorf("router could not be created; error reading default certificate file", err)
}
env := app.Environment{
"OPENSHIFT_MASTER": config.Host,
"OPENSHIFT_CA_DATA": string(config.CAData),
"OPENSHIFT_KEY_DATA": string(config.KeyData),
"OPENSHIFT_CERT_DATA": string(config.CertData),
"OPENSHIFT_INSECURE": insecure,
"DEFAULT_CERTIFICATE": defaultCert,
}
objects := []runtime.Object{
&dapi.DeploymentConfig{
ObjectMeta: kapi.ObjectMeta{
Name: name,
Labels: label,
},
Triggers: []dapi.DeploymentTriggerPolicy{
{Type: dapi.DeploymentTriggerOnConfigChange},
},
Template: dapi.DeploymentTemplate{
Strategy: dapi.DeploymentStrategy{
Type: dapi.DeploymentStrategyTypeRecreate,
},
ControllerTemplate: kapi.ReplicationControllerSpec{
Replicas: cfg.Replicas,
Selector: label,
Template: &kapi.PodTemplateSpec{
ObjectMeta: kapi.ObjectMeta{Labels: label},
Spec: kapi.PodSpec{
NodeSelector: nodeSelector,
Containers: []kapi.Container{
{
Name: "router",
Image: image,
Ports: ports,
Env: env.List(),
LivenessProbe: &kapi.Probe{
Handler: kapi.Handler{
TCPSocket: &kapi.TCPSocketAction{
Port: kutil.IntOrString{
IntVal: ports[0].ContainerPort,
},
},
},
InitialDelaySeconds: 10,
},
ImagePullPolicy: kapi.PullIfNotPresent,
},
},
},
},
},
},
},
}
objects = app.AddServices(objects)
// TODO: label all created objects with the same label - router=<name>
list := &kapi.List{Items: objects}
if output {
if err := p.PrintObj(list, out); err != nil {
return fmt.Errorf("Unable to print object: %v", err)
}
return nil
}
mapper, typer := f.Factory.Object()
bulk := configcmd.Bulk{
Mapper: mapper,
Typer: typer,
RESTClientFactory: f.Factory.RESTClient,
After: configcmd.NewPrintNameOrErrorAfter(out, os.Stderr),
}
if errs := bulk.Create(list, namespace); len(errs) != 0 {
return errExit
}
return nil
}
fmt.Fprintf(out, "Router %q service exists\n", name)
return nil
}