Bootstrap redfish Remote direct

This commit implements redfish remote direct subcommand
under bootstrap.

Change-Id: Idf97445f6fa59a77145eae1edaa15b1d22723f19
Signed-off-by: Kanwar Saad Bin Liaqat <kanwar.sbl@gmail.com>
This commit is contained in:
Kanwar Saad Bin Liaqat 2019-09-19 16:49:55 +03:00
parent 04f75938d9
commit 3bf54274c3
16 changed files with 1065 additions and 56 deletions

View File

@ -16,5 +16,8 @@ func NewBootstrapCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Co
ISOGenCmd := NewISOGenCommand(bootstrapRootCmd, rootSettings)
bootstrapRootCmd.AddCommand(ISOGenCmd)
remoteDirectCmd := NewRemoteDirectCommand(rootSettings)
bootstrapRootCmd.AddCommand(remoteDirectCmd)
return bootstrapRootCmd
}

View File

@ -0,0 +1,73 @@
package bootstrap
import (
"github.com/spf13/cobra"
"opendev.org/airship/airshipctl/pkg/environment"
alog "opendev.org/airship/airshipctl/pkg/log"
remote "opendev.org/airship/airshipctl/pkg/remote"
)
// RemoteDirect settings for remotedirect command
type RemoteDirectSettings struct {
*environment.AirshipCTLSettings
RemoteConfig remote.RemoteDirectConfig
}
// InitFlags adds flags and their default settings for Remote Direct command
func (cmdSetting *RemoteDirectSettings) InitFlags(cmd *cobra.Command) {
flags := cmd.Flags()
// TODO: Remove CLI flags after reading configuration from config documents
// ========================================================================
flags.StringVar(&cmdSetting.RemoteConfig.RemoteURL,
"remote-url",
"http://localhost:8000",
"[Temporary. Will be removed] Remote Redfish/Smash URL")
flags.StringVar(&cmdSetting.RemoteConfig.EphemeralNodeId,
"eph-node-id",
"",
"[Temporary. Will be removed] Ephemeral Node ID")
flags.StringVar(&cmdSetting.RemoteConfig.IsoPath,
"iso-path",
"",
"[Temporary. Will be removed] Remote ISO path for ephemeral node")
flags.StringVar(&cmdSetting.RemoteConfig.RemoteType,
"remote-type",
"redfish",
"Remote type e.g. redfish, smash etc.")
err := cmd.MarkFlagRequired("eph-node-id")
if err != nil {
alog.Fatal(err)
}
err = cmd.MarkFlagRequired("iso-path")
if err != nil {
alog.Fatal(err)
}
}
// New Bootstrap remote direct subcommand
func NewRemoteDirectCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Command {
settings := &RemoteDirectSettings{AirshipCTLSettings: rootSettings}
remoteDirect := &cobra.Command{
Use: "remotedirect",
Short: "Bootstrap ephemeral node",
RunE: func(cmd *cobra.Command, args []string) error {
/* TODO: Get config from settings.GetCurrentContext() and remove cli arguments */
/* Trigger remotedirect based on remote type */
return remote.DoRemoteDirect(settings.RemoteConfig)
},
}
settings.InitFlags(remoteDirect)
return remoteDirect
}

View File

@ -0,0 +1,20 @@
package bootstrap
import (
"testing"
"opendev.org/airship/airshipctl/testutil"
)
func TestRemoteDirect(t *testing.T) {
tests := []*testutil.CmdTest{
{
Name: "remotedirect-cmd-with-help",
CmdLine: "remotedirect --help",
Cmd: NewRemoteDirectCommand(nil),
},
}
for _, tt := range tests {
testutil.RunTest(t, tt)
}
}

View File

@ -6,6 +6,7 @@ Usage:
Available Commands:
help Help about any command
isogen Generate bootstrap ISO image
remotedirect Bootstrap ephemeral node
Flags:
-h, --help help for bootstrap

View File

@ -0,0 +1,11 @@
Bootstrap ephemeral node
Usage:
remotedirect [flags]
Flags:
--eph-node-id string [Temporary. Will be removed] Ephemeral Node ID
-h, --help help for remotedirect
--iso-path string [Temporary. Will be removed] Remote ISO path for ephemeral node
--remote-type string Remote type e.g. redfish, smash etc. (default "redfish")
--remote-url string [Temporary. Will be removed] Remote Redfish/Smash URL (default "http://localhost:8000")

2
go.mod
View File

