-
Notifications
You must be signed in to change notification settings - Fork 342
/
config.go
737 lines (655 loc) · 29.2 KB
/
config.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
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
package config
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import (
"bytes"
"errors"
"fmt"
"net/url"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/apache/trafficcontrol/v8/cache-config/t3cutil"
"github.com/apache/trafficcontrol/v8/lib/go-log"
"github.com/pborman/getopt/v2"
)
const AppName = "t3c-apply"
var TSHome string = "/opt/trafficserver"
const DefaultTSConfigDir = "/opt/trafficserver/etc/trafficserver"
const (
StatusDir = "/var/lib/trafficcontrol-cache-config/status"
Chkconfig = "/sbin/chkconfig"
Service = "/sbin/service"
SystemCtl = "/bin/systemctl"
TmpBase = "/tmp/trafficcontrol-cache-config"
TrafficCtl = "/bin/traffic_ctl"
TrafficServerOwner = "ats"
)
type SvcManagement int
const (
Unknown SvcManagement = 0
SystemD SvcManagement = 1
SystemV SvcManagement = 2 // legacy System V Init.
)
func (s SvcManagement) String() string {
switch s {
case Unknown:
return "Unknown"
case SystemD:
return "SystemD"
case SystemV:
return "SystemV"
}
return "Unknown"
}
type Cfg struct {
LogLocationDebug string
LogLocationErr string
LogLocationInfo string
LogLocationWarn string
CacheHostName string
SvcManagement SvcManagement
Retries int
ReverseProxyDisable bool
RpmDBOk bool
SkipOSCheck bool
UseStrategies t3cutil.UseStrategiesFlag
TOInsecure bool
TOTimeoutMS time.Duration
TOUser string
TOPass string
TOURL string
DNSLocalBind bool
WaitForParents bool
YumOptions string
// UseGit is whether to create and maintain a git repo of config changes.
// Note this only applies to the ATS config directory inferred or set via the flag.
// It does not do anything for config files generated outside that location.
UseGit UseGitFlag
NoCache bool
SyncDSUpdatesIPAllow bool
OmitViaStringRelease bool
NoOutgoingIP bool
DisableParentConfigComments bool
DefaultClientEnableH2 *bool
DefaultClientTLSVersions *string
// MaxMindLocation is a URL string for a download location for a maxmind database
// for use with either HeaderRewrite or Maxmind_ACL plugins
MaxMindLocation string
TsHome string
TsConfigDir string
ServiceAction t3cutil.ApplyServiceActionFlag
NoConfirmServiceAction bool
ReportOnly bool
GoDirect string
Files t3cutil.ApplyFilesFlag
InstallPackages bool
IgnoreUpdateFlag bool
NoUnsetUpdateFlag bool
UpdateIPAllow bool
Version string
GitRevision string
LocalATSVersion string
CacheType string
}
func (cfg Cfg) AppVersion() string { return t3cutil.VersionStr(AppName, cfg.Version, cfg.GitRevision) }
type UseGitFlag string
const (
UseGitAuto = "auto"
UseGitYes = "yes"
UseGitNo = "no"
UseGitInvalid = ""
)
func StrToUseGitFlag(str string) UseGitFlag {
str = strings.ToLower(strings.TrimSpace(str))
switch str {
case UseGitAuto:
fallthrough
case UseGitYes:
fallthrough
case UseGitNo:
return UseGitFlag(str)
default:
return UseGitInvalid
}
}
type WaitForParentsFlag string
const WaitForParentsDefault = WaitForParentsReval
const TimeAndDateLayout = "Jan 2, 2006 15:04 MST"
const (
WaitForParentsTrue = "true"
WaitForParentsFalse = "false"
WaitForParentsReval = "reval"
WaitForParentsInvalid = ""
)
func StrToWaitForParentsFlag(str string) WaitForParentsFlag {
str = strings.ToLower(strings.TrimSpace(str))
switch str {
case WaitForParentsTrue:
fallthrough
case WaitForParentsFalse:
fallthrough
case WaitForParentsReval:
return WaitForParentsFlag(str)
default:
return WaitForParentsInvalid
}
}
func (cfg Cfg) ErrorLog() log.LogLocation { return log.LogLocation(cfg.LogLocationErr) }
func (cfg Cfg) WarningLog() log.LogLocation { return log.LogLocation(cfg.LogLocationWarn) }
func (cfg Cfg) InfoLog() log.LogLocation { return log.LogLocation(cfg.LogLocationInfo) }
func (cfg Cfg) DebugLog() log.LogLocation { return log.LogLocation(cfg.LogLocationDebug) }
func (cfg Cfg) EventLog() log.LogLocation { return log.LogLocation(log.LogLocationNull) } // event logging is not used.
func directoryExists(dir string) (bool, os.FileInfo) {
info, err := os.Stat(dir)
if os.IsNotExist(err) {
return false, nil
}
return info.IsDir(), info
}
const rpmDir = "/var/lib/rpm"
// verifies the rpm database files. if there is any database corruption
// it will return false
func verifyRpmDB() bool {
exclude := regexp.MustCompile(`(^\.|^__)`)
dbFiles, err := os.ReadDir(rpmDir)
if err != nil {
return false
}
for _, file := range dbFiles {
if exclude.Match([]byte(file.Name())) {
continue
}
cmd := exec.Command("/usr/lib/rpm/rpmdb_verify", rpmDir+"/"+file.Name())
err := cmd.Run()
if err != nil || cmd.ProcessState.ExitCode() > 0 {
return false
}
}
return true
}
// derives the ATS Installation directory from
// the rpm config file list.
func GetTSPackageHome() string {
var dir []string
var output bytes.Buffer
var tsHome string = ""
var files []string
cmd := exec.Command("/bin/rpm", "-q", "-c", "trafficserver")
cmd.Stdout = &output
err := cmd.Run()
// on error or if the trafficserver rpm is not installed indicated
// by a return code of '1', return an empty string.
if err != nil || cmd.ProcessState.ExitCode() == 1 {
return ""
} else {
responseStr := string(output.Bytes())
files = strings.Split(responseStr, "\n")
}
if files != nil && err == nil { // trafficserver is installed, derive TSHome
for ii := range files {
line := strings.TrimSpace(files[ii])
if strings.Contains(line, "etc/trafficserver") {
dir = strings.Split(line, "/etc")
break
}
}
if dir != nil && len(dir) >= 0 {
if ok, _ := directoryExists(dir[0]); ok == true {
tsHome = dir[0]
}
}
}
return tsHome
}
func GetCfg(appVersion string, gitRevision string) (Cfg, error) {
var err error
toInfoLog := []string{}
version := getopt.BoolLong("version", 'E', "Print version information and exit.")
cacheHostNamePtr := getopt.StringLong("cache-host-name", 'H', "", "Host name of the cache to generate config for. Must be the server host name in Traffic Ops, not a URL, and not the FQDN")
retriesPtr := getopt.IntLong("num-retries", 'r', 3, "[number] retry connection to Traffic Ops URL [number] times, default is 3")
reverseProxyDisablePtr := getopt.BoolLong("reverse-proxy-disable", 'p', "[false | true] bypass the reverse proxy even if one has been configured default is false")
skipOSCheckPtr := getopt.BoolLong("skip-os-check", 'C', "[false | true] skip os check, default is false")
toInsecurePtr := getopt.BoolLong("traffic-ops-insecure", 'I', "[true | false] ignore certificate errors from Traffic Ops")
toTimeoutMSPtr := getopt.IntLong("traffic-ops-timeout-milliseconds", 't', 30000, "Timeout in milli-seconds for Traffic Ops requests, default is 30000")
toURLPtr := getopt.StringLong("traffic-ops-url", 'u', "", "Traffic Ops URL. Must be the full URL, including the scheme. Required. May also be set with the environment variable TO_URL")
toUserPtr := getopt.StringLong("traffic-ops-user", 'U', "", "Traffic Ops username. Required. May also be set with the environment variable TO_USER")
toPassPtr := getopt.StringLong("traffic-ops-password", 'P', "", "Traffic Ops password. Required. May also be set with the environment variable TO_PASS")
tsHomePtr := getopt.StringLong("trafficserver-home", 'R', "", "Trafficserver Package directory. May also be set with the environment variable TS_HOME")
dnsLocalBindPtr := getopt.BoolLong("dns-local-bind", 'b', "[true | false] whether to use the server's Service Addresses to set the ATS DNS local bind address")
help := getopt.BoolLong("help", 'h', "Print usage information and exit")
useGitStr := getopt.StringLong("git", 'g', "auto", "Create and use a git repo in the config directory. Options are yes, no, and auto. If yes, create and use. If auto, use if it exist. Default is auto.")
noCachePtr := getopt.BoolLong("no-cache", 'n', "Whether to not use a cache and make conditional requests to Traffic Ops")
syncdsUpdatesIPAllowPtr := getopt.BoolLong("syncds-updates-ipallow", 'S', "Whether syncds mode will update ipallow. This exists because ATS had a bug where reloading after changing ipallow would block everything. Default is false.")
omitViaStringReleasePtr := getopt.BoolLong("omit-via-string-release", 'e', "Whether to set the records.config via header to the ATS release from the RPM. Default true.")
noOutgoingIP := getopt.BoolLong("no-outgoing-ip", 'i', "Whether to not set the records.config outgoing IP to the server's addresses in Traffic Ops. Default is false.")
disableParentConfigCommentsPtr := getopt.BoolLong("disable-parent-config-comments", 'c', "Whether to disable verbose parent.config comments. Default false.")
defaultEnableH2 := getopt.BoolLong("default-client-enable-h2", '2', "Whether to enable HTTP/2 on Delivery Services by default, if they have no explicit Parameter. This is irrelevant if ATS records.config is not serving H2. If omitted, H2 is disabled.")
defaultClientTLSVersions := getopt.StringLong("default-client-tls-versions", 'V', "", "Comma-delimited list of default TLS versions for Delivery Services with no Parameter, e.g. --default-tls-versions='1.1,1.2,1.3'. If omitted, all versions are enabled.")
maxmindLocationPtr := getopt.StringLong("maxmind-location", 'M', "", "URL of a maxmind gzipped database file, to be installed into the trafficserver etc directory.")
verbosePtr := getopt.CounterLong("verbose", 'v', `Log verbosity. Logging is output to stderr. By default, errors are logged. To log warnings, pass '-v'. To log info, pass '-vv'. To omit error logging, see '-s'`)
cache := getopt.StringLong("cache", 'T', "ats", "Cache server type. Generate configuration files for specific cache server type, e.g. 'ats', 'varnish'.")
const silentFlagName = "silent"
silentPtr := getopt.BoolLong(silentFlagName, 's', `Silent. Errors are not logged, and the 'verbose' flag is ignored. If a fatal error occurs, the return code will be non-zero but no text will be output to stderr`)
const goDirectFlagName = "go-direct"
goDirectPtr := getopt.StringLong(goDirectFlagName, 'G', "old", "[true|false|old] default will set go_direct to old and it will be based on opposite of parent_is_proxy directivefalse, you can also set go_direct true, or false.")
const waitForParentsFlagName = "wait-for-parents"
waitForParentsPtr := getopt.BoolLong(waitForParentsFlagName, 'W', "[true | false] do not update if parent_pending = 1 in the update json. Default is false")
const serviceActionFlagName = "service-action"
const defaultServiceAction = t3cutil.ApplyServiceActionFlagReload
serviceActionPtr := getopt.EnumLong(serviceActionFlagName, 'a', []string{string(t3cutil.ApplyServiceActionFlagReload), string(t3cutil.ApplyServiceActionFlagRestart), string(t3cutil.ApplyServiceActionFlagNone), ""}, "", "action to perform on Traffic Server and other system services. Only reloads if necessary, but always restarts. Default is 'reload'")
const noConfirmServiceActionFlagName = "no-confirm-service-action"
noConfirmServiceAction := getopt.BoolLong(noConfirmServiceActionFlagName, 0, "Whether to skip waiting and confirming the service action succeeded (reload or restart) via t3c-tail. Default is false.")
const reportOnlyFlagName = "report-only"
reportOnlyPtr := getopt.BoolLong(reportOnlyFlagName, 'o', "Log information about necessary files and actions, but take no action. Default is false")
const filesFlagName = "files"
const defaultFiles = t3cutil.ApplyFilesFlagAll
filesPtr := getopt.EnumLong(filesFlagName, 'f', []string{string(t3cutil.ApplyFilesFlagAll), string(t3cutil.ApplyFilesFlagReval), ""}, "", "[all | reval] Which files to generate. If reval, the Traffic Ops server reval_pending flag is used instead of the upd_pending flag. Default is 'all'")
const installPackagesFlagName = "install-packages"
installPackagesPtr := getopt.BoolLong(installPackagesFlagName, 'k', "Whether to install necessary packages. Default is false.")
const ignoreUpdateFlagName = "ignore-update-flag"
ignoreUpdateFlagPtr := getopt.BoolLong(ignoreUpdateFlagName, 'F', "Whether to ignore the upd_pending or reval_pending flag in Traffic Ops, and always generate and apply files. If true, the flag is still unset in Traffic Ops after files are applied. Default is false.")
noUnsetUpdateFlagPtr := getopt.BoolLong("no-unset-update-flag", 'd', "Whether to not unset the update flag in Traffic Ops after applying files. This option makes it possible to generate test or debug configuration from a production Traffic Ops without un-setting queue or reval flags. Default is false.")
const updateIPAllowFlagName = "update-ipallow"
updateIPAllowPtr := getopt.BoolLong(updateIPAllowFlagName, 'A', "Whether ipallow file will be updated if necessary. This exists because ATS had a bug where reloading after changing ipallow would block everything. Default is false.")
const useStrategiesFlagName = "use-strategies"
const defaultUseStrategies = t3cutil.UseStrategiesFlagFalse
useStrategiesPtr := getopt.EnumLong(useStrategiesFlagName, 0, []string{string(t3cutil.UseStrategiesFlagTrue), string(t3cutil.UseStrategiesFlagCore), string(t3cutil.UseStrategiesFlagCore), ""}, "", "[true | core| false] whether to generate config using strategies.yaml instead of parent.config. If true use the parent_select plugin, if 'core' use ATS core strategies, if false use parent.config.")
const useLocalATSVersionFlagName = "local-ats-version"
useLocalATSVersionPtr := getopt.BoolLong(useLocalATSVersionFlagName, 0, "[true | false] whether to use the local installed ATS version for config generation. If false, attempt to use the Server Package Parameter and fall back to ATS 5. If true and the local ATS version cannot be found, an error will be logged and the version set to ATS 5. Default is false")
const runModeFlagName = "run-mode"
runModePtr := getopt.StringLong(runModeFlagName, 'm', "", `[badass | report | revalidate | syncds] run mode. Optional, convenience flag which sets other flags for common usage scenarios.
syncds keeps the defaults:
--report-only=false
--files=all
--install-packages=false
--service-action=reload
--ignore-update-flag=false
--update-ipallow=false
--no-unset-update-flag=false
revalidate sets --files=reval
--wait-for-parents=true
badass sets --install-packages=true
--service-action=restart
--ignore-update-flag=true
--update-ipallow=true
report sets --report-only=true
--no-unset-update-flag=true
--silent
Note the 'syncds' settings are all the flag defaults. Hence, if no mode is set, the default is effectively 'syncds'.
If any of the related flags are also set, they override the mode's default behavior.`)
getopt.Parse()
// The mode is never exposed outside this function to prevent accidentally changing behavior based on it,
// so we want to log what flags the mode set here, to aid debugging.
// But we can't do that until the loggers are initialized.
modeLogStrs := []string{}
fatalLogStrs := []string{}
if getopt.IsSet(runModeFlagName) {
runMode := t3cutil.StrToMode(*runModePtr)
if runMode == t3cutil.ModeInvalid {
fatalLogStrs = append(fatalLogStrs, *runModePtr+" is an invalid mode.")
}
modeLogStrs = append(modeLogStrs, "t3c-apply is running in "+runMode.String()+" mode")
switch runMode {
case t3cutil.ModeSyncDS:
// syncds flags are all the defaults, no need to change anything
case t3cutil.ModeRevalidate:
if !getopt.IsSet(filesFlagName) {
modeLogStrs = append(modeLogStrs, runMode.String()+" setting --"+filesFlagName+"="+t3cutil.ApplyFilesFlagReval.String())
*filesPtr = t3cutil.ApplyFilesFlagReval.String()
}
if !getopt.IsSet(waitForParentsFlagName) {
modeLogStrs = append(modeLogStrs, runMode.String()+" setting --"+waitForParentsFlagName+"="+"true")
*waitForParentsPtr = true
}
case t3cutil.ModeBadAss:
if !getopt.IsSet(serviceActionFlagName) {
modeLogStrs = append(modeLogStrs, runMode.String()+" setting --"+serviceActionFlagName+"="+t3cutil.ApplyServiceActionFlagRestart.String())
*serviceActionPtr = t3cutil.ApplyServiceActionFlagRestart.String()
}
if !getopt.IsSet(installPackagesFlagName) {
modeLogStrs = append(modeLogStrs, runMode.String()+" setting --"+installPackagesFlagName+"="+"true")
*installPackagesPtr = true
}
if !getopt.IsSet(ignoreUpdateFlagName) {
modeLogStrs = append(modeLogStrs, runMode.String()+" setting --"+ignoreUpdateFlagName+"="+"true")
*ignoreUpdateFlagPtr = true
}
if !getopt.IsSet(updateIPAllowFlagName) {
modeLogStrs = append(modeLogStrs, runMode.String()+" setting --"+updateIPAllowFlagName+"="+"true")
*updateIPAllowPtr = true
}
case t3cutil.ModeReport:
if !getopt.IsSet(reportOnlyFlagName) {
modeLogStrs = append(modeLogStrs, runMode.String()+" setting --"+reportOnlyFlagName+"="+"true")
*reportOnlyPtr = true
}
if !getopt.IsSet(ignoreUpdateFlagName) {
modeLogStrs = append(modeLogStrs, runMode.String()+" setting --"+ignoreUpdateFlagName+"="+"true")
*ignoreUpdateFlagPtr = true
}
if !getopt.IsSet(silentFlagName) {
modeLogStrs = append(modeLogStrs, runMode.String()+" setting --"+silentFlagName+"="+"true")
*silentPtr = true
}
}
}
switch *goDirectPtr {
case "false", "true", "old":
if !getopt.IsSet(goDirectFlagName) {
modeLogStrs = append(modeLogStrs, goDirectFlagName+" not set using default 'old'")
}
default:
modeLogStrs = append(modeLogStrs, *goDirectPtr+" is not a valid go-direct option setting default 'old'")
*goDirectPtr = "old"
}
if *serviceActionPtr == "" {
*serviceActionPtr = defaultServiceAction.String()
}
if *filesPtr == "" {
*filesPtr = defaultFiles.String()
}
if !getopt.IsSet(useStrategiesFlagName) {
*useStrategiesPtr = defaultUseStrategies.String()
}
logLocationError := log.LogLocationStderr
logLocationWarn := log.LogLocationNull
logLocationInfo := log.LogLocationNull
logLocationDebug := log.LogLocationNull
if *silentPtr {
logLocationError = log.LogLocationNull
} else {
if *verbosePtr >= 1 {
logLocationWarn = log.LogLocationStderr
}
if *verbosePtr >= 2 {
logLocationInfo = log.LogLocationStderr
logLocationDebug = log.LogLocationStderr // t3c only has 3 verbosity options: none (-s), error (default or --verbose=0), warning (-v), and info (-vv). Any code calling log.Debug is treated as Info.
}
}
if *verbosePtr > 2 {
fatalLogStrs = append(fatalLogStrs, "Too many verbose options. The maximum log verbosity level is 2 (-vv or --verbose=2) for errors (0), warnings (1), and info (2)")
}
var cacheHostName string
if len(*cacheHostNamePtr) > 0 {
cacheHostName = *cacheHostNamePtr
} else {
cacheHostName, err = os.Hostname()
if err != nil {
fatalLogStrs = append(fatalLogStrs, "Could not get the hostname from the O.S., please supply a hostname: "+err.Error())
}
// strings.Split always returns a slice with at least 1 element, so we don't need a len check
cacheHostName = strings.Split(cacheHostName, ".")[0]
}
useGit := StrToUseGitFlag(*useGitStr)
if useGit == UseGitInvalid {
fatalLogStrs = append(fatalLogStrs, "Invalid git flag '"+*useGitStr+"'. Valid options are yes, no, auto.")
}
retries := *retriesPtr
reverseProxyDisable := *reverseProxyDisablePtr
skipOsCheck := *skipOSCheckPtr
useStrategies := t3cutil.UseStrategiesFlag(*useStrategiesPtr)
toInsecure := *toInsecurePtr
toTimeoutMS := time.Millisecond * time.Duration(*toTimeoutMSPtr)
toURL := *toURLPtr
toUser := *toUserPtr
toPass := *toPassPtr
dnsLocalBind := *dnsLocalBindPtr
maxmindLocation := *maxmindLocationPtr
if *version {
cfg := &Cfg{Version: appVersion, GitRevision: gitRevision}
fmt.Println(cfg.AppVersion())
os.Exit(0)
} else if *help {
Usage()
return Cfg{}, nil
}
urlSourceStr := "argument" // for error messages
if toURL == "" {
urlSourceStr = "environment variable"
toURL = os.Getenv("TO_URL")
} else {
os.Setenv("TO_URL", toURL)
}
if toUser == "" {
toUser = os.Getenv("TO_USER")
} else {
os.Setenv("TO_USER", toUser)
}
if *toPassPtr == "" {
toPass = os.Getenv("TO_PASS")
} else {
os.Setenv("TO_PASS", toPass)
}
rpmDBisOk := verifyRpmDB()
if *installPackagesPtr && !rpmDBisOk {
if t3cutil.StrToMode(*runModePtr) == t3cutil.ModeBadAss {
fatalLogStrs = append(fatalLogStrs, "RPM database check failed unable to install packages cannot continue in badass mode")
} else {
fatalLogStrs = append(fatalLogStrs, "RPM database check failed unable to install packages cannot continue")
}
}
toInfoLog = append(toInfoLog, fmt.Sprintf("rpm database is ok: %t", rpmDBisOk))
// set TSHome
var tsHome = ""
if *tsHomePtr != "" {
tsHome = *tsHomePtr
toInfoLog = append(toInfoLog, fmt.Sprintf("set TSHome from command line: '%s'", TSHome))
}
if *tsHomePtr == "" { // evironment or rpm check.
tsHome = os.Getenv("TS_HOME") // check for the environment variable.
if tsHome != "" {
toInfoLog = append(toInfoLog, fmt.Sprintf("set TSHome from TS_HOME environment variable '%s'\n", TSHome))
} else if rpmDBisOk { // check using the config file listing from the rpm package if rpmdb is ok.
tsHome = GetTSPackageHome()
if tsHome != "" {
toInfoLog = append(toInfoLog, fmt.Sprintf("set TSHome from the RPM config file list '%s'\n", TSHome))
}
} else if tsHome == "" {
toInfoLog = append(toInfoLog, fmt.Sprintf("no override for TSHome was found, using the configured default: '%s'\n", TSHome))
}
}
tsConfigDir := DefaultTSConfigDir
if tsHome != "" {
TSHome = tsHome
tsConfigDir = tsHome + "/etc/trafficserver"
if cache != nil && *cache == "varnish" {
tsConfigDir = tsHome + "/etc/varnish"
}
toInfoLog = append(toInfoLog, fmt.Sprintf("TSHome: %s, TSConfigDir: %s\n", TSHome, tsConfigDir))
}
atsVersionStr := ""
if *useLocalATSVersionPtr {
atsVersionStr, err = GetATSVersionStr(tsHome)
if err != nil {
fatalLogStrs = append(fatalLogStrs, "getting local ATS version: "+err.Error())
}
}
toInfoLog = append(toInfoLog, fmt.Sprintf("ATSVersionStr: '%s'\n", atsVersionStr))
usageStr := "basic usage: t3c-apply --traffic-ops-url=myurl --traffic-ops-user=myuser --traffic-ops-password=mypass --cache-host-name=my-cache"
if strings.TrimSpace(toURL) == "" {
fatalLogStrs = append(fatalLogStrs, "Missing required argument --traffic-ops-url or TO_URL environment variable. "+usageStr)
}
if strings.TrimSpace(toUser) == "" {
fatalLogStrs = append(fatalLogStrs, "Missing required argument --traffic-ops-user or TO_USER environment variable. "+usageStr)
}
if strings.TrimSpace(toPass) == "" {
fatalLogStrs = append(fatalLogStrs, "Missing required argument --traffic-ops-password or TO_PASS environment variable. "+usageStr)
}
if strings.TrimSpace(cacheHostName) == "" {
fatalLogStrs = append(fatalLogStrs, "Missing required argument --cache-host-name. "+usageStr)
}
toURLParsed, err := url.Parse(toURL)
if err != nil {
return Cfg{}, errors.New("parsing Traffic Ops URL from " + urlSourceStr + " '" + toURL + "': " + err.Error())
} else if err = validateURL(toURLParsed); err != nil {
return Cfg{}, errors.New("invalid Traffic Ops URL from " + urlSourceStr + " '" + toURL + "': " + err.Error())
}
svcManagement := getOSSvcManagement()
yumOptions := os.Getenv("YUM_OPTIONS")
cfg := Cfg{
LogLocationDebug: logLocationDebug,
LogLocationErr: logLocationError,
LogLocationInfo: logLocationInfo,
LogLocationWarn: logLocationWarn,
CacheHostName: cacheHostName,
SvcManagement: svcManagement,
Retries: retries,
RpmDBOk: rpmDBisOk,
ReverseProxyDisable: reverseProxyDisable,
SkipOSCheck: skipOsCheck,
UseStrategies: useStrategies,
TOInsecure: toInsecure,
TOTimeoutMS: toTimeoutMS,
TOUser: toUser,
TOPass: toPass,
TOURL: toURL,
DNSLocalBind: dnsLocalBind,
WaitForParents: *waitForParentsPtr,
YumOptions: yumOptions,
UseGit: useGit,
NoCache: *noCachePtr,
SyncDSUpdatesIPAllow: *syncdsUpdatesIPAllowPtr,
UpdateIPAllow: *updateIPAllowPtr,
OmitViaStringRelease: *omitViaStringReleasePtr,
NoOutgoingIP: *noOutgoingIP,
DisableParentConfigComments: *disableParentConfigCommentsPtr,
DefaultClientEnableH2: defaultEnableH2,
DefaultClientTLSVersions: defaultClientTLSVersions,
MaxMindLocation: maxmindLocation,
TsHome: TSHome,
TsConfigDir: tsConfigDir,
GoDirect: *goDirectPtr,
ServiceAction: t3cutil.ApplyServiceActionFlag(*serviceActionPtr),
NoConfirmServiceAction: *noConfirmServiceAction,
ReportOnly: *reportOnlyPtr,
Files: t3cutil.ApplyFilesFlag(*filesPtr),
InstallPackages: *installPackagesPtr,
IgnoreUpdateFlag: *ignoreUpdateFlagPtr,
NoUnsetUpdateFlag: *noUnsetUpdateFlagPtr,
Version: appVersion,
GitRevision: gitRevision,
LocalATSVersion: atsVersionStr,
CacheType: *cache,
}
if err = log.InitCfg(cfg); err != nil {
return Cfg{}, errors.New("Initializing loggers: " + err.Error() + "\n")
}
if len(fatalLogStrs) > 0 {
for _, str := range fatalLogStrs {
str = strings.TrimSpace(str)
log.Errorln(str)
}
return Cfg{}, errors.New("fatal error has occurred")
}
for _, str := range modeLogStrs {
str = strings.TrimSpace(str)
if str == "" {
continue
}
log.Infoln(str)
}
for _, msg := range toInfoLog {
msg = strings.TrimSpace(msg)
if msg == "" {
continue
}
log.Infoln(msg)
}
printConfig(cfg)
return cfg, nil
}
func validateURL(u *url.URL) error {
if u == nil {
return errors.New("nil url")
}
if u.Scheme != "http" && u.Scheme != "https" {
return errors.New("scheme expected 'http' or 'https', actual '" + u.Scheme + "'")
}
if strings.TrimSpace(u.Host) == "" {
return errors.New("no host")
}
return nil
}
func isCommandAvailable(name string) bool {
command := name + " --version"
cmd := exec.Command("/bin/sh", "-c", command)
if err := cmd.Run(); err != nil {
return false
}
return true
}
func getOSSvcManagement() SvcManagement {
var _svcManager SvcManagement
if isCommandAvailable(SystemCtl) {
_svcManager = SystemD
} else if isCommandAvailable(Service) {
_svcManager = SystemV
}
if !isCommandAvailable(Chkconfig) {
return Unknown
}
// we have what we need
return _svcManager
}
func GetATSVersionStr(tsHome string) (string, error) {
tsPath := tsHome
tsPath = filepath.Join(tsPath, "bin")
tsPath = filepath.Join(tsPath, "traffic_server")
stdOut, stdErr, code := t3cutil.Do(`sh`, `-c`, `set -o pipefail && `+tsPath+` --version | head -1 | awk '{print $3}'`)
if code != 0 {
return "", fmt.Errorf("traffic_server --version returned code %v stderr '%v' stdout '%v'", code, string(stdErr), string(stdOut))
}
atsVersion := strings.TrimSpace(string(stdOut))
if atsVersion == "" {
return "", fmt.Errorf("traffic_server --version returned nothing, code %v stderr '%v' stdout '%v'", code, string(stdErr), string(stdOut))
}
return atsVersion, nil
}
func printConfig(cfg Cfg) {
// TODO add new flags
log.Debugf("LogLocationDebug: %s\n", cfg.LogLocationDebug)
log.Debugf("LogLocationErr: %s\n", cfg.LogLocationErr)
log.Debugf("LogLocationInfo: %s\n", cfg.LogLocationInfo)
log.Debugf("LogLocationWarn: %s\n", cfg.LogLocationWarn)
log.Debugf("CacheHostName: %s\n", cfg.CacheHostName)
log.Debugf("SvcManagement: %s\n", cfg.SvcManagement)
log.Debugf("GoDirect: %s\n", cfg.GoDirect)
log.Debugf("Retries: %d\n", cfg.Retries)
log.Debugf("ReverseProxyDisable: %t\n", cfg.ReverseProxyDisable)
log.Debugf("SkipOSCheck: %t\n", cfg.SkipOSCheck)
log.Debugf("TOInsecure: %t\n", cfg.TOInsecure)
log.Debugf("TOTimeoutMS: %d\n", cfg.TOTimeoutMS)
log.Debugf("TOUser: %s\n", cfg.TOUser)
log.Debugf("TOPass: Pass len: '%d'\n", len(cfg.TOPass))
log.Debugf("TOURL: %s\n", cfg.TOURL)
log.Debugf("TSHome: %s\n", TSHome)
log.Debugf("LocalATSVersion: %s\n", cfg.LocalATSVersion)
log.Debugf("WaitForParents: %v\n", cfg.WaitForParents)
log.Debugf("ServiceAction: %v\n", cfg.ServiceAction)
log.Debugf("NoConfirmServiceAction: %v\n", cfg.NoConfirmServiceAction)
log.Debugf("YumOptions: %s\n", cfg.YumOptions)
log.Debugf("MaxmindLocation: %s\n", cfg.MaxMindLocation)
}
func Usage() {
getopt.PrintUsage(os.Stdout)
}