You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by ro...@apache.org on 2021/03/23 22:08:57 UTC
[trafficcontrol] branch master updated: * Adds the Traffic Ops ORT
updater, to_updater (#5614)
This is an automated email from the ASF dual-hosted git repository.
rob pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
The following commit(s) were added to refs/heads/master by this push:
new fa014af * Adds the Traffic Ops ORT updater, to_updater (#5614)
fa014af is described below
commit fa014afccea03196184a13fb2a13144cc3b20a05
Author: John J. Rushford <jr...@apache.org>
AuthorDate: Tue Mar 23 16:08:40 2021 -0600
* Adds the Traffic Ops ORT updater, to_updater (#5614)
* Adds and ORT integration tests for the to_updater
and to_requester
* Updates the pborman/getopt vendored package with
required changes for the to_updater
* Updates the traffic_ops_ort RPM spec file to include
the to_requester and to_updater
---
go.mod | 5 +-
go.sum | 12 +-
traffic_ops/toclientlib/toclientlib.go | 1 -
traffic_ops_ort/build/build_rpm.sh | 6 +
traffic_ops_ort/build/traffic_ops_ort.spec | 21 ++
traffic_ops_ort/t3clib/getdata.go | 243 +++++++++++++++++++++
traffic_ops_ort/t3clib/utils.go | 61 ++++++
traffic_ops_ort/testing/ort-tests/t3c_mode_test.go | 9 +-
traffic_ops_ort/testing/ort-tests/tc-fixtures.json | 2 +-
.../testing/ort-tests/to_requester_test.go | 145 ++++++++++++
.../testing/ort-tests/to_updater_test.go | 130 +++++++++++
traffic_ops_ort/to_requester/config/config.go | 49 ++---
traffic_ops_ort/to_requester/to_requester.go | 37 +---
traffic_ops_ort/to_updater/README.md | 66 ++++++
.../{to_requester => to_updater}/config/config.go | 73 ++-----
traffic_ops_ort/to_updater/to_updater.go | 71 ++++++
vendor/github.com/pborman/getopt/v2/getopt.go | 74 ++++++-
vendor/github.com/pborman/getopt/v2/option.go | 48 ++--
vendor/github.com/pborman/getopt/v2/set.go | 16 ++
vendor/modules.txt | 6 +-
20 files changed, 922 insertions(+), 153 deletions(-)
diff --git a/go.mod b/go.mod
index df926f2..a9905a9 100644
--- a/go.mod
+++ b/go.mod
@@ -22,7 +22,6 @@ go 1.15
replace (
github.com/fsnotify/fsnotify v1.4.9 => github.com/fsnotify/fsnotify v1.3.0
github.com/golang/protobuf v1.4.2 => github.com/golang/protobuf v0.0.0-20171021043952-1643683e1b54
- github.com/pborman/getopt/v2 v2.1.0 => github.com/pborman/getopt/v2 v2.0.0-20200816005738-fd0d075bf4de
gopkg.in/yaml.v2 v2.3.0 => gopkg.in/yaml.v2 v2.2.1
)
@@ -63,8 +62,8 @@ require (
github.com/stretchr/testify v1.6.1 // indirect
go.etcd.io/bbolt v1.3.5
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
- golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
- golang.org/x/sys v0.0.0-20210227040730-b0d1d43c014d
+ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4
+ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005
golang.org/x/text v0.3.5 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0
diff --git a/go.sum b/go.sum
index 6227238..80eb155 100644
--- a/go.sum
+++ b/go.sum
@@ -73,8 +73,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
-github.com/pborman/getopt/v2 v2.0.0-20200816005738-fd0d075bf4de h1:i3rei6KlOqwCd9rxoYKfXEzf9LRN9dQMarOfAvH5Fos=
-github.com/pborman/getopt/v2 v2.0.0-20200816005738-fd0d075bf4de/go.mod h1:4NtW75ny4eBw9fO1bhtNdYTlZKYX5/tBLtsOpwKIKd0=
+github.com/pborman/getopt/v2 v2.1.0 h1:eNfR+r+dWLdWmV8g5OlpyrTYHkhVNxHBdN2cCrJmOEA=
+github.com/pborman/getopt/v2 v2.1.0/go.mod h1:4NtW75ny4eBw9fO1bhtNdYTlZKYX5/tBLtsOpwKIKd0=
github.com/pkg/errors v0.8.2-0.20190227000051-27936f6d90f9 h1:PCj9X21C4pet4sEcElTfAi6LSl5ShkjE8doieLc+cbU=
github.com/pkg/errors v0.8.2-0.20190227000051-27936f6d90f9/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -92,8 +92,8 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 h1:b0LrWgu8+q7z4J+0Y3Umo5q1dL7NXBkKBWkaVkAq17E=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -107,8 +107,8 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210227040730-b0d1d43c014d h1:9fH9JvLNoSpsDWcXJ4dSE3lZW99Z3OCUZLr07g60U6o=
-golang.org/x/sys v0.0.0-20210227040730-b0d1d43c014d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 h1:pDMpM2zh2MT0kHy037cKlSby2nEhD50SYqwQk76Nm40=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/traffic_ops/toclientlib/toclientlib.go b/traffic_ops/toclientlib/toclientlib.go
index 83f0820..042be03 100644
--- a/traffic_ops/toclientlib/toclientlib.go
+++ b/traffic_ops/toclientlib/toclientlib.go
@@ -34,7 +34,6 @@ import (
"github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
-
"golang.org/x/net/publicsuffix"
)
diff --git a/traffic_ops_ort/build/build_rpm.sh b/traffic_ops_ort/build/build_rpm.sh
index b493d31..9720f21 100755
--- a/traffic_ops_ort/build/build_rpm.sh
+++ b/traffic_ops_ort/build/build_rpm.sh
@@ -67,6 +67,12 @@ initBuildArea() {
(cd t3c;
go build -v -gcflags "$gcflags" -ldflags "${ldflags} -X main.GitRevision=$(git rev-parse HEAD) -X main.BuildTimestamp=$(date +'%Y-%M-%dT%H:%M:%s') -X main.Version=${TC_VERSION}" -tags "$tags")
+ (cd to_requester;
+ go build -v -gcflags "$gcflags" -ldflags "${ldflags} -X main.GitRevision=$(git rev-parse HEAD) -X main.BuildTimestamp=$(date +'%Y-%M-%dT%H:%M:%s') -X main.Version=${TC_VERSION}" -tags "$tags")
+
+ (cd to_updater;
+ go build -v -gcflags "$gcflags" -ldflags "${ldflags} -X main.GitRevision=$(git rev-parse HEAD) -X main.BuildTimestamp=$(date +'%Y-%M-%dT%H:%M:%s') -X main.Version=${TC_VERSION}" -tags "$tags")
+
cp -p traffic_ops_ort.pl "$dest";
cp -p supermicro_udev_mapper.pl "$dest";
mkdir -p "${dest}/build";
diff --git a/traffic_ops_ort/build/traffic_ops_ort.spec b/traffic_ops_ort/build/traffic_ops_ort.spec
index 55202c3..195313a 100644
--- a/traffic_ops_ort/build/traffic_ops_ort.spec
+++ b/traffic_ops_ort/build/traffic_ops_ort.spec
@@ -57,6 +57,19 @@ got3cdir=src/github.com/apache/trafficcontrol/traffic_ops_ort/t3c
cp "$TC_DIR"/traffic_ops_ort/t3c/t3c .
) || { echo "Could not copy go program at $(pwd): $!"; exit 1; }
+# copy to_requester binary
+go_toreq_dir=src/github.com/apache/trafficcontrol/traffic_ops_ort/to_requester
+( mkdir -p "$go_toreq_dir" && \
+ cd "$go_toreq_dir" && \
+ cp "$TC_DIR"/traffic_ops_ort/to_requester/to_requester .
+) || { echo "Could not copy go program at $(pwd): $!"; exit 1; }
+
+# copy to_updater binary
+go_toupd_dir=src/github.com/apache/trafficcontrol/traffic_ops_ort/to_updater
+( mkdir -p "$go_toupd_dir" && \
+ cd "$go_toupd_dir" && \
+ cp "$TC_DIR"/traffic_ops_ort/to_updater/to_updater .
+) || { echo "Could not copy go program at $(pwd): $!"; exit 1; }
%install
mkdir -p ${RPM_BUILD_ROOT}/opt/ort
@@ -74,6 +87,12 @@ cp -p "$src"/atstccfg/atstccfg ${RPM_BUILD_ROOT}/opt/ort
t3csrc=src/github.com/apache/trafficcontrol/traffic_ops_ort/t3c
cp -p "$t3csrc"/t3c ${RPM_BUILD_ROOT}/opt/ort
+to_req_src=src/github.com/apache/trafficcontrol/traffic_ops_ort/to_requester
+cp -p "$to_req_src"/to_requester ${RPM_BUILD_ROOT}/opt/ort
+
+to_upd_src=src/github.com/apache/trafficcontrol/traffic_ops_ort/to_updater
+cp -p "$to_upd_src"/to_updater ${RPM_BUILD_ROOT}/opt/ort
+
%clean
rm -rf ${RPM_BUILD_ROOT}
@@ -86,6 +105,8 @@ rm -rf ${RPM_BUILD_ROOT}
/opt/ort/supermicro_udev_mapper.pl
/opt/ort/atstccfg
/opt/ort/t3c
+/opt/ort/to_requester
+/opt/ort/to_updater
%config(noreplace) /etc/logrotate.d/atstccfg
%config(noreplace) /var/log/ort/atstccfg.log
diff --git a/traffic_ops_ort/t3clib/getdata.go b/traffic_ops_ort/t3clib/getdata.go
new file mode 100644
index 0000000..e38542d
--- /dev/null
+++ b/traffic_ops_ort/t3clib/getdata.go
@@ -0,0 +1,243 @@
+/*
+ * 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.
+ */
+
+// package getdata gets and posts non-config data from Traffic Ops which is related to config generation and needed by ORT.
+// For example, the --get-data, --set-queue-status, and --set-reval-status arguments.
+package t3clib
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "os"
+ "time"
+
+ "github.com/apache/trafficcontrol/lib/go-atscfg"
+ "github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/traffic_ops_ort/atstccfg/toreq"
+ "github.com/apache/trafficcontrol/traffic_ops_ort/atstccfg/torequtil"
+)
+
+type TCCfg struct {
+ CacheHostName string
+ GetData string
+ TOClient *toreq.TOClient
+ TOInsecure bool
+ TOTimeoutMS time.Duration
+ TOPass string
+ TOUser string
+ TOURL *url.URL
+ UserAgent string
+}
+
+func GetDataFuncs() map[string]func(TCCfg, io.Writer) error {
+ return map[string]func(TCCfg, io.Writer) error{
+ `update-status`: WriteServerUpdateStatus,
+ `packages`: WritePackages,
+ `chkconfig`: WriteChkconfig,
+ `system-info`: WriteSystemInfo,
+ `statuses`: WriteStatuses,
+ }
+}
+
+func GetServerUpdateStatus(cfg TCCfg) (*tc.ServerUpdateStatus, error) {
+ status, _, err := cfg.TOClient.GetServerUpdateStatus(tc.CacheName(cfg.CacheHostName))
+ if err != nil {
+ return nil, errors.New("getting server update status: " + err.Error())
+ }
+ return &status, nil
+}
+
+func WriteData(cfg TCCfg) error {
+ log.Infoln("Getting data '" + cfg.GetData + "'")
+ dataF, ok := GetDataFuncs()[cfg.GetData]
+ if !ok {
+ return errors.New("unknown data request '" + cfg.GetData + "'")
+ }
+ return dataF(cfg, os.Stdout)
+}
+
+const SystemInfoParamConfigFile = `global`
+
+// WriteSystemInfo writes the "system info" to output.
+//
+// This is the same info at /api/1.x/system/info, which is actually just all Parameters with the config_file 'global'.
+// Note this is different than the more common "global parameters", which usually refers to all Parameters on the Profile named 'GLOBAL'.
+//
+// This is identical to the /api/1.x/system/info endpoint, except it does not include a '{response: {parameters:' wrapper.
+//
+func WriteSystemInfo(cfg TCCfg, output io.Writer) error {
+ paramArr, _, err := cfg.TOClient.GetConfigFileParameters(SystemInfoParamConfigFile)
+ if err != nil {
+ return errors.New("getting system info parameters: " + err.Error())
+ }
+ params := map[string]string{}
+ for _, param := range paramArr {
+ params[param.Name] = param.Value
+ }
+ if err := json.NewEncoder(output).Encode(params); err != nil {
+ return errors.New("encoding system info parameters: " + err.Error())
+ }
+ return nil
+}
+
+// WriteStatuses writes the Traffic Ops statuses to output.
+// Note this is identical to /api/1.x/statuses except it omits the '{response:' wrapper.
+func WriteStatuses(cfg TCCfg, output io.Writer) error {
+ statuses, _, err := cfg.TOClient.GetStatuses()
+ if err != nil {
+ return errors.New("getting statuses: " + err.Error())
+ }
+ if err := json.NewEncoder(output).Encode(statuses); err != nil {
+ return errors.New("encoding statuses: " + err.Error())
+ }
+ return nil
+}
+
+// WriteUpdateStatus writes the Traffic Ops server update status to output.
+// Note this is identical to /api/1.x/servers/name/update_status except it omits the '[]' wrapper.
+func WriteServerUpdateStatus(cfg TCCfg, output io.Writer) error {
+ status, err := GetServerUpdateStatus(cfg)
+ if err != nil {
+ return err
+ }
+ if err := json.NewEncoder(output).Encode(status); err != nil {
+ return errors.New("encoding server update status: " + err.Error())
+ }
+ return nil
+}
+
+// WriteORTServerPackages writes the packages for serverName to output.
+// Note this is identical to /ort/serverName/packages.
+func WritePackages(cfg TCCfg, output io.Writer) error {
+ packages, err := GetPackages(cfg)
+ if err != nil {
+ return errors.New("getting ORT server packages: " + err.Error())
+ }
+ if err := json.NewEncoder(output).Encode(packages); err != nil {
+ return errors.New("writing packages: " + err.Error())
+ }
+ return nil
+}
+
+func GetPackages(cfg TCCfg) ([]Package, error) {
+ server, _, err := cfg.TOClient.GetServerByHostName(string(cfg.CacheHostName))
+ if err != nil {
+ return nil, errors.New("getting server: " + err.Error())
+ } else if server.Profile == nil {
+ return nil, errors.New("getting server: nil profile")
+ }
+ params, _, err := cfg.TOClient.GetServerProfileParameters(*server.Profile)
+ if err != nil {
+ return nil, errors.New("getting server profile '" + *server.Profile + "' parameters: " + err.Error())
+ }
+ packages := []Package{}
+ for _, param := range params {
+ if param.ConfigFile != atscfg.PackagesParamConfigFile {
+ continue
+ }
+ packages = append(packages, Package{Name: param.Name, Version: param.Value})
+ }
+ return packages, nil
+}
+
+type Package struct {
+ Name string `json:"name"`
+ Version string `json:"version"`
+}
+
+// WriteChkconfig writes the chkconfig for cfg.CacheHostName to output.
+// Note this is identical to /ort/serverName/chkconfig.
+func WriteChkconfig(cfg TCCfg, output io.Writer) error {
+ chkconfig, err := GetChkconfig(cfg)
+ if err != nil {
+ return errors.New("getting chkconfig: " + err.Error())
+ }
+ if err := json.NewEncoder(output).Encode(chkconfig); err != nil {
+ return errors.New("writing chkconfig: " + err.Error())
+ }
+ return nil
+}
+
+func GetChkconfig(cfg TCCfg) ([]ChkConfigEntry, error) {
+ server, _, err := cfg.TOClient.GetServerByHostName(string(cfg.CacheHostName))
+ if err != nil {
+ return nil, errors.New("getting server: " + err.Error())
+ } else if server.Profile == nil {
+ return nil, errors.New("getting server: nil profile")
+ }
+ params, _, err := cfg.TOClient.GetServerProfileParameters(*server.Profile)
+ if err != nil {
+ return nil, errors.New("getting server profile '" + *server.Profile + "' parameters: " + err.Error())
+ }
+ chkconfig := []ChkConfigEntry{}
+ for _, param := range params {
+ if param.ConfigFile != atscfg.ChkconfigParamConfigFile {
+ continue
+ }
+ chkconfig = append(chkconfig, ChkConfigEntry{Name: param.Name, Val: param.Value})
+ }
+ return chkconfig, nil
+}
+
+type ChkConfigEntry struct {
+ Name string `json:"name"`
+ Val string `json:"value"`
+}
+
+// SetUpdateStatus sets the queue and reval status of serverName in Traffic Ops.
+func SetUpdateStatus(cfg TCCfg, serverName string, queue bool, revalPending bool) error {
+ reqInf, err := cfg.TOClient.C.SetUpdateServerStatuses(string(serverName), &queue, &revalPending)
+ if err != nil {
+ return errors.New("setting update statuses (Traffic Ops '" + torequtil.MaybeIPStr(reqInf.RemoteAddr) + "'): " + err.Error())
+ }
+ return nil
+}
+
+// setUpdateStatusLegacy sets the queue and reval status of serverName in Traffic Ops,
+// using the legacy pre-2.0 /update endpoint.
+func setUpdateStatusLegacy(cfg TCCfg, serverName tc.CacheName, queue bool, revalPending bool) error {
+ path := `/update/` + string(serverName) + `?updated=` + jsonBoolStr(queue) + `&reval_updated=` + jsonBoolStr(revalPending)
+ // C and RawRequest should generally never be used, but the alternatve here is to manually get the cookie and do an http.Get. We need to hit a non-API endpoint, no API endpoint exists for what we need.
+ resp, _, err := cfg.TOClient.C.RawRequest(http.MethodPost, path, nil)
+ if err != nil {
+ return errors.New("setting update statuses: " + err.Error())
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != 200 {
+ bodyBts, err := ioutil.ReadAll(resp.Body)
+ if err == nil {
+ return fmt.Errorf("Traffic Ops returned %v %v", resp.StatusCode, string(bodyBts))
+ }
+ return fmt.Errorf("Traffic Ops returned %v (error reading body)", resp.StatusCode)
+ }
+ return nil
+}
+
+func jsonBoolStr(b bool) string {
+ if b {
+ return `true`
+ }
+ return `false`
+}
diff --git a/traffic_ops_ort/t3clib/utils.go b/traffic_ops_ort/t3clib/utils.go
new file mode 100644
index 0000000..33210c2
--- /dev/null
+++ b/traffic_ops_ort/t3clib/utils.go
@@ -0,0 +1,61 @@
+package t3clib
+
+/*
+ * 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.
+ */
+
+// Utility functions.
+
+import (
+ "errors"
+ "net/url"
+ "strings"
+
+ "github.com/apache/trafficcontrol/traffic_ops_ort/atstccfg/toreq"
+)
+
+func TOConnect(tccfg *TCCfg) (*TCCfg, error) {
+ _toClient, err := toreq.New(
+ tccfg.TOURL,
+ tccfg.TOUser,
+ tccfg.TOPass,
+ tccfg.TOInsecure,
+ tccfg.TOTimeoutMS,
+ tccfg.UserAgent)
+
+ if err != nil {
+ return nil, errors.New("failed to connect to traffic ops: " + err.Error())
+ }
+
+ tccfg.TOClient = _toClient
+
+ return tccfg, 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
+}
diff --git a/traffic_ops_ort/testing/ort-tests/t3c_mode_test.go b/traffic_ops_ort/testing/ort-tests/t3c_mode_test.go
index 3e2050f..f20f9fe 100644
--- a/traffic_ops_ort/testing/ort-tests/t3c_mode_test.go
+++ b/traffic_ops_ort/testing/ort-tests/t3c_mode_test.go
@@ -17,6 +17,7 @@ package orttest
import (
"bytes"
"errors"
+ "fmt"
"github.com/apache/trafficcontrol/traffic_ops_ort/testing/ort-tests/tcdata"
"github.com/apache/trafficcontrol/traffic_ops_ort/testing/ort-tests/util"
"os"
@@ -42,7 +43,7 @@ var (
)
func TestT3cBadassAndSyncDs(t *testing.T) {
- t.Logf("------------- Starting TestT3cBadassAndSyncDs ---------------")
+ fmt.Println("------------- Starting TestT3cBadassAndSyncDs ---------------")
tcd.WithObjs(t, []tcdata.TCObj{
tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
@@ -77,7 +78,7 @@ func TestT3cBadassAndSyncDs(t *testing.T) {
time.Sleep(time.Second * 5)
- t.Logf("------------------------ running SYNCDS Test ------------------")
+ fmt.Println("------------------------ running SYNCDS Test ------------------")
// remove the remap.config in preparation for running syncds
remap := test_config_dir + "/remap.config"
err = os.Remove(remap)
@@ -100,10 +101,10 @@ func TestT3cBadassAndSyncDs(t *testing.T) {
if !util.FileExists(remap) {
t.Fatalf("ERROR: syncds failed to pull down %s\n", remap)
}
- t.Logf("------------------------ end SYNCDS Test ------------------")
+ fmt.Println("------------------------ end SYNCDS Test ------------------")
})
- t.Logf("------------- End of TestT3cBadassAndSyncDs ---------------")
+ fmt.Println("------------- End of TestT3cBadassAndSyncDs ---------------")
}
func setQueueUpdateStatus(host_name string, update string) error {
diff --git a/traffic_ops_ort/testing/ort-tests/tc-fixtures.json b/traffic_ops_ort/testing/ort-tests/tc-fixtures.json
index b9bc068..2b13899 100644
--- a/traffic_ops_ort/testing/ort-tests/tc-fixtures.json
+++ b/traffic_ops_ort/testing/ort-tests/tc-fixtures.json
@@ -1337,7 +1337,7 @@
"lastUpdated": "2020-04-21T05:19:43.853831+00:00",
"name": "tm.instance_name",
"secure": false,
- "value": "Traffic Ops API Tests"
+ "value": "Traffic Ops ORT Tests"
}
],
"physLocations": [
diff --git a/traffic_ops_ort/testing/ort-tests/to_requester_test.go b/traffic_ops_ort/testing/ort-tests/to_requester_test.go
new file mode 100644
index 0000000..0b3e8cf
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/to_requester_test.go
@@ -0,0 +1,145 @@
+package orttest
+
+/*
+ Licensed 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"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/traffic_ops_ort/testing/ort-tests/tcdata"
+ "os/exec"
+ "strings"
+ "testing"
+)
+
+type Package struct {
+ Name *string `json:"name"`
+ Version *string `json:"version"`
+}
+
+func TestTORequester(t *testing.T) {
+ fmt.Println("------------- Starting TestTORequester tests ---------------")
+ tcd.WithObjs(t, []tcdata.TCObj{
+ tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
+ tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+ tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
+ tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
+ tcdata.DeliveryServices}, func() {
+
+ // chkconfig test
+ output, err := ExecTORequester("atlanta-edge-03", "chkconfig")
+ if err != nil {
+ t.Fatalf("ERROR: to_requester exec failed: %v\n", err)
+ }
+ var chkConfig []map[string]interface{}
+ err = json.Unmarshal([]byte(output), &chkConfig)
+ if err != nil {
+ t.Fatalf("ERROR unmarshalling json output: " + err.Error())
+ }
+ if chkConfig[0]["name"] != "trafficserver" {
+ t.Fatal("ERROR unexpected result, expected 'trafficserver'")
+ }
+
+ // get system-info test
+ output, err = ExecTORequester("atlanta-edge-03", "system-info")
+ if err != nil {
+ t.Fatalf("ERROR: to_requester exec failed: %v\n", err)
+ }
+ var sysInfo map[string]interface{}
+ err = json.Unmarshal([]byte(output), &sysInfo)
+ if err != nil {
+ t.Fatalf("ERROR unmarshalling json output: " + err.Error())
+ }
+ if sysInfo["tm.instance_name"] != "Traffic Ops ORT Tests" {
+ t.Fatalf("ERROR: unexpected 'tm.instance_name'")
+ }
+
+ // statuses test
+ output, err = ExecTORequester("atlanta-edge-03", "statuses")
+ if err != nil {
+ t.Fatalf("ERROR: to_requester exec failed: %v\n", err)
+ }
+ // should parse json to an array of 'tc.Status'
+ var statuses []tc.Status
+ err = json.Unmarshal([]byte(output), &statuses)
+ if err != nil {
+ t.Fatalf("ERROR unmarshalling json output: " + err.Error())
+ }
+
+ // packages test
+ output, err = ExecTORequester("atlanta-edge-03", "packages")
+ if err != nil {
+ t.Fatalf("ERROR: to_requester exec failed: %v\n", err)
+ }
+ // should parse to an array of 'Package'
+ var packages []Package
+ err = json.Unmarshal([]byte(output), &packages)
+ if err != nil {
+ t.Fatalf("ERROR unmarshalling json output: " + err.Error())
+ }
+ if *packages[0].Name != "trafficserver" {
+ t.Fatal("ERROR unexpected result, expected 'trafficserver'")
+ }
+
+ // update-status test
+ output, err = ExecTORequester("atlanta-edge-03", "update-status")
+ if err != nil {
+ t.Fatalf("ERROR: to_requester exec failed: %v\n", err)
+ }
+ var serverStatus tc.ServerUpdateStatus
+ err = json.Unmarshal([]byte(output), &serverStatus)
+ if err != nil {
+ t.Fatalf("ERROR unmarshalling json output: " + err.Error())
+ }
+ if serverStatus.HostName != "atlanta-edge-03" {
+ t.Fatal("ERROR unexpected result, expected 'atlanta-edge-03'")
+ }
+
+ })
+ fmt.Println("------------- End of TestTORequester tests ---------------")
+}
+
+func ExecTORequester(host string, data_req string) (string, error) {
+ args := []string{
+ "--traffic-ops-insecure=true",
+ "--login-dispersion=0",
+ "--traffic-ops-timeout-milliseconds=3000",
+ "--traffic-ops-user=" + tcd.Config.TrafficOps.Users.Admin,
+ "--traffic-ops-password=" + tcd.Config.TrafficOps.UserPassword,
+ "--traffic-ops-url=" + tcd.Config.TrafficOps.URL,
+ "--cache-host-name=" + host,
+ "--log-location-error=test.log",
+ "--log-location-info=test.log",
+ "--log-location-debug=test.log",
+ "--get-data=" + data_req,
+ }
+ cmd := exec.Command("/opt/ort/to_requester", args...)
+ var out bytes.Buffer
+ var errOut bytes.Buffer
+ cmd.Stdout = &out
+ cmd.Stderr = &errOut
+ err := cmd.Run()
+ if err != nil {
+ return "", errors.New(err.Error() + ": " + "stdout: " + out.String() + " stderr: " + errOut.String())
+ }
+
+ // capture the last line of JSON in the 'Stdout' buffer 'out'
+ output := strings.Split(strings.TrimSpace(strings.Replace(out.String(), "\r\n", "\n", -1)), "\n")
+ lastLine := output[len(output)-1]
+
+ return lastLine, nil
+}
diff --git a/traffic_ops_ort/testing/ort-tests/to_updater_test.go b/traffic_ops_ort/testing/ort-tests/to_updater_test.go
new file mode 100644
index 0000000..b288fe3
--- /dev/null
+++ b/traffic_ops_ort/testing/ort-tests/to_updater_test.go
@@ -0,0 +1,130 @@
+package orttest
+
+/*
+ Licensed 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"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/traffic_ops_ort/testing/ort-tests/tcdata"
+ "os/exec"
+ "strconv"
+ "testing"
+)
+
+func TestTOUpdater(t *testing.T) {
+ fmt.Println("------------- Starting TestTOUpdater tests ---------------")
+ tcd.WithObjs(t, []tcdata.TCObj{
+ tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
+ tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+ tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
+ tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
+ tcdata.DeliveryServices}, func() {
+
+ // retrieve the current server status
+ output, err := ExecTORequester("atlanta-edge-03", "update-status")
+ if err != nil {
+ t.Fatalf("ERROR: to_requester Exec failed: %v\n", err)
+ }
+ var serverStatus tc.ServerUpdateStatus
+ err = json.Unmarshal([]byte(output), &serverStatus)
+ if err != nil {
+ t.Fatalf("ERROR unmarshalling json output: " + err.Error())
+ }
+ if serverStatus.HostName != "atlanta-edge-03" {
+ t.Fatal("ERROR unexpected result, expected 'atlanta-edge-03'")
+ }
+ if serverStatus.RevalPending != false {
+ t.Fatal("ERROR unexpected result, expected RevalPending is 'false'")
+ }
+ if serverStatus.UpdatePending != false {
+ t.Fatal("ERROR unexpected result, expected UpdatePending is 'false'")
+ }
+
+ // change the server update status
+ err = ExecTOUpdater("atlanta-edge-03", false, true)
+ if err != nil {
+ t.Fatalf("ERROR: to_updater Exec failed: %v\n", err)
+ }
+ // verify the update status is now 'true'
+ output, err = ExecTORequester("atlanta-edge-03", "update-status")
+ if err != nil {
+ t.Fatalf("ERROR: to_requester Exec failed: %v\n", err)
+ }
+ err = json.Unmarshal([]byte(output), &serverStatus)
+ if err != nil {
+ t.Fatalf("ERROR unmarshalling json output: " + err.Error())
+ }
+ if serverStatus.RevalPending != false {
+ t.Fatal("ERROR unexpected result, expected RevalPending is 'false'")
+ }
+ if serverStatus.UpdatePending != true {
+ t.Fatal("ERROR unexpected result, expected UpdatePending is 'true'")
+ }
+
+ // now change the reval stat and put server update status back
+ err = ExecTOUpdater("atlanta-edge-03", true, false)
+ if err != nil {
+ t.Fatalf("ERROR: to_updater Exec failed: %v\n", err)
+ }
+ // verify the change
+ output, err = ExecTORequester("atlanta-edge-03", "update-status")
+ if err != nil {
+ t.Fatalf("ERROR: to_requester Exec failed: %v\n", err)
+ }
+ err = json.Unmarshal([]byte(output), &serverStatus)
+ if err != nil {
+ t.Fatalf("ERROR unmarshalling json output: " + err.Error())
+ }
+ if serverStatus.RevalPending != true {
+ t.Fatal("ERROR unexpected result, expected RevalPending is 'false'")
+ }
+ if serverStatus.UpdatePending != false {
+ t.Fatal("ERROR unexpected result, expected UpdatePending is 'true'")
+ }
+
+ })
+ fmt.Println("------------- End of TestTOUpdater tests ---------------")
+}
+
+func ExecTOUpdater(host string, reval_status bool, update_status bool) error {
+ args := []string{
+ "--traffic-ops-insecure=true",
+ "--login-dispersion=0",
+ "--traffic-ops-timeout-milliseconds=3000",
+ "--traffic-ops-user=" + tcd.Config.TrafficOps.Users.Admin,
+ "--traffic-ops-password=" + tcd.Config.TrafficOps.UserPassword,
+ "--traffic-ops-url=" + tcd.Config.TrafficOps.URL,
+ "--cache-host-name=" + host,
+ "--log-location-error=test.log",
+ "--log-location-info=test.log",
+ "--log-location-debug=test.log",
+ "--set-reval-status=" + strconv.FormatBool(reval_status),
+ "--set-update-status=" + strconv.FormatBool(update_status),
+ }
+ cmd := exec.Command("/opt/ort/to_updater", args...)
+ var out bytes.Buffer
+ var errOut bytes.Buffer
+ cmd.Stdout = &out
+ cmd.Stderr = &errOut
+ err := cmd.Run()
+ if err != nil {
+ return errors.New(err.Error() + ": " + "stdout: " + out.String() + " stderr: " + errOut.String())
+ }
+
+ return nil
+}
diff --git a/traffic_ops_ort/to_requester/config/config.go b/traffic_ops_ort/to_requester/config/config.go
index 72a4cee..ac22ee9 100644
--- a/traffic_ops_ort/to_requester/config/config.go
+++ b/traffic_ops_ort/to_requester/config/config.go
@@ -24,11 +24,10 @@ import (
"fmt"
"net/url"
"os"
- "strings"
"time"
"github.com/apache/trafficcontrol/lib/go-log"
- "github.com/apache/trafficcontrol/traffic_ops_ort/atstccfg/toreq"
+ "github.com/apache/trafficcontrol/traffic_ops_ort/t3clib"
"github.com/pborman/getopt/v2"
)
@@ -42,18 +41,7 @@ type Cfg struct {
LogLocationError string
LogLocationInfo string
LoginDispersion time.Duration
- CacheHostName string
- GetData string
- TOInsecure bool
- TOTimeoutMS time.Duration
- TOUser string
- TOPass string
- TOURL *url.URL
-}
-
-type TCCfg struct {
- Cfg
- TOClient *toreq.TOClient
+ t3clib.TCCfg
}
func (cfg Cfg) DebugLog() log.LogLocation { return log.LogLocation(cfg.LogLocationDebug) }
@@ -76,7 +64,7 @@ func InitConfig() (Cfg, error) {
logLocationInfoPtr := getopt.StringLong("log-location-info", 'i', "stderr", "Where to log infos. May be a file path, stdout, stderr")
dispersionPtr := getopt.IntLong("login-dispersion", 'l', 0, "[seconds] wait a random number of seconds between 0 and [seconds] before login to traffic ops, default 0")
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")
- getDataPtr := getopt.StringLong("get-data", 'D', "system-info", "non-config-file Traffic Ops Data to get. Valid values are all, update-status, packages, chkconfig, system-info, and statuses")
+ getDataPtr := getopt.StringLong("get-data", 'D', "system-info", "non-config-file Traffic Ops Data to get. Valid values are update-status, packages, chkconfig, system-info, and statuses")
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")
@@ -111,7 +99,7 @@ func InitConfig() (Cfg, error) {
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 {
+ } else if err := t3clib.ValidateURL(toURLParsed); err != nil {
return Cfg{}, errors.New("invalid Traffic Ops URL from " + urlSourceStr + " '" + toURL + "': " + err.Error())
}
@@ -131,13 +119,15 @@ func InitConfig() (Cfg, error) {
LogLocationError: *logLocationErrorPtr,
LogLocationInfo: *logLocationInfoPtr,
LoginDispersion: dispersion,
- CacheHostName: cacheHostName,
- GetData: *getDataPtr,
- TOInsecure: *toInsecurePtr,
- TOTimeoutMS: toTimeoutMS,
- TOUser: toUser,
- TOPass: toPass,
- TOURL: toURLParsed,
+ TCCfg: t3clib.TCCfg{
+ CacheHostName: cacheHostName,
+ GetData: *getDataPtr,
+ TOInsecure: *toInsecurePtr,
+ TOTimeoutMS: toTimeoutMS,
+ TOUser: toUser,
+ TOPass: toPass,
+ TOURL: toURLParsed,
+ },
}
if err := log.InitCfg(cfg); err != nil {
@@ -147,19 +137,6 @@ func InitConfig() (Cfg, error) {
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 (cfg Cfg) PrintConfig() {
fmt.Printf("CommandArgs: %s\n", cfg.CommandArgs)
fmt.Printf("LogLocationDebug: %s\n", cfg.LogLocationDebug)
diff --git a/traffic_ops_ort/to_requester/to_requester.go b/traffic_ops_ort/to_requester/to_requester.go
index 7be297b..f4a76f7 100644
--- a/traffic_ops_ort/to_requester/to_requester.go
+++ b/traffic_ops_ort/to_requester/to_requester.go
@@ -15,8 +15,9 @@ Description
Options
-D, --get-data=value
non-config-file Traffic Ops Data to get. Valid values are
- all, update-status, packages, chkconfig, system-info, and
- statuses [all]
+ update-status, packages, chkconfig, system-info, and
+ statuses
+ Default is system-info
-d, --log-location-debug=value
Where to log debugs. May be a file path, stdout or stderr.
Default is no debug logging.
@@ -73,14 +74,12 @@ package main
*/
import (
- "errors"
"fmt"
"os"
"github.com/apache/trafficcontrol/lib/go-log"
- "github.com/apache/trafficcontrol/traffic_ops_ort/atstccfg/toreq"
+ "github.com/apache/trafficcontrol/traffic_ops_ort/t3clib"
"github.com/apache/trafficcontrol/traffic_ops_ort/to_requester/config"
- "github.com/apache/trafficcontrol/traffic_ops_ort/to_requester/getdata"
)
var (
@@ -100,36 +99,16 @@ func main() {
}
// login to traffic ops.
- tccfg, err := toConnect()
+ tccfg, err := t3clib.TOConnect(&cfg.TCCfg)
if err != nil {
log.Errorf("%s\n", err)
os.Exit(2)
}
- if tccfg.Cfg.GetData != "" {
- if err := getdata.WriteData(*tccfg); err != nil {
+
+ if cfg.GetData != "" {
+ if err := t3clib.WriteData(*tccfg); err != nil {
log.Errorf("writing data: %s\n", err.Error())
os.Exit(3)
}
}
}
-
-/*
- * connect and login to traffic ops
- */
-func toConnect() (*config.TCCfg, error) {
- toClient, err := toreq.New(cfg.TOURL, cfg.TOUser, cfg.TOPass, cfg.TOInsecure, cfg.TOTimeoutMS, config.UserAgent)
- if err != nil {
- return nil, errors.New("failed to connect to traffic ops: " + err.Error())
- }
-
- if toClient.FellBack() {
- log.Warnln("Traffic Ops does not support the latest version supported by this app! Falling back to previous major Traffic Ops API version!")
- }
-
- tccfg := config.TCCfg{
- Cfg: cfg,
- TOClient: toClient,
- }
-
- return &tccfg, nil
-}
diff --git a/traffic_ops_ort/to_updater/README.md b/traffic_ops_ort/to_updater/README.md
new file mode 100644
index 0000000..9c7361e
--- /dev/null
+++ b/traffic_ops_ort/to_updater/README.md
@@ -0,0 +1,66 @@
+<!--
+ 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.
+-->
+
+# Traffic Ops Updater
+
+## Synopsis
+ to_updater [-h] [-a value] [-d value] [-e value] [-H value] [-i value] \
+ [-l value] [-P value] [-q value] [-t value] [-u value] [-U value]
+
+## Description
+ The to_requester is used to set the update and reval status,
+ on Traffic Ops.
+
+## Options
+ -a --set-reval-status [true | false] set the servers revalidate status (required)
+ -q --set-update-status [true | false] set the servers update queue status (required)
+
+ -d, --log-location-debug=value
+ Where to log debugs. May be a file path, stdout or stderr.
+ Default is no debug logging.
+ -e, --log-location-error=value
+ Where to log errors. May be a file path, stdout, or stderr.
+ Default is stderr.
+ -i, --log-location-info=value
+ Where to log infos. May be a file path, stdout or stderr.
+ Default is stderr.
+ -H, --cache-host-name=value
+ Host name of the cache to update the statuses of on TrafficOps.
+ Server host name in Traffic Ops, not a URL, and not the FQDN.
+ Defaults to the OS configured hostname.
+ -h, --help Print usage information and exit
+ -I, --traffic-ops-insecure
+ [true | false] ignore certificate errors from Traffic Ops
+ -l, --login-dispersion=value
+ [seconds] wait a random number of seconds between 0 and
+ [seconds] before login to traffic ops, default 0
+ -P, --traffic-ops-password=value
+ Traffic Ops password. Required. May also be set with the
+ environment variable TO_PASS
+ -t, --traffic-ops-timeout-milliseconds=value
+ Timeout in milli-seconds for Traffic Ops requests, default
+ is 30000 [30000]
+ -u, --traffic-ops-url=value
+ Traffic Ops URL. Must be the full URL, including the scheme.
+ Required. May also be set with the environment variable
+ TO_URL
+ -U, --traffic-ops-user=value
+ Traffic Ops username. Required. May also be set with the
+ environment variable TO_USER
+
diff --git a/traffic_ops_ort/to_requester/config/config.go b/traffic_ops_ort/to_updater/config/config.go
similarity index 74%
copy from traffic_ops_ort/to_requester/config/config.go
copy to traffic_ops_ort/to_updater/config/config.go
index 72a4cee..152cf97 100644
--- a/traffic_ops_ort/to_requester/config/config.go
+++ b/traffic_ops_ort/to_updater/config/config.go
@@ -21,22 +21,21 @@ package config
import (
"errors"
- "fmt"
"net/url"
"os"
- "strings"
"time"
"github.com/apache/trafficcontrol/lib/go-log"
- "github.com/apache/trafficcontrol/traffic_ops_ort/atstccfg/toreq"
+ "github.com/apache/trafficcontrol/traffic_ops_ort/t3clib"
"github.com/pborman/getopt/v2"
)
-const AppName = "to_requester"
+const AppName = "to_updater"
const Version = "0.1"
const UserAgent = AppName + "/" + Version
type Cfg struct {
+ Debug bool
CommandArgs []string
LogLocationDebug string
LogLocationError string
@@ -44,16 +43,9 @@ type Cfg struct {
LoginDispersion time.Duration
CacheHostName string
GetData string
- TOInsecure bool
- TOTimeoutMS time.Duration
- TOUser string
- TOPass string
- TOURL *url.URL
-}
-
-type TCCfg struct {
- Cfg
- TOClient *toreq.TOClient
+ UpdatePending bool
+ RevalPending bool
+ t3clib.TCCfg
}
func (cfg Cfg) DebugLog() log.LogLocation { return log.LogLocation(cfg.LogLocationDebug) }
@@ -70,13 +62,15 @@ func Usage() {
// InitConfig() intializes the configuration variables and loggers.
func InitConfig() (Cfg, error) {
-
logLocationDebugPtr := getopt.StringLong("log-location-debug", 'd', "", "Where to log debugs. May be a file path, stdout, stderr")
logLocationErrorPtr := getopt.StringLong("log-location-error", 'e', "stderr", "Where to log errors. May be a file path, stdout, stderr")
logLocationInfoPtr := getopt.StringLong("log-location-info", 'i', "stderr", "Where to log infos. May be a file path, stdout, stderr")
dispersionPtr := getopt.IntLong("login-dispersion", 'l', 0, "[seconds] wait a random number of seconds between 0 and [seconds] before login to traffic ops, default 0")
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")
- getDataPtr := getopt.StringLong("get-data", 'D', "system-info", "non-config-file Traffic Ops Data to get. Valid values are all, update-status, packages, chkconfig, system-info, and statuses")
+ var updatePendingPtr bool
+ var revalPendingPtr bool
+ getopt.FlagLong(&updatePendingPtr, "set-update-status", 'q', "[true | false] sets the servers update status").Mandatory()
+ getopt.FlagLong(&revalPendingPtr, "set-reval-status", 'a', "[true | false] sets the servers revalidate status").Mandatory()
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")
@@ -111,7 +105,7 @@ func InitConfig() (Cfg, error) {
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 {
+ } else if err := t3clib.ValidateURL(toURLParsed); err != nil {
return Cfg{}, errors.New("invalid Traffic Ops URL from " + urlSourceStr + " '" + toURL + "': " + err.Error())
}
@@ -131,13 +125,17 @@ func InitConfig() (Cfg, error) {
LogLocationError: *logLocationErrorPtr,
LogLocationInfo: *logLocationInfoPtr,
LoginDispersion: dispersion,
- CacheHostName: cacheHostName,
- GetData: *getDataPtr,
- TOInsecure: *toInsecurePtr,
- TOTimeoutMS: toTimeoutMS,
- TOUser: toUser,
- TOPass: toPass,
- TOURL: toURLParsed,
+ UpdatePending: updatePendingPtr,
+ RevalPending: revalPendingPtr,
+ TCCfg: t3clib.TCCfg{
+ CacheHostName: cacheHostName,
+ GetData: "update-status",
+ TOInsecure: *toInsecurePtr,
+ TOTimeoutMS: toTimeoutMS,
+ TOUser: toUser,
+ TOPass: toPass,
+ TOURL: toURLParsed,
+ },
}
if err := log.InitCfg(cfg); err != nil {
@@ -146,30 +144,3 @@ func InitConfig() (Cfg, error) {
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 (cfg Cfg) PrintConfig() {
- fmt.Printf("CommandArgs: %s\n", cfg.CommandArgs)
- fmt.Printf("LogLocationDebug: %s\n", cfg.LogLocationDebug)
- fmt.Printf("LogLocationError: %s\n", cfg.LogLocationError)
- fmt.Printf("LogLocationInfo: %s\n", cfg.LogLocationInfo)
- fmt.Printf("LoginDispersion : %s\n", cfg.LoginDispersion)
- fmt.Printf("CacheHostName: %s\n", cfg.CacheHostName)
- fmt.Printf("TOInsecure: %s\n", cfg.TOInsecure)
- fmt.Printf("TOTimeoutMS: %s\n", cfg.TOTimeoutMS)
- fmt.Printf("TOUser: %s\n", cfg.TOUser)
- fmt.Printf("TOPass: xxxxxx\n")
- fmt.Printf("TOURL: %s\n", cfg.TOURL)
-}
diff --git a/traffic_ops_ort/to_updater/to_updater.go b/traffic_ops_ort/to_updater/to_updater.go
new file mode 100644
index 0000000..33106e5
--- /dev/null
+++ b/traffic_ops_ort/to_updater/to_updater.go
@@ -0,0 +1,71 @@
+package main
+
+/*
+ * 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 (
+ "fmt"
+ "os"
+
+ "github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/apache/trafficcontrol/traffic_ops_ort/t3clib"
+ "github.com/apache/trafficcontrol/traffic_ops_ort/to_updater/config"
+)
+
+var (
+ cfg config.Cfg
+)
+
+func main() {
+ var err error
+
+ cfg, err = config.InitConfig()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "ERROR: %s\n", err.Error())
+ os.Exit(1)
+ } else {
+ log.Infoln("configuration initialized")
+ }
+
+ // login to TrafficOps
+ tccfg, err := t3clib.TOConnect(&cfg.TCCfg)
+ if err != nil {
+ log.Errorf("%s\n", err)
+ os.Exit(2)
+ }
+
+ err = t3clib.SetUpdateStatus(*tccfg, cfg.TCCfg.CacheHostName, cfg.UpdatePending, cfg.RevalPending)
+ if err != nil {
+ log.Errorf("%s, %s\n", err, cfg.TCCfg.CacheHostName)
+ os.Exit(3)
+ }
+
+ cur_status, err := t3clib.GetServerUpdateStatus(*tccfg)
+ if err != nil {
+ log.Errorf("%s, %s\n", err, cfg.TCCfg.CacheHostName)
+ os.Exit(4)
+ }
+
+ if cur_status.UpdatePending != cfg.UpdatePending && cfg.RevalPending != cfg.RevalPending {
+ log.Errorf("ERROR: update failed, update status and/or reval status was not set.\n")
+ } else {
+ log.Infoln("Update successfully completed")
+ }
+
+}
diff --git a/vendor/github.com/pborman/getopt/v2/getopt.go b/vendor/github.com/pborman/getopt/v2/getopt.go
index 1ffbfaa..e5c52bf 100644
--- a/vendor/github.com/pborman/getopt/v2/getopt.go
+++ b/vendor/github.com/pborman/getopt/v2/getopt.go
@@ -152,6 +152,34 @@
// Unless an option type explicitly prohibits it, an option may appear more than
// once in the arguments. The last value provided to the option is the value.
//
+// MANDATORY OPTIONS
+//
+// An option marked as mandatory and not seen when parsing will cause an error
+// to be reported such as: "program: --name is a mandatory option". An option
+// is marked mandatory by using the Mandatory method:
+//
+// getopt.FlagLong(&fileName, "path", 0, "the path").Mandatory()
+//
+// Mandatory options have (required) appended to their help message:
+//
+// --path=value the path (required)
+//
+// MUTUALLY EXCLUSIVE OPTIONS
+//
+// Options can be marked as part of a mutually exclusive group. When two or
+// more options in a mutually exclusive group are both seen while parsing then
+// an error such as "program: options -a and -b are mutually exclusive" will be
+// reported. Mutually exclusive groups are declared using the SetGroup method:
+//
+// getopt.Flag(&a, 'a', "use method A").SetGroup("method")
+// getopt.Flag(&a, 'b', "use method B").SetGroup("method")
+//
+// A set can have multiple mutually exclusive groups. Mutually exclusive groups
+// are identified with their group name in {}'s appeneded to their help message:
+//
+// -a use method A {method}
+// -b use method B {method}
+//
// BUILTIN TYPES
//
// The Flag and FlagLong functions support most standard Go types. For the
@@ -318,7 +346,7 @@ func (s *Set) PrintOptions(w io.Writer) {
for _, opt := range s.options {
if opt.uname != "" {
opt.help = strings.TrimSpace(opt.help)
- if len(opt.help) == 0 {
+ if len(opt.help) == 0 && !opt.mandatory && opt.group == "" {
fmt.Fprintf(w, " %s\n", opt.uname)
continue
}
@@ -350,6 +378,12 @@ func (s *Set) PrintOptions(w io.Writer) {
if def != "" {
helpMsg += " [" + def + "]"
}
+ if opt.group != "" {
+ helpMsg += " {" + opt.group + "}"
+ }
+ if opt.mandatory {
+ helpMsg += " (required)"
+ }
help := strings.Split(helpMsg, "\n")
// If they did not put in newlines then we will insert
@@ -444,6 +478,12 @@ func (s *Set) Getopt(args []string, fn func(Option) bool) (err error) {
}
}
}()
+
+ defer func() {
+ if err == nil {
+ err = s.checkOptions()
+ }
+ }()
if fn == nil {
fn = func(Option) bool { return true }
}
@@ -562,3 +602,35 @@ Parsing:
s.args = []string{}
return nil
}
+
+func (s *Set) checkOptions() error {
+ groups := map[string]Option{}
+ for _, opt := range s.options {
+ if !opt.Seen() {
+ if opt.mandatory {
+ return fmt.Errorf("option %s is mandatory", opt.Name())
+ }
+ continue
+ }
+ if opt.group == "" {
+ continue
+ }
+ if opt2 := groups[opt.group]; opt2 != nil {
+ return fmt.Errorf("options %s and %s are mutually exclusive", opt2.Name(), opt.Name())
+ }
+ groups[opt.group] = opt
+ }
+ for _, group := range s.requiredGroups {
+ if groups[group] != nil {
+ continue
+ }
+ var flags []string
+ for _, opt := range s.options {
+ if opt.group == group {
+ flags = append(flags, opt.Name())
+ }
+ }
+ return fmt.Errorf("exactly one of the following options must be specified: %s", strings.Join(flags, ", "))
+ }
+ return nil
+}
diff --git a/vendor/github.com/pborman/getopt/v2/option.go b/vendor/github.com/pborman/getopt/v2/option.go
index 67822bc..c97bb29 100644
--- a/vendor/github.com/pborman/getopt/v2/option.go
+++ b/vendor/github.com/pborman/getopt/v2/option.go
@@ -59,21 +59,31 @@ type Option interface {
// yet been seen, including resetting the value of the option
// to its original default state.
Reset()
+
+ // Mandataory sets the mandatory flag of the option. Parse will
+ // fail if a mandatory option is missing.
+ Mandatory() Option
+
+ // SetGroup sets the option as part of a radio group. Parse will
+ // fail if two options in the same group are seen.
+ SetGroup(string) Option
}
type option struct {
- short rune // 0 means no short name
- long string // "" means no long name
- isLong bool // True if they used the long name
- flag bool // true if a boolean flag
- defval string // default value
- optional bool // true if we take an optional value
- help string // help message
- where string // file where the option was defined
- value Value // current value of option
- count int // number of times we have seen this option
- name string // name of the value (for usage)
- uname string // name of the option (for usage)
+ short rune // 0 means no short name
+ long string // "" means no long name
+ isLong bool // True if they used the long name
+ flag bool // true if a boolean flag
+ defval string // default value
+ optional bool // true if we take an optional value
+ help string // help message
+ where string // file where the option was defined
+ value Value // current value of option
+ count int // number of times we have seen this option
+ name string // name of the value (for usage)
+ uname string // name of the option (for usage)
+ mandatory bool // this option must be specified
+ group string // mutual exclusion group
}
// usageName returns the name of the option for printing usage lines in one
@@ -121,12 +131,14 @@ func (o *option) sortName() string {
return o.long[:1] + o.long
}
-func (o *option) Seen() bool { return o.count > 0 }
-func (o *option) Count() int { return o.count }
-func (o *option) IsFlag() bool { return o.flag }
-func (o *option) String() string { return o.value.String() }
-func (o *option) SetOptional() Option { o.optional = true; return o }
-func (o *option) SetFlag() Option { o.flag = true; return o }
+func (o *option) Seen() bool { return o.count > 0 }
+func (o *option) Count() int { return o.count }
+func (o *option) IsFlag() bool { return o.flag }
+func (o *option) String() string { return o.value.String() }
+func (o *option) SetOptional() Option { o.optional = true; return o }
+func (o *option) SetFlag() Option { o.flag = true; return o }
+func (o *option) Mandatory() Option { o.mandatory = true; return o }
+func (o *option) SetGroup(g string) Option { o.group = g; return o }
func (o *option) Value() Value {
if o == nil {
diff --git a/vendor/github.com/pborman/getopt/v2/set.go b/vendor/github.com/pborman/getopt/v2/set.go
index a7895c8..9f73ede 100644
--- a/vendor/github.com/pborman/getopt/v2/set.go
+++ b/vendor/github.com/pborman/getopt/v2/set.go
@@ -46,6 +46,7 @@ type Set struct {
shortOptions map[rune]*option
longOptions map[string]*option
options optionList
+ requiredGroups []string
}
// New returns a newly created option set.
@@ -291,3 +292,18 @@ func (s *Set) Reset() {
opt.Reset()
}
}
+
+// RequiredGroup marks the group set with Option.SetGroup as required. At least
+// one option in the group must be seen by parse. Calling RequiredGroup with a
+// group name that has no options will cause parsing to always fail.
+func (s *Set) RequiredGroup(group string) {
+ s.requiredGroups = append(s.requiredGroups, group)
+}
+
+// RequiredGroup marks the group set with Option.SetGroup as required on the
+// command line. At least one option in the group must be seen by parse.
+// Calling RequiredGroup with a group name that has no options will cause
+// parsing to always fail.
+func RequiredGroup(group string) {
+ CommandLine.requiredGroups = append(CommandLine.requiredGroups, group)
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 431ea0f..583cecf 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -165,7 +165,7 @@ github.com/onsi/gomega/matchers/support/goraph/edge
github.com/onsi/gomega/matchers/support/goraph/node
github.com/onsi/gomega/matchers/support/goraph/util
github.com/onsi/gomega/types
-# github.com/pborman/getopt/v2 v2.1.0 => github.com/pborman/getopt/v2 v2.0.0-20200816005738-fd0d075bf4de
+# github.com/pborman/getopt/v2 v2.1.0
## explicit
github.com/pborman/getopt/v2
# github.com/pkg/errors v0.8.2-0.20190227000051-27936f6d90f9
@@ -183,7 +183,7 @@ golang.org/x/crypto/ed25519/internal/edwards25519
golang.org/x/crypto/ocsp
golang.org/x/crypto/pbkdf2
golang.org/x/crypto/scrypt
-# golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
+# golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4
## explicit
golang.org/x/net/bpf
golang.org/x/net/html
@@ -198,7 +198,7 @@ golang.org/x/net/internal/socket
golang.org/x/net/ipv4
golang.org/x/net/ipv6
golang.org/x/net/publicsuffix
-# golang.org/x/sys v0.0.0-20210227040730-b0d1d43c014d
+# golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005
## explicit
golang.org/x/sys/internal/unsafeheader
golang.org/x/sys/unix