@ -7,6 +7,8 @@ require (
github.com/Azure/go-autorest/autorest v0.2.0 // indirect
github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e // indirect
github.com/Microsoft/go-winio v0.4.12 // indirect
github.com/Nordix/go-redfish v0.0.0-20191016123452-b7790941aa86
github.com/Nordix/go-redfish/client v0.0.0-20191016123452-b7790941aa86
github.com/argoproj/argo v2.3.0+incompatible
github.com/argoproj/pkg v0.0.0-20190409001913-7e3ef65c8d44 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect

59
go.sum
View File

@ -1,10 +1,8 @@
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
contrib.go.opencensus.io/exporter/ocagent v0.4.12 h1:jGFvw3l57ViIVEPKKEUXPcLYIXJmQxLUh6ey1eJhwyc=
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest/autorest v0.2.0 h1:zBtSTOQTtjzHVRe+mhkiHvHwRTKHhjBEyo1m6DfI3So=
github.com/Azure/go-autorest/autorest v0.2.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
@ -12,7 +10,6 @@ github.com/Azure/go-autorest/autorest/adal v0.1.0 h1:RSw/7EAullliqwkZvgIGDYZWQm1
github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0 h1:Kx+AUU2Te+A3JIyYn6Dfs+cFgx5XorQKuIXrZGoq/SI=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
@ -22,9 +19,12 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e h1:eb0Pzkt15Bm7f2FFYv7sjY7NPFi3cPkS3tv1CcrFBWA=
github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc=
github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/Nordix/go-redfish v0.0.0-20191016123452-b7790941aa86 h1:+Myox4ydvtWzVSnvF/ZjABEA7RDiPRgE3sS5u4oQsHk=
github.com/Nordix/go-redfish v0.0.0-20191016123452-b7790941aa86/go.mod h1:xlgfxiVLo/0lxBF98IhyCkAboUX27SOXieV1LsbyA30=
github.com/Nordix/go-redfish/client v0.0.0-20191016123452-b7790941aa86 h1:Frmy6I0XnpsCrM+FFrXq3354M3dVSrR+fFGIGb3fxXk=
github.com/Nordix/go-redfish/client v0.0.0-20191016123452-b7790941aa86/go.mod h1:44cqm4BYTmR3MAfUFZmYYOXZ4S9rDr8QM6MlQagq4M8=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
@ -54,15 +54,11 @@ github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlR
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/colinmarc/hdfs v0.0.0-20180802165501-48eb8d6c34a9 h1:hJqIlFDcaaBLEBkCuqyEtzSsloo/h+lm08Qrq1OM/e8=
github.com/colinmarc/hdfs v0.0.0-20180802165501-48eb8d6c34a9/go.mod h1:0DumPviB681UcSuJErAbDIOx6SIaJWj463TymfZG02I=
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -89,11 +85,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f h1:8GDPb0tCY8LQ+OJ3dbHb5sA6YZWXFORQYZx5sdsTlMs=
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f h1:AUj1VoZUfhPhOPHULCQQDnGhRelpFWHMLhQVWDsS0v4=
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.6+incompatible h1:tfrHha8zJ01ywiOEC1miGY8st1/igzWB8OmvPgoYX7w=
github.com/emicklei/go-restful v2.9.6+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
@ -107,7 +100,6 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
@ -131,11 +123,9 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@ -144,15 +134,11 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450 h1:7xqw01UYS+KCI25bMrPxwNYkSns2Db1ziQPpVq99FpE=
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=
github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995 h1:f5gsjBiF9tRRVomCvrkGMMWI8W1f2OBFar2c5oakAP0=
github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8=
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e h1:KhcknUwkWHKZPbFy2P7jH5LKJ3La+0ZeknkkmrSgqb0=
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
@ -167,15 +153,12 @@ github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I=
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc h1:f8eY6cV/x1x+HLjOp4r72s/31/V2aTUtg5oKRRPf8/Q=
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
@ -185,11 +168,9 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03 h1:FUwcHNlEqkqLjLBdCp5PRlCFijNjvcYANOZXzCfXwCM=
github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
@ -204,14 +185,11 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
@ -240,7 +218,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@ -249,26 +226,21 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -292,11 +264,9 @@ github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYe
github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
@ -311,11 +281,9 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
@ -325,20 +293,14 @@ github.com/vishvananda/netlink v0.0.0-20171020171820-b2de5d10e38e h1:f1yevOHP+Su
github.com/vishvananda/netlink v0.0.0-20171020171820-b2de5d10e38e/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netns v0.0.0-20171111001504-be1fbeda1936 h1:J9gO8RJCAFlln1jsvRba/CWVUnMHwObklfxxjErl1uk=
github.com/vishvananda/netns v0.0.0-20171111001504-be1fbeda1936/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 h1:j2hhcujLRHAg872RWAV5yaUrEjHEObwDv3aImCaNLek=
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2 h1:NAfh7zF0/3/HqtMvJNZ/RFrSlCE6ZTlHmKfhL/Dm1Jk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -366,10 +328,9 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -394,7 +355,6 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190621203818-d432491b9138 h1:t8BZD9RDjkm9/h7yYN6kE8oaeov5r9aztkB7zKA5Tkg=
golang.org/x/sys v0.0.0-20190621203818-d432491b9138/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@ -411,7 +371,6 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw
google.golang.org/api v0.3.1 h1:oJra/lMfmtm13/rgY/8i3MzjFWYXvQIAKjQ3HqofMk8=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
@ -422,9 +381,7 @@ google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
@ -432,7 +389,6 @@ gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hr
gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo=
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM=
gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q=
gopkg.in/jcmturner/goidentity.v2 v2.0.0 h1:6Bmcdaxb0dD3HyHbo/MtJ2Q1wXLDuZJFwXZmuZvM+zw=
gopkg.in/jcmturner/goidentity.v2 v2.0.0/go.mod h1:vCwK9HeXksMeUmQ4SxDd1tRz4LejrKh3KRVjQWhjvZI=
gopkg.in/jcmturner/gokrb5.v5 v5.3.0 h1:RS1MYApX27Hx1Xw7NECs7XxGxxrm69/4OmaRuX9kwec=
gopkg.in/jcmturner/gokrb5.v5 v5.3.0/go.mod h1:oQz8Wc5GsctOTgCVyKad1Vw4TCWz5G6gfIQr88RPv4k=
@ -443,14 +399,11 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@ -475,10 +428,8 @@ k8s.io/cluster-bootstrap v0.0.0-20190516232516-d7d78ab2cfe7 h1:5wvjieVoU4oovHlke
k8s.io/cluster-bootstrap v0.0.0-20190516232516-d7d78ab2cfe7/go.mod h1:iBSm2nwo3OaiuW8VDvc3ySDXK5SKfUrxwPvBloKG7zg=
k8s.io/component-base v0.0.0-20190516230545-9b07ebd4193b h1:uGhPlhc5zuKFBhA2A94CFQjr1R9gz3u65BQFGqcV+rE=
k8s.io/component-base v0.0.0-20190516230545-9b07ebd4193b/go.mod h1:DMaomcf3j3MM2j1FsvlLVVlc7wA2jPytEur3cP9zRxQ=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6 h1:4s3/R4+OYYYUKptXPhZKjQ04WJ6EhQQVFdjOFvCazDk=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.3 h1:niceAagH1tzskmaie/icWd7ci1wbG7Bf2c6YGcQv+3c=
k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=

14
pkg/error/error.go Normal file
View File

@ -0,0 +1,14 @@
package error
// AirshipError is the base error type
// used to create extended error types
// in other airshipctl packages.
type AirshipError struct {
Message string
}
// Error function implments the golang
// error interface
func (ae *AirshipError) Error() string {
return ae.Message
}

17
pkg/remote/errors.go Normal file
View File

@ -0,0 +1,17 @@
package remote
import (
"fmt"
aerror "opendev.org/airship/airshipctl/pkg/error"
)
type RemoteDirectError struct {
aerror.AirshipError
}
func NewRemoteDirectErrorf(format string, v ...interface{}) error {
e := &RemoteDirectError{}
e.Message = fmt.Sprintf(format, v...)
return e
}

View File

@ -0,0 +1,42 @@
package redfish
import (
"fmt"
"net/http"
aerror "opendev.org/airship/airshipctl/pkg/error"
)
type RedfishClientError struct {
aerror.AirshipError
}
func NewRedfishClientErrorf(format string, v ...interface{}) error {
e := &RedfishClientError{}
e.Message = fmt.Sprintf(format, v...)
return e
}
type RedfishConfigError struct {
aerror.AirshipError
}
func NewRedfishConfigErrorf(format string, v ...interface{}) error {
e := &RedfishConfigError{}
e.Message = fmt.Sprintf(format, v...)
return e
}
// Redfish client return error if response has no body.
// In this case this function further checks the
// HTTP response code.
func ScreenRedfishError(httpResp *http.Response, err error) error {
if err != nil && httpResp != nil {
if httpResp.StatusCode < 200 || httpResp.StatusCode >= 400 {
return NewRedfishClientErrorf("%s", err.Error())
}
} else if err != nil {
return NewRedfishClientErrorf("%s", err.Error())
}
return nil
}

View File

@ -0,0 +1,127 @@
package redfish
import (
"context"
"net/url"
redfishApi "github.com/Nordix/go-redfish/api"
redfishClient "github.com/Nordix/go-redfish/client"
alog "opendev.org/airship/airshipctl/pkg/log"
)
type RedfishRemoteDirect struct {
// Context
Context context.Context
// remote URL
RemoteURL url.URL
// ephemeral Host ID
EphemeralNodeId string
// ISO URL
IsoPath string
// Redfish Client implementation
Api redfishApi.RedfishAPI
}
// Top level function to handle Redfish remote direct
func (cfg RedfishRemoteDirect) DoRemoteDirect() error {
alog.Debugf("Using Redfish Endpoint: '%s'", cfg.RemoteURL.String())
/* TODO: Add Authentication when redfish library supports it. */
/* Get system details */
systemId := cfg.EphemeralNodeId
system, _, err := cfg.Api.GetSystem(cfg.Context, systemId)
if err != nil {
return NewRedfishClientErrorf("Get System[%s] failed with err: %s", systemId, err.Error())
}
alog.Debugf("Ephemeral Node System ID: '%s'", systemId)
/* get manager for system */
managerId, err := GetResourceIDFromURL(system.Links.ManagedBy[0].OdataId)
if err != nil {
return err
}
alog.Debugf("Ephemeral node managerId: '%s'", managerId)
/* Get manager's Cd or DVD virtual media ID */
vMediaId, vMediaType, err := GetVirtualMediaId(cfg.Context, cfg.Api, managerId)
if err != nil {
return err
}
alog.Debugf("Ephemeral Node Virtual Media Id: '%s'", vMediaId)
/* Load ISO in manager's virtual media */
err = SetVirtualMedia(cfg.Context, cfg.Api, managerId, vMediaId, cfg.IsoPath)
if err != nil {
return err
}
alog.Debugf("Successfully loaded virtual media: '%s'", cfg.IsoPath)
/* Set system's bootsource to selected media */
err = SetSystemBootSourceForMediaType(cfg.Context, cfg.Api, systemId, vMediaType)
if err != nil {
return err
}
/* Reboot system */
err = RebootSystem(cfg.Context, cfg.Api, systemId)
if err != nil {
return err
}
alog.Debug("Restarted ephemeral host")
return nil
}
// Creates a new Redfish remote direct client.
func NewRedfishRemoteDirectClient(ctx context.Context,
remoteURL string,
ephNodeID string,
isoPath string,
) (RedfishRemoteDirect, error) {
if remoteURL == "" {
return RedfishRemoteDirect{},
NewRedfishConfigErrorf("redfish remote url empty")
}
if ephNodeID == "" {
return RedfishRemoteDirect{},
NewRedfishConfigErrorf("redfish ephemeral node id empty")
}
if isoPath == "" {
return RedfishRemoteDirect{},
NewRedfishConfigErrorf("redfish ephemeral node iso Path empty")
}
cfg := &redfishClient.Configuration{
BasePath: remoteURL,
DefaultHeader: make(map[string]string),
UserAgent: "airshipctl/client",
}
var api redfishApi.RedfishAPI = redfishClient.NewAPIClient(cfg).DefaultApi
url, err := url.Parse(remoteURL)
if err != nil {
return RedfishRemoteDirect{}, NewRedfishConfigErrorf("Invalid URL format: %v", err)
}
client := RedfishRemoteDirect{
Context: ctx,
RemoteURL: *url,
EphemeralNodeId: ephNodeID,
IsoPath: isoPath,
Api: api,
}
return client, nil
}

View File

@ -0,0 +1,266 @@
package redfish_test
import (
"context"
"fmt"
"net/http"
"testing"
redfishAPI "github.com/Nordix/go-redfish/api"
redfishMocks "github.com/Nordix/go-redfish/api/mocks"
redfishClient "github.com/Nordix/go-redfish/client"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
. "opendev.org/airship/airshipctl/pkg/remote/redfish"
)
const (
computerSystemID = "server-100"
)
var (
ctx = context.Background()
defaultURL = "https://localhost:1234"
SampleSystem = redfishClient.ComputerSystem{
Id: computerSystemID,
Name: "server-100",
UUID: "58893887-8974-2487-2389-841168418919",
Status: redfishClient.Status{
State: "Enabled",
Health: "OK",
},
Links: redfishClient.SystemLinks{
ManagedBy: []redfishClient.IdRef{
{OdataId: "/redfish/v1/Managers/manager-1"},
},
},
Boot: redfishClient.Boot{
BootSourceOverrideTarget: redfishClient.BOOTSOURCE_CD,
BootSourceOverrideEnabled: redfishClient.BOOTSOURCEOVERRIDEENABLED_CONTINUOUS,
BootSourceOverrideTargetRedfishAllowableValues: []redfishClient.BootSource{
redfishClient.BOOTSOURCE_CD,
redfishClient.BOOTSOURCE_FLOPPY,
redfishClient.BOOTSOURCE_HDD,
redfishClient.BOOTSOURCE_PXE,
},
},
}
)
func getDefaultRedfishRemoteDirectObj(t *testing.T, api redfishAPI.RedfishAPI) RedfishRemoteDirect {
t.Helper()
rDCfg, err := NewRedfishRemoteDirectClient(
ctx,
defaultURL,
computerSystemID,
"/tmp/test.iso",
)
assert.NoError(t, err)
rDCfg.Api = api
return rDCfg
}
func TestRedfishRemoteDirectNormal(t *testing.T) {
m := &redfishMocks.RedfishAPI{}
defer m.AssertExpectations(t)
systemID := computerSystemID
m.On("GetSystem", ctx, systemID).
Return(SampleSystem, nil, nil)
m.On("InsertVirtualMedia", ctx, "manager-1", "Cd", mock.Anything).
Return(redfishClient.RedfishError{}, nil, nil)
systemReq := redfishClient.ComputerSystem{
Boot: redfishClient.Boot{
BootSourceOverrideTarget: redfishClient.BOOTSOURCE_CD,
},
}
m.On("SetSystem", ctx, systemID, systemReq).
Times(1).
Return(redfishClient.ComputerSystem{}, nil, nil)
resetReq := redfishClient.ResetRequestBody{}
resetReq.ResetType = redfishClient.RESETTYPE_FORCE_OFF
m.On("ResetSystem", ctx, systemID, resetReq).
Times(1).
Return(redfishClient.RedfishError{}, nil, nil)
resetReq.ResetType = redfishClient.RESETTYPE_ON
m.On("ResetSystem", ctx, systemID, resetReq).
Times(1).
Return(redfishClient.RedfishError{}, nil, nil)
rDCfg := getDefaultRedfishRemoteDirectObj(t, m)
err := rDCfg.DoRemoteDirect()
assert.NoError(t, err)
}
func TestRedfishRemoteDirectInvalidSystemId(t *testing.T) {
m := &redfishMocks.RedfishAPI{}
defer m.AssertExpectations(t)
systemID := "invalid-server"
localRDCfg := getDefaultRedfishRemoteDirectObj(t, m)
localRDCfg.EphemeralNodeId = systemID
realErr := fmt.Errorf("%s system do not exist", systemID)
m.On("GetSystem", ctx, systemID).
Times(1).
Return(redfishClient.ComputerSystem{}, nil, realErr)
err := localRDCfg.DoRemoteDirect()
_, ok := err.(*RedfishClientError)
assert.True(t, ok)
}
func TestRedfishRemoteDirectGetSystemNetworkError(t *testing.T) {
m := &redfishMocks.RedfishAPI{}
defer m.AssertExpectations(t)
systemID := computerSystemID
realErr := fmt.Errorf("Server request timeout")
httpResp := &http.Response{
StatusCode: 408,
}
m.On("GetSystem", ctx, systemID).
Times(1).
Return(redfishClient.ComputerSystem{}, httpResp, realErr)
rDCfg := getDefaultRedfishRemoteDirectObj(t, m)
err := rDCfg.DoRemoteDirect()
_, ok := err.(*RedfishClientError)
assert.True(t, ok)
}
func TestRedfishRemoteDirectInvalidIsoPath(t *testing.T) {
m := &redfishMocks.RedfishAPI{}
defer m.AssertExpectations(t)
systemID := computerSystemID
rDCfg := getDefaultRedfishRemoteDirectObj(t, m)
localRDCfg := rDCfg
localRDCfg.IsoPath = "bogus/path/to.iso"
errStr := "Invalid remote boot path"
realErr := fmt.Errorf(errStr)
httpResp := &http.Response{
StatusCode: 500,
}
m.On("GetSystem", ctx, systemID).
Times(1).
Return(SampleSystem, nil, nil)
m.On("InsertVirtualMedia", ctx, "manager-1", "Cd", mock.Anything).
Return(redfishClient.RedfishError{}, httpResp, realErr)
err := localRDCfg.DoRemoteDirect()
_, ok := err.(*RedfishClientError)
assert.True(t, ok)
}
func TestRedfishRemoteDirectCdDvdNotAvailableInBootSources(t *testing.T) {
m := &redfishMocks.RedfishAPI{}
defer m.AssertExpectations(t)
systemID := computerSystemID
invalidSystem := SampleSystem
invalidSystem.Boot.BootSourceOverrideTargetRedfishAllowableValues = []redfishClient.BootSource{
redfishClient.BOOTSOURCE_HDD,
redfishClient.BOOTSOURCE_PXE,
}
m.On("GetSystem", ctx, systemID).
Return(invalidSystem, nil, nil)
m.On("InsertVirtualMedia", ctx, "manager-1", "Cd", mock.Anything).
Return(redfishClient.RedfishError{}, nil, nil)
rDCfg := getDefaultRedfishRemoteDirectObj(t, m)
err := rDCfg.DoRemoteDirect()
_, ok := err.(*RedfishClientError)
assert.True(t, ok)
}
func TestRedfishRemoteDirectSetSystemBootSourceFailed(t *testing.T) {
m := &redfishMocks.RedfishAPI{}
defer m.AssertExpectations(t)
systemID := computerSystemID
m.On("GetSystem", ctx, systemID).
Return(SampleSystem, nil, nil)
m.On("InsertVirtualMedia", ctx, "manager-1", "Cd", mock.Anything).
Return(redfishClient.RedfishError{}, nil, nil)
realErr := fmt.Errorf("Unauthorized")
httpResp := &http.Response{
StatusCode: 401,
}
m.On("SetSystem", ctx, systemID, mock.Anything).
Times(1).
Return(redfishClient.ComputerSystem{}, httpResp, realErr)
rDCfg := getDefaultRedfishRemoteDirectObj(t, m)
err := rDCfg.DoRemoteDirect()
_, ok := err.(*RedfishClientError)
assert.True(t, ok)
}
func TestRedfishRemoteDirectSystemRebootFailed(t *testing.T) {
m := &redfishMocks.RedfishAPI{}
defer m.AssertExpectations(t)
systemID := computerSystemID
m.On("GetSystem", ctx, systemID).
Return(SampleSystem, nil, nil)
m.On("InsertVirtualMedia", ctx, mock.Anything, mock.Anything, mock.Anything).
Return(redfishClient.RedfishError{}, nil, nil)
m.On("SetSystem", ctx, systemID, mock.Anything).
Times(1).
Return(redfishClient.ComputerSystem{}, nil, nil)
realErr := fmt.Errorf("Unauthorized")
httpResp := &http.Response{
StatusCode: 401,
}
resetReq := redfishClient.ResetRequestBody{}
resetReq.ResetType = redfishClient.RESETTYPE_FORCE_OFF
m.On("ResetSystem", ctx, systemID, resetReq).
Times(1).
Return(redfishClient.RedfishError{}, httpResp, realErr)
rDCfg := getDefaultRedfishRemoteDirectObj(t, m)
err := rDCfg.DoRemoteDirect()
_, ok := err.(*RedfishClientError)
assert.True(t, ok)
}

118
pkg/remote/redfish/utils.go Normal file
View File

@ -0,0 +1,118 @@
package redfish
import (
"context"
"net/url"
"strings"
"time"
redfishApi "github.com/Nordix/go-redfish/api"
redfishClient "github.com/Nordix/go-redfish/client"
)
const (
SystemRebootDelay = 2 * time.Second
)
// Redfish Id ref is a URI which contains resource Id
// as the last part. This function extracts resource
// ID from ID ref
func GetResourceIDFromURL(refURL string) (string, error) {
u, err := url.Parse(refURL)
if err != nil {
return "", err
}
url := strings.TrimSuffix(u.Path, "/")
elems := strings.Split(url, "/")
id := elems[len(elems)-1]
return id, nil
}
// Checks whether an ID exists in Redfish IDref collection
func IsIDInList(idRefList []redfishClient.IdRef, id string) bool {
for _, r := range idRefList {
rId, _ := GetResourceIDFromURL(r.OdataId)
if rId == id {
return true
}
}
return false
}
// This function walks through the list of manager's virtual media resources
// and gets the ID of virtualmedia which has type "CD" or "DVD"
func GetVirtualMediaId(ctx context.Context,
api redfishApi.RedfishAPI,
managerId string,
) (string, string, error) {
// TODO: Sushy emulator has a bug which sends 'virtualMedia.inserted' field as
// string instead of a boolean which causes the redfish client to fail
// while unmarshalling this field.
// Insert logic here after the bug is fixed in sushy-emulator.
return "Cd", "CD", nil
}
// This function walks through the bootsources of a system and sets the bootsource
// which is compatible with the given media type.
func SetSystemBootSourceForMediaType(ctx context.Context,
api redfishApi.RedfishAPI,
systemId string,
mediaType string) error {
/* Check available boot sources for system */
system, _, err := api.GetSystem(ctx, systemId)
if err != nil {
return NewRedfishClientErrorf("Get System[%s] failed with err: %s", systemId, err.Error())
}
allowableValues := system.Boot.BootSourceOverrideTargetRedfishAllowableValues
for _, bootSource := range allowableValues {
if strings.EqualFold(string(bootSource), mediaType) {
/* set boot source */
systemReq := redfishClient.ComputerSystem{}
systemReq.Boot.BootSourceOverrideTarget = bootSource
_, httpResp, err := api.SetSystem(ctx, systemId, systemReq)
return ScreenRedfishError(httpResp, err)
}
}
return NewRedfishClientErrorf("failed to set system[%s] boot source", systemId)
}
// Reboots a system by force shutoff and turning on.
func RebootSystem(ctx context.Context, api redfishApi.RedfishAPI, systemId string) error {
resetReq := redfishClient.ResetRequestBody{}
resetReq.ResetType = redfishClient.RESETTYPE_FORCE_OFF
_, httpResp, err := api.ResetSystem(ctx, systemId, resetReq)
if err != nil {
return ScreenRedfishError(httpResp, err)
}
time.Sleep(SystemRebootDelay)
resetReq.ResetType = redfishClient.RESETTYPE_ON
_, httpResp, err = api.ResetSystem(ctx, systemId, resetReq)
return ScreenRedfishError(httpResp, err)
}
// Insert the remote virtual media to the given virtual media id.
// This assumes that isoPath is accessible to the redfish server and
// virtualMedia device is either of type CD or DVD.
func SetVirtualMedia(ctx context.Context,
api redfishApi.RedfishAPI,
managerId string,
vMediaId string,
isoPath string) error {
vMediaReq := redfishClient.InsertMediaRequestBody{}
vMediaReq.Image = isoPath
vMediaReq.Inserted = true
_, httpResp, err := api.InsertVirtualMedia(ctx, managerId, vMediaId, vMediaReq)
return ScreenRedfishError(httpResp, err)
}

View File

@ -0,0 +1,194 @@
package redfish
import (
"context"
"fmt"
"net/http"
"testing"
redfishMocks "github.com/Nordix/go-redfish/api/mocks"
redfishClient "github.com/Nordix/go-redfish/client"
"github.com/stretchr/testify/assert"
)
const (
systemID = "123"
)
func TestRedfishErrorNoError(t *testing.T) {
err := ScreenRedfishError(nil, nil)
assert.NoError(t, err)
}
func TestRedfishErrorNonNilErrorWithoutHttpResp(t *testing.T) {
realErr := fmt.Errorf("Sample error")
err := ScreenRedfishError(nil, realErr)
assert.Error(t, err)
_, ok := err.(*RedfishClientError)
assert.True(t, ok)
}
func TestRedfishErrorNonNilErrorWithHttpRespError(t *testing.T) {
realErr := fmt.Errorf("Sample error")
httpResp := &http.Response{StatusCode: 408}
err := ScreenRedfishError(httpResp, realErr)
assert.Equal(t, err, NewRedfishClientErrorf(realErr.Error()))
httpResp.StatusCode = 400
err = ScreenRedfishError(httpResp, realErr)
assert.Equal(t, err, NewRedfishClientErrorf(realErr.Error()))
httpResp.StatusCode = 199
err = ScreenRedfishError(httpResp, realErr)
assert.Equal(t, err, NewRedfishClientErrorf(realErr.Error()))
}
func TestRedfishErrorNonNilErrorWithHttpRespOK(t *testing.T) {
realErr := fmt.Errorf("Sample error")
httpResp := &http.Response{StatusCode: 204}
err := ScreenRedfishError(httpResp, realErr)
assert.NoError(t, err)
httpResp.StatusCode = 200
err = ScreenRedfishError(httpResp, realErr)
assert.NoError(t, err)
httpResp.StatusCode = 399
err = ScreenRedfishError(httpResp, realErr)
assert.NoError(t, err)
}
func TestRedfishUtilGetResIDFromURL(t *testing.T) {
// simple case
url := "api/user/123"
id, err := GetResourceIDFromURL(url)
assert.NoError(t, err)
assert.Equal(t, id, "123")
// FQDN
url = "http://www.abc.com/api/user/123"
id, err = GetResourceIDFromURL(url)
assert.NoError(t, err)
assert.Equal(t, id, "123")
//Trailing slash
url = "api/user/123/"
id, err = GetResourceIDFromURL(url)
assert.NoError(t, err)
assert.Equal(t, id, "123")
}
func TestRedfishUtilIsIdInList(t *testing.T) {
idList := []redfishClient.IdRef{
{OdataId: "/path/to/id/1"},
{OdataId: "/path/to/id/2"},
{OdataId: "/path/to/id/3"},
{OdataId: "/path/to/id/4"},
}
emptyList := []redfishClient.IdRef{}
res := IsIDInList(idList, "1")
assert.True(t, res)
res = IsIDInList(idList, "100")
assert.False(t, res)
res = IsIDInList(emptyList, "1")
assert.False(t, res)
}
func TestRedfishUtilRebootSystemOK(t *testing.T) {
m := &redfishMocks.RedfishAPI{}
defer m.AssertExpectations(t)
ctx := context.Background()
resetReq := redfishClient.ResetRequestBody{}
resetReq.ResetType = redfishClient.RESETTYPE_FORCE_OFF
m.On("ResetSystem", ctx, systemID, resetReq).
Times(1).
Return(redfishClient.RedfishError{}, nil, nil)
resetReq.ResetType = redfishClient.RESETTYPE_ON
m.On("ResetSystem", ctx, systemID, resetReq).
Times(1).
Return(redfishClient.RedfishError{}, nil, nil)
err := RebootSystem(ctx, m, systemID)
assert.NoError(t, err)
}
func TestRedfishUtilRebootSystemForceOffError2(t *testing.T) {
m := &redfishMocks.RedfishAPI{}
defer m.AssertExpectations(t)
ctx := context.Background()
realErr := fmt.Errorf("Unauthorized")
httpResp := &http.Response{
StatusCode: 401,
}
resetReq := redfishClient.ResetRequestBody{}
resetReq.ResetType = redfishClient.RESETTYPE_FORCE_OFF
m.On("ResetSystem", ctx, systemID, resetReq).
Times(1).
Return(redfishClient.RedfishError{}, httpResp, realErr)
err := RebootSystem(ctx, m, systemID)
assert.Error(t, err)
}
func TestRedfishUtilRebootSystemForceOffError(t *testing.T) {
m := &redfishMocks.RedfishAPI{}
defer m.AssertExpectations(t)
ctx := context.Background()
realErr := fmt.Errorf("Unauthorized")
httpResp := &http.Response{
StatusCode: 401,
}
resetReq := redfishClient.ResetRequestBody{}
resetReq.ResetType = redfishClient.RESETTYPE_FORCE_OFF
m.On("ResetSystem", ctx, systemID, resetReq).
Times(1).
Return(redfishClient.RedfishError{}, httpResp, realErr)
err := RebootSystem(ctx, m, systemID)
assert.Error(t, err)
}
func TestRedfishUtilRebootSystemTurningOnError(t *testing.T) {
m := &redfishMocks.RedfishAPI{}
defer m.AssertExpectations(t)
ctx := context.Background()
resetReq := redfishClient.ResetRequestBody{}
resetReq.ResetType = redfishClient.RESETTYPE_FORCE_OFF
m.On("ResetSystem", ctx, systemID, resetReq).
Times(1).
Return(redfishClient.RedfishError{}, nil, nil)
realErr := fmt.Errorf("Unauthorized")
httpResp := &http.Response{
StatusCode: 401,
}
resetOnReq := redfishClient.ResetRequestBody{}
resetOnReq.ResetType = redfishClient.RESETTYPE_ON
m.On("ResetSystem", ctx, systemID, resetOnReq).
Times(1).
Return(redfishClient.RedfishError{}, httpResp, realErr)
err := RebootSystem(ctx, m, systemID)
assert.Error(t, err)
}

View File

@ -0,0 +1,86 @@
package remote
import (
"context"
alog "opendev.org/airship/airshipctl/pkg/log"
redfish "opendev.org/airship/airshipctl/pkg/remote/redfish"
)
const (
AirshipRemoteTypeRedfish string = "redfish"
AirshipRemoteTypeSmash string = "smash"
)
// This structure defines the common remote direct config
// for all remote types.
type RemoteDirectConfig struct {
// remote type
RemoteType string
// remote URL
RemoteURL string
// ephemeral Host ID
EphemeralNodeId string
// ISO URL
IsoPath string
// TODO: Ephemeral Node IP
// TODO: kubeconfig (in object form or raw yaml?) for ephemeral node validation.
// TODO: More fields can be added on need basis
}
// Interface to be implemented by remoteDirect implementation
type RemoteDirectClient interface {
DoRemoteDirect() error
}
// Get remotedirect client based on config
func getRemoteDirectClient(remoteConfig RemoteDirectConfig) (RemoteDirectClient, error) {
var client RemoteDirectClient
var err error
switch remoteConfig.RemoteType {
case AirshipRemoteTypeRedfish:
alog.Debug("Remote type redfish")
client, err = redfish.NewRedfishRemoteDirectClient(
context.Background(),
remoteConfig.RemoteURL,
remoteConfig.EphemeralNodeId,
remoteConfig.IsoPath)
if err != nil {
alog.Debugf("redfish remotedirect client creation failed")
return nil, err
}
default:
return nil, NewRemoteDirectErrorf("invalid remote type")
}
return client, nil
}
// Top level function to execute remote direct based on remote type
func DoRemoteDirect(remoteConfig RemoteDirectConfig) error {
client, err := getRemoteDirectClient(remoteConfig)
if err != nil {
return err
}
err = client.DoRemoteDirect()
if err != nil {
alog.Debugf("remote direct failed: %s", err)
return err
}
alog.Print("Remote direct successfully completed")
return nil
}

View File

@ -0,0 +1,84 @@
package remote
import (
"testing"
"github.com/stretchr/testify/assert"
redfish "opendev.org/airship/airshipctl/pkg/remote/redfish"
)
func TestUnknownRemoteType(t *testing.T) {
rdCfg := RemoteDirectConfig{
RemoteType: "new-remote",
RemoteURL: "http://localhost:8000",
EphemeralNodeId: "test-node",
IsoPath: "/test.iso",
}
err := DoRemoteDirect(rdCfg)
_, ok := err.(*RemoteDirectError)
assert.True(t, ok)
}
func TestRedfishRemoteDirectWithBogusConfig(t *testing.T) {
rdCfg := RemoteDirectConfig{
RemoteType: "redfish",
RemoteURL: "http://nolocalhost:8888",
EphemeralNodeId: "test-node",
IsoPath: "/test.iso",
}
err := DoRemoteDirect(rdCfg)
_, ok := err.(*redfish.RedfishClientError)
assert.True(t, ok)
}
func TestRedfishRemoteDirectWithEmptyURL(t *testing.T) {
rdCfg := RemoteDirectConfig{
RemoteType: "redfish",
RemoteURL: "",
EphemeralNodeId: "test-node",
IsoPath: "/test.iso",
}
err := DoRemoteDirect(rdCfg)
_, ok := err.(*redfish.RedfishConfigError)
assert.True(t, ok)
}
func TestRedfishRemoteDirectWithEmptyNodeID(t *testing.T) {
rdCfg := RemoteDirectConfig{
RemoteType: "redfish",
RemoteURL: "http://nolocalhost:8888",
EphemeralNodeId: "",
IsoPath: "/test.iso",
}
err := DoRemoteDirect(rdCfg)
_, ok := err.(*redfish.RedfishConfigError)
assert.True(t, ok)
}
func TestRedfishRemoteDirectWithEmptyIsoPath(t *testing.T) {
rdCfg := RemoteDirectConfig{
RemoteType: "redfish",
RemoteURL: "http://nolocalhost:8888",
EphemeralNodeId: "123",
IsoPath: "",
}
err := DoRemoteDirect(rdCfg)
_, ok := err.(*redfish.RedfishConfigError)
assert.True(t, ok)
}