diff --git a/.gitignore b/.gitignore index 6c69adbb..e7c639ca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # project data /.idea /.tools +/.vscode /vendor # binaries @@ -12,6 +13,9 @@ /man_page /config_defaults /update_configuration +/wwapic +/wwapid +/wwapird # other created files /man_pages @@ -25,4 +29,7 @@ _dist/ config warewulf-*.tar.gz Defaults.mk +/etc/wwapid.config +/etc/wwapic.config +/etc/wwapird.config diff --git a/Makefile b/Makefile index 7ffbfb7b..60ec87c6 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,7 @@ export GOPROXY # built tags needed for wwbuild binary WW_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree -all: config vendor wwctl wwclient bash_completion.d man_pages config_defaults +all: config vendor wwctl wwclient bash_completion.d man_pages config_defaults wwapid wwapic wwapird build: lint test-it vet all @@ -170,6 +170,9 @@ files: all install -d -m 0755 $(DESTDIR)$(WWDATADIR)/ipxe test -f $(DESTDIR)$(WWCONFIGDIR)/warewulf.conf || install -m 644 etc/warewulf.conf $(DESTDIR)$(WWCONFIGDIR) test -f $(DESTDIR)$(WWCONFIGDIR)/nodes.conf || install -m 644 etc/nodes.conf $(DESTDIR)$(WWCONFIGDIR) + test -f $(DESTDIR)$(SYSCONFDIR)/warewulf/wwapic.conf || install -m 644 etc/wwapic.conf $(DESTDIR)$(SYSCONFDIR)/warewulf/ + test -f $(DESTDIR)$(SYSCONFDIR)/warewulf/wwapid.conf || install -m 644 etc/wwapid.conf $(DESTDIR)$(SYSCONFDIR)/warewulf/ + test -f $(DESTDIR)$(SYSCONFDIR)/warewulf/wwapird.conf || install -m 644 etc/wwapird.conf $(DESTDIR)$(SYSCONFDIR)/warewulf/ cp -r etc/examples $(DESTDIR)$(WWCONFIGDIR)/ cp -r etc/ipxe $(DESTDIR)$(WWCONFIGDIR)/ cp -r overlays/* $(DESTDIR)$(WWOVERLAYDIR)/ @@ -180,6 +183,9 @@ files: all chmod 644 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/etc/ssh/ssh*.pub.ww chmod 750 $(DESTDIR)$(WWOVERLAYDIR)/host install -m 0755 wwctl $(DESTDIR)$(BINDIR) + install -m 0755 wwapic $(DESTDIR)$(BINDIR) + install -m 0755 wwapid $(DESTDIR)$(BINDIR) + install -m 0755 wwapird $(DESTDIR)$(BINDIR) install -m 0644 include/firewalld/warewulf.xml $(DESTDIR)$(FIREWALLDDIR) install -m 0644 include/systemd/warewulfd.service $(DESTDIR)$(SYSTEMDDIR) install -m 0644 LICENSE.md $(DESTDIR)$(WWDOCDIR) @@ -243,6 +249,33 @@ dist: vendor config cd .dist; tar -czf ../$(WAREWULF)-$(VERSION).tar.gz $(WAREWULF)-$(VERSION) rm -rf .dist +## wwapi generate code from protobuf. Requires protoc and protoc-grpc-gen-gateway to generate code. +## To setup latest protoc: +## Download the protobuf-all-[VERSION].tar.gz from https://github.com/protocolbuffers/protobuf/releases +## Extract the contents and change in the directory +## ./configure +## make +## make check +## sudo make install +## sudo ldconfig # refresh shared library cache. +## To setup protoc-gen-grpc-gateway, see https://github.com/grpc-ecosystem/grpc-gateway +proto: + protoc -I internal/pkg/api/routes/v1 -I=. \ + --grpc-gateway_out=. \ + --grpc-gateway_opt logtostderr=true \ + --go_out=. \ + --go-grpc_out=. \ + routes.proto + +wwapid: ## Build the grpc api server. + go build -o ./wwapid internal/app/api/wwapid/wwapid.go + +wwapic: ## Build the sample wwapi client. + go build -o ./wwapic internal/app/api/wwapic/wwapic.go + +wwapird: ## Build the rest api server (revese proxy to the grpc api server). + go build -o ./wwapird internal/app/api/wwapird/wwapird.go + clean: rm -f wwclient rm -f wwctl diff --git a/etc/wwapic.conf b/etc/wwapic.conf new file mode 100644 index 00000000..e16cc959 --- /dev/null +++ b/etc/wwapic.conf @@ -0,0 +1,9 @@ +# Configuration for wwapic, the grpc wwapi client. +api: + server: localhost + port: 9872 +tls: + enabled: true + cert: /usr/local/etc/warewulf/keys/wwapic/client.pem + key: /usr/local/etc/warewulf/keys/wwapic/client.key + cacert: /usr/local/etc/warewulf/keys/wwapic/cacert.pem \ No newline at end of file diff --git a/etc/wwapic.conf.in b/etc/wwapic.conf.in new file mode 100644 index 00000000..9bf8260d --- /dev/null +++ b/etc/wwapic.conf.in @@ -0,0 +1,9 @@ +# Configuration for wwapic, the grpc wwapi client. +api: + server: localhost + port: 9872 +tls: + enabled: true + cert: @SYSCONFDIR@/warewulf/keys/wwapic/client.pem + key: @SYSCONFDIR@/warewulf/keys/wwapic/client.key + cacert: @SYSCONFDIR@/warewulf/keys/wwapic/cacert.pem \ No newline at end of file diff --git a/etc/wwapid.conf b/etc/wwapid.conf new file mode 100644 index 00000000..b5ad8389 --- /dev/null +++ b/etc/wwapid.conf @@ -0,0 +1,10 @@ +# Configuration for wwapird, the wwapi grpc server. +api: + version: 1.0.0 + prefix: v1 + port: 9872 +tls: + enabled: true + cert: /usr/local/etc/warewulf/keys/wwapid/server.pem + key: /usr/local/etc/warewulf/keys/wwapid/server.key + cacert: /usr/local/etc/warewulf/keys/wwapid/cacert.pem \ No newline at end of file diff --git a/etc/wwapid.conf.in b/etc/wwapid.conf.in new file mode 100644 index 00000000..75ca7271 --- /dev/null +++ b/etc/wwapid.conf.in @@ -0,0 +1,10 @@ +# Configuration for wwapird, the wwapi grpc server. +api: + version: 1.0.0 + prefix: v1 + port: 9872 +tls: + enabled: true + cert: @SYSCONFDIR@/warewulf/keys/wwapid/server.pem + key: @SYSCONFDIR@/warewulf/keys/wwapid/server.key + cacert: @SYSCONFDIR@/warewulf/keys/wwapid/cacert.pem \ No newline at end of file diff --git a/etc/wwapird.conf b/etc/wwapird.conf new file mode 100644 index 00000000..d9714794 --- /dev/null +++ b/etc/wwapird.conf @@ -0,0 +1,20 @@ +# Configuration for wwapird, the grpc / http reverse proxy REST server. +# This server allows one to curl the wwapi. +clientapi: + server: localhost + port: 9872 +serverapi: + version: ignored + prefix: ignored + port: 9871 +clienttls: + enabled: true + cert: /usr/local/etc/warewulf/keys/wwapird/client.pem + key: /usr/local/etc/warewulf/keys/wwapird/client.key + cacert: /usr/local/etc/warewulf/keys/wwapird/cacert.pem +servertls: + enabled: true + cert: ignored + key: /usr/local/etc/warewulf/keys/server.key + cacert: ignored + concatcert: /usr/local/etc/warewulf/keys/serverAndCacert.pem # This is a cat of server.pem and cacert.pem diff --git a/etc/wwapird.conf.in b/etc/wwapird.conf.in new file mode 100644 index 00000000..1b037cde --- /dev/null +++ b/etc/wwapird.conf.in @@ -0,0 +1,20 @@ +# Configuration for wwapird, the grpc / http reverse proxy REST server. +# This server allows one to curl the wwapi. +clientapi: + server: localhost + port: 9872 +serverapi: + version: ignored + prefix: ignored + port: 9871 +clienttls: + enabled: true + cert: @SYSCONFDIR@/warewulf/keys/wwapird/client.pem + key: @SYSCONFDIR@/warewulf/keys/wwapird/client.key + cacert: @SYSCONFDIR@/warewulf/keys/wwapird/cacert.pem +servertls: + enabled: true + cert: ignored + key: @SYSCONFDIR@/warewulf/keys/server.key + cacert: ignored + concatcert: @SYSCONFDIR@/warewulf/keys/serverAndCacert.pem # This is a cat of server.pem and cacert.pem diff --git a/go.mod b/go.mod index ff13b90c..b921bcfc 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,9 @@ require ( github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e github.com/creasty/defaults v1.5.2 github.com/fatih/color v1.13.0 + github.com/golang/glog v1.0.0 github.com/google/uuid v1.1.2 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.0 github.com/manifoldco/promptui v0.8.0 github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 github.com/opencontainers/umoci v0.4.6 @@ -17,6 +19,10 @@ require ( github.com/spf13/cobra v1.1.1 github.com/stretchr/testify v1.7.0 github.com/talos-systems/go-smbios v0.1.1 - golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 + google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e + google.golang.org/grpc v1.45.0 + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 + google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 7d0741a2..a58cfc27 100644 --- a/go.sum +++ b/go.sum @@ -10,18 +10,28 @@ cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6T cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -70,6 +80,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apex/log v1.4.0 h1:jYWeNt9kWJOf1ifht8UjsCQ00eiPnFrUzCBCiiJMw/g= github.com/apex/log v1.4.0/go.mod h1:UMNC4vQNC7hb5gyr47r18ylK1n34rV7GO+gb0wpXvcE= github.com/apex/logs v0.0.7/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= @@ -119,6 +130,11 @@ github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLI github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= @@ -261,6 +277,8 @@ github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -307,6 +325,8 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -317,6 +337,8 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -330,8 +352,10 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -340,20 +364,26 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -375,6 +405,10 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.0 h1:ESEyqQqXXFIcImj/BE8oKEX37Zsuceb2cZI+EL/zNCY= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.0/go.mod h1:XnLCLFp3tjoZJszVKjfpyAK6J8sYIcQXWQxmqLWF21I= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -614,6 +648,7 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rootless-containers/proto v0.1.0 h1:gS1JOMEtk1YDYHCzBAf/url+olMJbac7MTrgSeP6zh4= github.com/rootless-containers/proto v0.1.0/go.mod h1:vgkUFZbQd0gcE/K/ZwtE4MYjZPu0UNHLXIQxhyqAFh8= @@ -670,6 +705,7 @@ github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRci github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -719,7 +755,9 @@ github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= @@ -735,6 +773,8 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -805,6 +845,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -816,17 +857,26 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -886,9 +936,15 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -901,17 +957,24 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -953,8 +1016,18 @@ golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -971,12 +1044,19 @@ google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -997,10 +1077,22 @@ google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e h1:fNKDNuUyC4WH+inqDMpfXDdfvwfYILbsX+oskGZ8hxg= +google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1013,9 +1105,17 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1025,8 +1125,11 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 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= @@ -1051,6 +1154,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1071,6 +1175,7 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= @@ -1091,3 +1196,5 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyz sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/google/api/annotations.proto b/google/api/annotations.proto new file mode 100644 index 00000000..85c361b4 --- /dev/null +++ b/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright (c) 2015, Google Inc. +// +// 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. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} diff --git a/google/api/client.proto b/google/api/client.proto new file mode 100644 index 00000000..3b3fd0c4 --- /dev/null +++ b/google/api/client.proto @@ -0,0 +1,99 @@ +// Copyright 2018 Google LLC +// +// 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. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "ClientProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // A definition of a client library method signature. + // + // In client libraries, each proto RPC corresponds to one or more methods + // which the end user is able to call, and calls the underlying RPC. + // Normally, this method receives a single argument (a struct or instance + // corresponding to the RPC request object). Defining this field will + // add one or more overloads providing flattened or simpler method signatures + // in some languages. + // + // The fields on the method signature are provided as a comma-separated + // string. + // + // For example, the proto RPC and annotation: + // + // rpc CreateSubscription(CreateSubscriptionRequest) + // returns (Subscription) { + // option (google.api.method_signature) = "name,topic"; + // } + // + // Would add the following Java overload (in addition to the method accepting + // the request object): + // + // public final Subscription createSubscription(String name, String topic) + // + // The following backwards-compatibility guidelines apply: + // + // * Adding this annotation to an unannotated method is backwards + // compatible. + // * Adding this annotation to a method which already has existing + // method signature annotations is backwards compatible if and only if + // the new method signature annotation is last in the sequence. + // * Modifying or removing an existing method signature annotation is + // a breaking change. + // * Re-ordering existing method signature annotations is a breaking + // change. + repeated string method_signature = 1051; +} + +extend google.protobuf.ServiceOptions { + // The hostname for this service. + // This should be specified with no prefix or protocol. + // + // Example: + // + // service Foo { + // option (google.api.default_host) = "foo.googleapi.com"; + // ... + // } + string default_host = 1049; + + // OAuth scopes needed for the client. + // + // Example: + // + // service Foo { + // option (google.api.oauth_scopes) = \ + // "https://www.googleapis.com/auth/cloud-platform"; + // ... + // } + // + // If there is more than one scope, use a comma-separated string: + // + // Example: + // + // service Foo { + // option (google.api.oauth_scopes) = \ + // "https://www.googleapis.com/auth/cloud-platform," + // "https://www.googleapis.com/auth/monitoring"; + // ... + // } + string oauth_scopes = 1050; +} diff --git a/google/api/http.proto b/google/api/http.proto new file mode 100644 index 00000000..2bd3a19b --- /dev/null +++ b/google/api/http.proto @@ -0,0 +1,318 @@ +// Copyright 2018 Google LLC +// +// 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. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parmeters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// `HttpRule` defines the mapping of an RPC method to one or more HTTP +// REST API methods. The mapping specifies how different portions of the RPC +// request message are mapped to URL path, URL query parameters, and +// HTTP request body. The mapping is typically specified as an +// `google.api.http` annotation on the RPC method, +// see "google/api/annotations.proto" for details. +// +// The mapping consists of a field specifying the path template and +// method kind. The path template can refer to fields in the request +// message, as in the example below which describes a REST GET +// operation on a resource collection of messages: +// +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http).get = "/v1/messages/{message_id}/{sub.subfield}"; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // mapped to the URL +// SubMessage sub = 2; // `sub.subfield` is url-mapped +// } +// message Message { +// string text = 1; // content of the resource +// } +// +// The same http annotation can alternatively be expressed inside the +// `GRPC API Configuration` YAML file. +// +// http: +// rules: +// - selector: .Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// This definition enables an automatic, bidrectional mapping of HTTP +// JSON to RPC. Example: +// +// HTTP | RPC +// -----|----- +// `GET /v1/messages/123456/foo` | `GetMessage(message_id: "123456" sub: SubMessage(subfield: "foo"))` +// +// In general, not only fields but also field paths can be referenced +// from a path pattern. Fields mapped to the path pattern cannot be +// repeated and must have a primitive (non-message) type. +// +// Any fields in the request message which are not bound by the path +// pattern automatically become (optional) HTTP query +// parameters. Assume the following definition of the request message: +// +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http).get = "/v1/messages/{message_id}"; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // mapped to the URL +// int64 revision = 2; // becomes a parameter +// SubMessage sub = 3; // `sub.subfield` becomes a parameter +// } +// +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | RPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))` +// +// Note that fields which are mapped to HTTP parameters must have a +// primitive type or a repeated primitive type. Message types are not +// allowed. In the case of a repeated type, the parameter can be +// repeated in the URL, as in `...?param=A¶m=B`. +// +// For HTTP method kinds which allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// put: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | RPC +// -----|----- +// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// put: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | RPC +// -----|----- +// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice of +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// +// This enables the following two alternative HTTP JSON to RPC +// mappings: +// +// HTTP | RPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: "123456")` +// +// # Rules for HTTP mapping +// +// The rules for mapping HTTP path, query parameters, and body fields +// to the request message are as follows: +// +// 1. The `body` field specifies either `*` or a field path, or is +// omitted. If omitted, it indicates there is no HTTP request body. +// 2. Leaf fields (recursive expansion of nested messages in the +// request) can be classified into three types: +// (a) Matched in the URL template. +// (b) Covered by body (if body is `*`, everything except (a) fields; +// else everything under the body field) +// (c) All other fields. +// 3. URL query parameters found in the HTTP request are mapped to (c) fields. +// 4. Any body sent with an HTTP request can contain only (b) fields. +// +// The syntax of the path template is as follows: +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single path segment. The syntax `**` matches zero +// or more path segments, which must be the last part of the path except the +// `Verb`. The syntax `LITERAL` matches literal text in the path. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path, all characters +// except `[-_.~0-9a-zA-Z]` are percent-encoded. Such variables show up in the +// Discovery Document as `{var}`. +// +// If a variable contains one or more path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path, all +// characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. Such variables +// show up in the Discovery Document as `{+var}`. +// +// NOTE: While the single segment variable matches the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 +// Simple String Expansion, the multi segment variable **does not** match +// RFC 6570 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. +// +// NOTE: the field paths in variables and in the `body` must not refer to +// repeated fields or map fields. +message HttpRule { + // Selects methods to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Used for listing and getting information about resources. + string get = 2; + + // Used for updating a resource. + string put = 3; + + // Used for creating a resource. + string post = 4; + + // Used for deleting a resource. + string delete = 5; + + // Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP body, or + // `*` for mapping all fields not captured by the path pattern to the HTTP + // body. NOTE: the referred field must not be a repeated field and must be + // present at the top-level of request message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // body of response. Other response fields are ignored. When + // not set, the response message will be used as HTTP body of response. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} diff --git a/google/bytestream/bytestream.proto b/google/bytestream/bytestream.proto new file mode 100644 index 00000000..814bdf3e --- /dev/null +++ b/google/bytestream/bytestream.proto @@ -0,0 +1,181 @@ +// Copyright 2016 Google Inc. +// +// 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. + +syntax = "proto3"; + +package google.bytestream; + +import "google/api/annotations.proto"; +import "google/protobuf/wrappers.proto"; + +option go_package = "google.golang.org/genproto/googleapis/bytestream;bytestream"; +option java_outer_classname = "ByteStreamProto"; +option java_package = "com.google.bytestream"; + +// #### Introduction +// +// The Byte Stream API enables a client to read and write a stream of bytes to +// and from a resource. Resources have names, and these names are supplied in +// the API calls below to identify the resource that is being read from or +// written to. +// +// All implementations of the Byte Stream API export the interface defined here: +// +// * `Read()`: Reads the contents of a resource. +// +// * `Write()`: Writes the contents of a resource. The client can call `Write()` +// multiple times with the same resource and can check the status of the write +// by calling `QueryWriteStatus()`. +// +// #### Service parameters and metadata +// +// The ByteStream API provides no direct way to access/modify any metadata +// associated with the resource. +// +// #### Errors +// +// The errors returned by the service are in the Google canonical error space. +service ByteStream { + // `Read()` is used to retrieve the contents of a resource as a sequence + // of bytes. The bytes are returned in a sequence of responses, and the + // responses are delivered as the results of a server-side streaming RPC. + rpc Read(ReadRequest) returns (stream ReadResponse); + + // `Write()` is used to send the contents of a resource as a sequence of + // bytes. The bytes are sent in a sequence of request protos of a client-side + // streaming RPC. + // + // A `Write()` action is resumable. If there is an error or the connection is + // broken during the `Write()`, the client should check the status of the + // `Write()` by calling `QueryWriteStatus()` and continue writing from the + // returned `committed_size`. This may be less than the amount of data the + // client previously sent. + // + // Calling `Write()` on a resource name that was previously written and + // finalized could cause an error, depending on whether the underlying service + // allows over-writing of previously written resources. + // + // When the client closes the request channel, the service will respond with + // a `WriteResponse`. The service will not view the resource as `complete` + // until the client has sent a `WriteRequest` with `finish_write` set to + // `true`. Sending any requests on a stream after sending a request with + // `finish_write` set to `true` will cause an error. The client **should** + // check the `WriteResponse` it receives to determine how much data the + // service was able to commit and whether the service views the resource as + // `complete` or not. + rpc Write(stream WriteRequest) returns (WriteResponse); + + // `QueryWriteStatus()` is used to find the `committed_size` for a resource + // that is being written, which can then be used as the `write_offset` for + // the next `Write()` call. + // + // If the resource does not exist (i.e., the resource has been deleted, or the + // first `Write()` has not yet reached the service), this method returns the + // error `NOT_FOUND`. + // + // The client **may** call `QueryWriteStatus()` at any time to determine how + // much data has been processed for this resource. This is useful if the + // client is buffering data and needs to know which data can be safely + // evicted. For any sequence of `QueryWriteStatus()` calls for a given + // resource name, the sequence of returned `committed_size` values will be + // non-decreasing. + rpc QueryWriteStatus(QueryWriteStatusRequest) + returns (QueryWriteStatusResponse); +} + +// Request object for ByteStream.Read. +message ReadRequest { + // The name of the resource to read. + string resource_name = 1; + + // The offset for the first byte to return in the read, relative to the start + // of the resource. + // + // A `read_offset` that is negative or greater than the size of the resource + // will cause an `OUT_OF_RANGE` error. + int64 read_offset = 2; + + // The maximum number of `data` bytes the server is allowed to return in the + // sum of all `ReadResponse` messages. A `read_limit` of zero indicates that + // there is no limit, and a negative `read_limit` will cause an error. + // + // If the stream returns fewer bytes than allowed by the `read_limit` and no + // error occurred, the stream includes all data from the `read_offset` to the + // end of the resource. + int64 read_limit = 3; +} + +// Response object for ByteStream.Read. +message ReadResponse { + // A portion of the data for the resource. The service **may** leave `data` + // empty for any given `ReadResponse`. This enables the service to inform the + // client that the request is still live while it is running an operation to + // generate more data. + bytes data = 10; +} + +// Request object for ByteStream.Write. +message WriteRequest { + // The name of the resource to write. This **must** be set on the first + // `WriteRequest` of each `Write()` action. If it is set on subsequent calls, + // it **must** match the value of the first request. + string resource_name = 1; + + // The offset from the beginning of the resource at which the data should be + // written. It is required on all `WriteRequest`s. + // + // In the first `WriteRequest` of a `Write()` action, it indicates + // the initial offset for the `Write()` call. The value **must** be equal to + // the `committed_size` that a call to `QueryWriteStatus()` would return. + // + // On subsequent calls, this value **must** be set and **must** be equal to + // the sum of the first `write_offset` and the sizes of all `data` bundles + // sent previously on this stream. + // + // An incorrect value will cause an error. + int64 write_offset = 2; + + // If `true`, this indicates that the write is complete. Sending any + // `WriteRequest`s subsequent to one in which `finish_write` is `true` will + // cause an error. + bool finish_write = 3; + + // A portion of the data for the resource. The client **may** leave `data` + // empty for any given `WriteRequest`. This enables the client to inform the + // service that the request is still live while it is running an operation to + // generate more data. + bytes data = 10; +} + +// Response object for ByteStream.Write. +message WriteResponse { + // The number of bytes that have been processed for the given resource. + int64 committed_size = 1; +} + +// Request object for ByteStream.QueryWriteStatus. +message QueryWriteStatusRequest { + // The name of the resource whose write status is being requested. + string resource_name = 1; +} + +// Response object for ByteStream.QueryWriteStatus. +message QueryWriteStatusResponse { + // The number of bytes that have been processed for the given resource. + int64 committed_size = 1; + + // `complete` is `true` only if the client has sent a `WriteRequest` with + // `finish_write` set to true, and the server has processed that request. + bool complete = 2; +} diff --git a/google/longrunning/operations.proto b/google/longrunning/operations.proto new file mode 100644 index 00000000..299eefb2 --- /dev/null +++ b/google/longrunning/operations.proto @@ -0,0 +1,247 @@ +// Copyright 2019 Google LLC. +// +// 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. + +syntax = "proto3"; + +package google.longrunning; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/protobuf/any.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/empty.proto"; +import "google/rpc/status.proto"; +import "google/protobuf/descriptor.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.LongRunning"; +option go_package = "google.golang.org/genproto/googleapis/longrunning;longrunning"; +option java_multiple_files = true; +option java_outer_classname = "OperationsProto"; +option java_package = "com.google.longrunning"; +option php_namespace = "Google\\LongRunning"; + +extend google.protobuf.MethodOptions { + // Additional information regarding long-running operations. + // In particular, this specifies the types that are returned from + // long-running operations. + // + // Required for methods that return `google.longrunning.Operation`; invalid + // otherwise. + google.longrunning.OperationInfo operation_info = 1049; +} + +// Manages long-running operations with an API service. +// +// When an API method normally takes long time to complete, it can be designed +// to return [Operation][google.longrunning.Operation] to the client, and the client can use this +// interface to receive the real response asynchronously by polling the +// operation resource, or pass the operation resource to another API (such as +// Google Cloud Pub/Sub API) to receive the response. Any API service that +// returns long-running operations should implement the `Operations` interface +// so developers can have a consistent client experience. +service Operations { + option (google.api.default_host) = "longrunning.googleapis.com"; + + // Lists operations that match the specified filter in the request. If the + // server doesn't support this method, it returns `UNIMPLEMENTED`. + // + // NOTE: the `name` binding allows API services to override the binding + // to use different resource name schemes, such as `users/*/operations`. To + // override the binding, API services can add a binding such as + // `"/v1/{name=users/*}/operations"` to their service configuration. + // For backwards compatibility, the default name includes the operations + // collection id, however overriding users must ensure the name binding + // is the parent resource, without the operations collection id. + rpc ListOperations(ListOperationsRequest) returns (ListOperationsResponse) { + option (google.api.http) = { + get: "/v1/{name=operations}" + }; + option (google.api.method_signature) = "name,filter"; + } + + // Gets the latest state of a long-running operation. Clients can use this + // method to poll the operation result at intervals as recommended by the API + // service. + rpc GetOperation(GetOperationRequest) returns (Operation) { + option (google.api.http) = { + get: "/v1/{name=operations/**}" + }; + option (google.api.method_signature) = "name"; + } + + // Deletes a long-running operation. This method indicates that the client is + // no longer interested in the operation result. It does not cancel the + // operation. If the server doesn't support this method, it returns + // `google.rpc.Code.UNIMPLEMENTED`. + rpc DeleteOperation(DeleteOperationRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/{name=operations/**}" + }; + option (google.api.method_signature) = "name"; + } + + // Starts asynchronous cancellation on a long-running operation. The server + // makes a best effort to cancel the operation, but success is not + // guaranteed. If the server doesn't support this method, it returns + // `google.rpc.Code.UNIMPLEMENTED`. Clients can use + // [Operations.GetOperation][google.longrunning.Operations.GetOperation] or + // other methods to check whether the cancellation succeeded or whether the + // operation completed despite cancellation. On successful cancellation, + // the operation is not deleted; instead, it becomes an operation with + // an [Operation.error][google.longrunning.Operation.error] value with a [google.rpc.Status.code][google.rpc.Status.code] of 1, + // corresponding to `Code.CANCELLED`. + rpc CancelOperation(CancelOperationRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/v1/{name=operations/**}:cancel" + body: "*" + }; + option (google.api.method_signature) = "name"; + } + + // Waits for the specified long-running operation until it is done or reaches + // at most a specified timeout, returning the latest state. If the operation + // is already done, the latest state is immediately returned. If the timeout + // specified is greater than the default HTTP/RPC timeout, the HTTP/RPC + // timeout is used. If the server does not support this method, it returns + // `google.rpc.Code.UNIMPLEMENTED`. + // Note that this method is on a best-effort basis. It may return the latest + // state before the specified timeout (including immediately), meaning even an + // immediate response is no guarantee that the operation is done. + rpc WaitOperation(WaitOperationRequest) returns (Operation) { + } +} + +// This resource represents a long-running operation that is the result of a +// network API call. +message Operation { + // The server-assigned name, which is only unique within the same service that + // originally returns it. If you use the default HTTP mapping, the + // `name` should be a resource name ending with `operations/{unique_id}`. + string name = 1; + + // Service-specific metadata associated with the operation. It typically + // contains progress information and common metadata such as create time. + // Some services might not provide such metadata. Any method that returns a + // long-running operation should document the metadata type, if any. + google.protobuf.Any metadata = 2; + + // If the value is `false`, it means the operation is still in progress. + // If `true`, the operation is completed, and either `error` or `response` is + // available. + bool done = 3; + + // The operation result, which can be either an `error` or a valid `response`. + // If `done` == `false`, neither `error` nor `response` is set. + // If `done` == `true`, exactly one of `error` or `response` is set. + oneof result { + // The error result of the operation in case of failure or cancellation. + google.rpc.Status error = 4; + + // The normal response of the operation in case of success. If the original + // method returns no data on success, such as `Delete`, the response is + // `google.protobuf.Empty`. If the original method is standard + // `Get`/`Create`/`Update`, the response should be the resource. For other + // methods, the response should have the type `XxxResponse`, where `Xxx` + // is the original method name. For example, if the original method name + // is `TakeSnapshot()`, the inferred response type is + // `TakeSnapshotResponse`. + google.protobuf.Any response = 5; + } +} + +// The request message for [Operations.GetOperation][google.longrunning.Operations.GetOperation]. +message GetOperationRequest { + // The name of the operation resource. + string name = 1; +} + +// The request message for [Operations.ListOperations][google.longrunning.Operations.ListOperations]. +message ListOperationsRequest { + // The name of the operation's parent resource. + string name = 4; + + // The standard list filter. + string filter = 1; + + // The standard list page size. + int32 page_size = 2; + + // The standard list page token. + string page_token = 3; +} + +// The response message for [Operations.ListOperations][google.longrunning.Operations.ListOperations]. +message ListOperationsResponse { + // A list of operations that matches the specified filter in the request. + repeated Operation operations = 1; + + // The standard List next-page token. + string next_page_token = 2; +} + +// The request message for [Operations.CancelOperation][google.longrunning.Operations.CancelOperation]. +message CancelOperationRequest { + // The name of the operation resource to be cancelled. + string name = 1; +} + +// The request message for [Operations.DeleteOperation][google.longrunning.Operations.DeleteOperation]. +message DeleteOperationRequest { + // The name of the operation resource to be deleted. + string name = 1; +} + +// The request message for [Operations.WaitOperation][google.longrunning.Operations.WaitOperation]. +message WaitOperationRequest { + // The name of the operation resource to wait on. + string name = 1; + + // The maximum duration to wait before timing out. If left blank, the wait + // will be at most the time permitted by the underlying HTTP/RPC protocol. + // If RPC context deadline is also specified, the shorter one will be used. + google.protobuf.Duration timeout = 2; +} + +// A message representing the message types used by a long-running operation. +// +// Example: +// +// rpc LongRunningRecognize(LongRunningRecognizeRequest) +// returns (google.longrunning.Operation) { +// option (google.longrunning.operation_info) = { +// response_type: "LongRunningRecognizeResponse" +// metadata_type: "LongRunningRecognizeMetadata" +// }; +// } +message OperationInfo { + // Required. The message name of the primary return type for this + // long-running operation. + // This type will be used to deserialize the LRO's response. + // + // If the response is in a different package from the rpc, a fully-qualified + // message name must be used (e.g. `google.protobuf.Struct`). + // + // Note: Altering this value constitutes a breaking change. + string response_type = 1; + + // Required. The message name of the metadata type for this long-running + // operation. + // + // If the response is in a different package from the rpc, a fully-qualified + // message name must be used (e.g. `google.protobuf.Struct`). + // + // Note: Altering this value constitutes a breaking change. + string metadata_type = 2; +} diff --git a/google/rpc/code.proto b/google/rpc/code.proto new file mode 100644 index 00000000..8fef4117 --- /dev/null +++ b/google/rpc/code.proto @@ -0,0 +1,186 @@ +// Copyright 2017 Google Inc. +// +// 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. + +syntax = "proto3"; + +package google.rpc; + +option go_package = "google.golang.org/genproto/googleapis/rpc/code;code"; +option java_multiple_files = true; +option java_outer_classname = "CodeProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + + +// The canonical error codes for Google APIs. +// +// +// Sometimes multiple error codes may apply. Services should return +// the most specific error code that applies. For example, prefer +// `OUT_OF_RANGE` over `FAILED_PRECONDITION` if both codes apply. +// Similarly prefer `NOT_FOUND` or `ALREADY_EXISTS` over `FAILED_PRECONDITION`. +enum Code { + // Not an error; returned on success + // + // HTTP Mapping: 200 OK + OK = 0; + + // The operation was cancelled, typically by the caller. + // + // HTTP Mapping: 499 Client Closed Request + CANCELLED = 1; + + // Unknown error. For example, this error may be returned when + // a `Status` value received from another address space belongs to + // an error space that is not known in this address space. Also + // errors raised by APIs that do not return enough error information + // may be converted to this error. + // + // HTTP Mapping: 500 Internal Server Error + UNKNOWN = 2; + + // The client specified an invalid argument. Note that this differs + // from `FAILED_PRECONDITION`. `INVALID_ARGUMENT` indicates arguments + // that are problematic regardless of the state of the system + // (e.g., a malformed file name). + // + // HTTP Mapping: 400 Bad Request + INVALID_ARGUMENT = 3; + + // The deadline expired before the operation could complete. For operations + // that change the state of the system, this error may be returned + // even if the operation has completed successfully. For example, a + // successful response from a server could have been delayed long + // enough for the deadline to expire. + // + // HTTP Mapping: 504 Gateway Timeout + DEADLINE_EXCEEDED = 4; + + // Some requested entity (e.g., file or directory) was not found. + // + // Note to server developers: if a request is denied for an entire class + // of users, such as gradual feature rollout or undocumented whitelist, + // `NOT_FOUND` may be used. If a request is denied for some users within + // a class of users, such as user-based access control, `PERMISSION_DENIED` + // must be used. + // + // HTTP Mapping: 404 Not Found + NOT_FOUND = 5; + + // The entity that a client attempted to create (e.g., file or directory) + // already exists. + // + // HTTP Mapping: 409 Conflict + ALREADY_EXISTS = 6; + + // The caller does not have permission to execute the specified + // operation. `PERMISSION_DENIED` must not be used for rejections + // caused by exhausting some resource (use `RESOURCE_EXHAUSTED` + // instead for those errors). `PERMISSION_DENIED` must not be + // used if the caller can not be identified (use `UNAUTHENTICATED` + // instead for those errors). This error code does not imply the + // request is valid or the requested entity exists or satisfies + // other pre-conditions. + // + // HTTP Mapping: 403 Forbidden + PERMISSION_DENIED = 7; + + // The request does not have valid authentication credentials for the + // operation. + // + // HTTP Mapping: 401 Unauthorized + UNAUTHENTICATED = 16; + + // Some resource has been exhausted, perhaps a per-user quota, or + // perhaps the entire file system is out of space. + // + // HTTP Mapping: 429 Too Many Requests + RESOURCE_EXHAUSTED = 8; + + // The operation was rejected because the system is not in a state + // required for the operation's execution. For example, the directory + // to be deleted is non-empty, an rmdir operation is applied to + // a non-directory, etc. + // + // Service implementors can use the following guidelines to decide + // between `FAILED_PRECONDITION`, `ABORTED`, and `UNAVAILABLE`: + // (a) Use `UNAVAILABLE` if the client can retry just the failing call. + // (b) Use `ABORTED` if the client should retry at a higher level + // (e.g., when a client-specified test-and-set fails, indicating the + // client should restart a read-modify-write sequence). + // (c) Use `FAILED_PRECONDITION` if the client should not retry until + // the system state has been explicitly fixed. E.g., if an "rmdir" + // fails because the directory is non-empty, `FAILED_PRECONDITION` + // should be returned since the client should not retry unless + // the files are deleted from the directory. + // + // HTTP Mapping: 400 Bad Request + FAILED_PRECONDITION = 9; + + // The operation was aborted, typically due to a concurrency issue such as + // a sequencer check failure or transaction abort. + // + // See the guidelines above for deciding between `FAILED_PRECONDITION`, + // `ABORTED`, and `UNAVAILABLE`. + // + // HTTP Mapping: 409 Conflict + ABORTED = 10; + + // The operation was attempted past the valid range. E.g., seeking or + // reading past end-of-file. + // + // Unlike `INVALID_ARGUMENT`, this error indicates a problem that may + // be fixed if the system state changes. For example, a 32-bit file + // system will generate `INVALID_ARGUMENT` if asked to read at an + // offset that is not in the range [0,2^32-1], but it will generate + // `OUT_OF_RANGE` if asked to read from an offset past the current + // file size. + // + // There is a fair bit of overlap between `FAILED_PRECONDITION` and + // `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific + // error) when it applies so that callers who are iterating through + // a space can easily look for an `OUT_OF_RANGE` error to detect when + // they are done. + // + // HTTP Mapping: 400 Bad Request + OUT_OF_RANGE = 11; + + // The operation is not implemented or is not supported/enabled in this + // service. + // + // HTTP Mapping: 501 Not Implemented + UNIMPLEMENTED = 12; + + // Internal errors. This means that some invariants expected by the + // underlying system have been broken. This error code is reserved + // for serious errors. + // + // HTTP Mapping: 500 Internal Server Error + INTERNAL = 13; + + // The service is currently unavailable. This is most likely a + // transient condition, which can be corrected by retrying with + // a backoff. + // + // See the guidelines above for deciding between `FAILED_PRECONDITION`, + // `ABORTED`, and `UNAVAILABLE`. + // + // HTTP Mapping: 503 Service Unavailable + UNAVAILABLE = 14; + + // Unrecoverable data loss or corruption. + // + // HTTP Mapping: 500 Internal Server Error + DATA_LOSS = 15; +} diff --git a/google/rpc/error_details.proto b/google/rpc/error_details.proto new file mode 100644 index 00000000..a8eadd6c --- /dev/null +++ b/google/rpc/error_details.proto @@ -0,0 +1,247 @@ +// Copyright 2020 Google LLC +// +// 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. + +syntax = "proto3"; + +package google.rpc; + +import "google/protobuf/duration.proto"; + +option go_package = "google.golang.org/genproto/googleapis/rpc/errdetails;errdetails"; +option java_multiple_files = true; +option java_outer_classname = "ErrorDetailsProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + +// Describes when the clients can retry a failed request. Clients could ignore +// the recommendation here or retry when this information is missing from error +// responses. +// +// It's always recommended that clients should use exponential backoff when +// retrying. +// +// Clients should wait until `retry_delay` amount of time has passed since +// receiving the error response before retrying. If retrying requests also +// fail, clients should use an exponential backoff scheme to gradually increase +// the delay between retries based on `retry_delay`, until either a maximum +// number of retries have been reached or a maximum retry delay cap has been +// reached. +message RetryInfo { + // Clients should wait at least this long between retrying the same request. + google.protobuf.Duration retry_delay = 1; +} + +// Describes additional debugging info. +message DebugInfo { + // The stack trace entries indicating where the error occurred. + repeated string stack_entries = 1; + + // Additional debugging information provided by the server. + string detail = 2; +} + +// Describes how a quota check failed. +// +// For example if a daily limit was exceeded for the calling project, +// a service could respond with a QuotaFailure detail containing the project +// id and the description of the quota limit that was exceeded. If the +// calling project hasn't enabled the service in the developer console, then +// a service could respond with the project id and set `service_disabled` +// to true. +// +// Also see RetryInfo and Help types for other details about handling a +// quota failure. +message QuotaFailure { + // A message type used to describe a single quota violation. For example, a + // daily quota or a custom quota that was exceeded. + message Violation { + // The subject on which the quota check failed. + // For example, "clientip:" or "project:". + string subject = 1; + + // A description of how the quota check failed. Clients can use this + // description to find more about the quota configuration in the service's + // public documentation, or find the relevant quota limit to adjust through + // developer console. + // + // For example: "Service disabled" or "Daily Limit for read operations + // exceeded". + string description = 2; + } + + // Describes all quota violations. + repeated Violation violations = 1; +} + +// Describes the cause of the error with structured details. +// +// Example of an error when contacting the "pubsub.googleapis.com" API when it +// is not enabled: +// { "reason": "API_DISABLED" +// "domain": "googleapis.com" +// "metadata": { +// "resource": "projects/123", +// "service": "pubsub.googleapis.com" +// } +// } +// This response indicates that the pubsub.googleapis.com API is not enabled. +// +// Example of an error that is returned when attempting to create a Spanner +// instance in a region that is out of stock: +// { "reason": "STOCKOUT" +// "domain": "spanner.googleapis.com", +// "metadata": { +// "availableRegions": "us-central1,us-east2" +// } +// } +// +message ErrorInfo { + // The reason of the error. This is a constant value that identifies the + // proximate cause of the error. Error reasons are unique within a particular + // domain of errors. This should be at most 63 characters and match + // /[A-Z0-9_]+/. + string reason = 1; + + // The logical grouping to which the "reason" belongs. Often "domain" will + // contain the registered service name of the tool or product that is the + // source of the error. Example: "pubsub.googleapis.com". If the error is + // common across many APIs, the first segment of the example above will be + // omitted. The value will be, "googleapis.com". + string domain = 2; + + // Additional structured details about this error. + // + // Keys should match /[a-zA-Z0-9-_]/ and be limited to 64 characters in + // length. When identifying the current value of an exceeded limit, the units + // should be contained in the key, not the value. For example, rather than + // {"instanceLimit": "100/request"}, should be returned as, + // {"instanceLimitPerRequest": "100"}, if the client exceeds the number of + // instances that can be created in a single (batch) request. + map metadata = 3; +} + +// Describes what preconditions have failed. +// +// For example, if an RPC failed because it required the Terms of Service to be +// acknowledged, it could list the terms of service violation in the +// PreconditionFailure message. +message PreconditionFailure { + // A message type used to describe a single precondition failure. + message Violation { + // The type of PreconditionFailure. We recommend using a service-specific + // enum type to define the supported precondition violation subjects. For + // example, "TOS" for "Terms of Service violation". + string type = 1; + + // The subject, relative to the type, that failed. + // For example, "google.com/cloud" relative to the "TOS" type would indicate + // which terms of service is being referenced. + string subject = 2; + + // A description of how the precondition failed. Developers can use this + // description to understand how to fix the failure. + // + // For example: "Terms of service not accepted". + string description = 3; + } + + // Describes all precondition violations. + repeated Violation violations = 1; +} + +// Describes violations in a client request. This error type focuses on the +// syntactic aspects of the request. +message BadRequest { + // A message type used to describe a single bad request field. + message FieldViolation { + // A path leading to a field in the request body. The value will be a + // sequence of dot-separated identifiers that identify a protocol buffer + // field. E.g., "field_violations.field" would identify this field. + string field = 1; + + // A description of why the request element is bad. + string description = 2; + } + + // Describes all violations in a client request. + repeated FieldViolation field_violations = 1; +} + +// Contains metadata about the request that clients can attach when filing a bug +// or providing other forms of feedback. +message RequestInfo { + // An opaque string that should only be interpreted by the service generating + // it. For example, it can be used to identify requests in the service's logs. + string request_id = 1; + + // Any data that was used to serve this request. For example, an encrypted + // stack trace that can be sent back to the service provider for debugging. + string serving_data = 2; +} + +// Describes the resource that is being accessed. +message ResourceInfo { + // A name for the type of resource being accessed, e.g. "sql table", + // "cloud storage bucket", "file", "Google calendar"; or the type URL + // of the resource: e.g. "type.googleapis.com/google.pubsub.v1.Topic". + string resource_type = 1; + + // The name of the resource being accessed. For example, a shared calendar + // name: "example.com_4fghdhgsrgh@group.calendar.google.com", if the current + // error is + // [google.rpc.Code.PERMISSION_DENIED][google.rpc.Code.PERMISSION_DENIED]. + string resource_name = 2; + + // The owner of the resource (optional). + // For example, "user:" or "project:". + string owner = 3; + + // Describes what error is encountered when accessing this resource. + // For example, updating a cloud project may require the `writer` permission + // on the developer console project. + string description = 4; +} + +// Provides links to documentation or for performing an out of band action. +// +// For example, if a quota check failed with an error indicating the calling +// project hasn't enabled the accessed service, this can contain a URL pointing +// directly to the right place in the developer console to flip the bit. +message Help { + // Describes a URL link. + message Link { + // Describes what the link offers. + string description = 1; + + // The URL of the link. + string url = 2; + } + + // URL(s) pointing to additional information on handling the current error. + repeated Link links = 1; +} + +// Provides a localized error message that is safe to return to the user +// which can be attached to an RPC error. +message LocalizedMessage { + // The locale used following the specification defined at + // http://www.rfc-editor.org/rfc/bcp/bcp47.txt. + // Examples are: "en-US", "fr-CH", "es-MX" + string locale = 1; + + // The localized error message in the above locale. + string message = 2; +} \ No newline at end of file diff --git a/google/rpc/status.proto b/google/rpc/status.proto new file mode 100644 index 00000000..0839ee96 --- /dev/null +++ b/google/rpc/status.proto @@ -0,0 +1,92 @@ +// Copyright 2017 Google Inc. +// +// 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. + +syntax = "proto3"; + +package google.rpc; + +import "google/protobuf/any.proto"; + +option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; +option java_multiple_files = true; +option java_outer_classname = "StatusProto"; +option java_package = "com.google.rpc"; +option objc_class_prefix = "RPC"; + + +// The `Status` type defines a logical error model that is suitable for different +// programming environments, including REST APIs and RPC APIs. It is used by +// [gRPC](https://github.com/grpc). The error model is designed to be: +// +// - Simple to use and understand for most users +// - Flexible enough to meet unexpected needs +// +// # Overview +// +// The `Status` message contains three pieces of data: error code, error message, +// and error details. The error code should be an enum value of +// [google.rpc.Code][google.rpc.Code], but it may accept additional error codes if needed. The +// error message should be a developer-facing English message that helps +// developers *understand* and *resolve* the error. If a localized user-facing +// error message is needed, put the localized message in the error details or +// localize it in the client. The optional error details may contain arbitrary +// information about the error. There is a predefined set of error detail types +// in the package `google.rpc` that can be used for common error conditions. +// +// # Language mapping +// +// The `Status` message is the logical representation of the error model, but it +// is not necessarily the actual wire format. When the `Status` message is +// exposed in different client libraries and different wire protocols, it can be +// mapped differently. For example, it will likely be mapped to some exceptions +// in Java, but more likely mapped to some error codes in C. +// +// # Other uses +// +// The error model and the `Status` message can be used in a variety of +// environments, either with or without APIs, to provide a +// consistent developer experience across different environments. +// +// Example uses of this error model include: +// +// - Partial errors. If a service needs to return partial errors to the client, +// it may embed the `Status` in the normal response to indicate the partial +// errors. +// +// - Workflow errors. A typical workflow has multiple steps. Each step may +// have a `Status` message for error reporting. +// +// - Batch operations. If a client uses batch request and batch response, the +// `Status` message should be used directly inside batch response, one for +// each error sub-response. +// +// - Asynchronous operations. If an API call embeds asynchronous operation +// results in its response, the status of those operations should be +// represented directly using the `Status` message. +// +// - Logging. If some API errors are stored in logs, the message `Status` could +// be used directly after any stripping needed for security/privacy reasons. +message Status { + // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + int32 code = 1; + + // A developer-facing error message, which should be in English. Any + // user-facing error message should be localized and sent in the + // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + string message = 2; + + // A list of messages that carry the error details. There is a common set of + // message types for APIs to use. + repeated google.protobuf.Any details = 3; +} diff --git a/internal/app/api/wwapic/README.md b/internal/app/api/wwapic/README.md new file mode 100644 index 00000000..2ea04b4c --- /dev/null +++ b/internal/app/api/wwapic/README.md @@ -0,0 +1,4 @@ +# wwapiclient is intended as and example grpc wwapid client. +Run wwapid to start the server +```./wwapic``` will get the version. +This works with or without mTLS. \ No newline at end of file diff --git a/internal/app/api/wwapic/wwapic.go b/internal/app/api/wwapic/wwapic.go new file mode 100644 index 00000000..f4c09a63 --- /dev/null +++ b/internal/app/api/wwapic/wwapic.go @@ -0,0 +1,89 @@ +package main + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "log" + "path" + "time" + + "github.com/hpcng/warewulf/internal/pkg/api/apiconfig" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/buildconfig" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/types/known/emptypb" +) + +// wwapic is intended as a sample wwapi client. + +func main() { + log.Println("Client running") + + // Read the config file. + config, err := apiconfig.NewClient(path.Join(buildconfig.SYSCONFDIR(), "warewulf/wwapic.conf")) + if err != nil { + log.Fatalf("err: %v", err) + } + + var opts []grpc.DialOption + if config.TlsConfig.Enabled { + + // Load the client cert and its key + clientCert, err := tls.LoadX509KeyPair(config.TlsConfig.Cert, config.TlsConfig.Key) + if err != nil { + log.Fatalf("Failed to load client cert and key. %s.", err) + } + + // Load the CA cert. + var cacert []byte + cacert, err = ioutil.ReadFile(config.TlsConfig.CaCert) + if err != nil { + log.Fatalf("Failed to load cacert. err: %s\n", err) + } + + // Put the CA cert into the cert pool. + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(cacert) { + log.Fatalf("Failed to append CA cert to certificate pool. %s.", err) + } + + // Create the TLS configuration + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{clientCert}, + RootCAs: certPool, + MinVersion: tls.VersionTLS13, + MaxVersion: tls.VersionTLS13, + } + + // Create TLS credentials from the TLS configuration + creds := credentials.NewTLS(tlsConfig) + opts = append(opts, grpc.DialOption(grpc.WithTransportCredentials(creds))) + } else { + opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) + } + + conn, err := grpc.Dial(fmt.Sprintf("%s:%v", config.ApiConfig.Server, config.ApiConfig.Port), opts...) + if err != nil { + log.Fatalln(err) + } + defer conn.Close() + + client := wwapiv1.NewWWApiClient(conn) + request := &emptypb.Empty{} + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + response, err := client.Version(ctx, request) + if err != nil { + log.Fatalln(err) + } + + log.Printf("Version Response: %v\n", response) +} \ No newline at end of file diff --git a/internal/app/api/wwapid/README.md b/internal/app/api/wwapid/README.md new file mode 100644 index 00000000..0c0a18dc --- /dev/null +++ b/internal/app/api/wwapid/README.md @@ -0,0 +1,7 @@ +# Warewulf API Daemon + +wwapid is a grpc service serving the Warewulf API. For v1, the intent is to serve the same interface as wwctl serves. For this preview PR, we are serving much of ```wwctl node``` and ```wwctl container```. + +Initial security is by mTLS. For development we are generating our own keys. A good tutorial for this is here: https://www.handracs.info/blog/grpcmtlsgo/ + +The configuration file is wwapid.conf. \ No newline at end of file diff --git a/internal/app/api/wwapid/wwapid.go b/internal/app/api/wwapid/wwapid.go new file mode 100644 index 00000000..3ae7bf8c --- /dev/null +++ b/internal/app/api/wwapid/wwapid.go @@ -0,0 +1,343 @@ +package main + +import ( + "bufio" + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "log" + "net" + "os" + "path" + + "github.com/hpcng/warewulf/internal/pkg/api/apiconfig" + "github.com/hpcng/warewulf/internal/pkg/api/container" + "github.com/hpcng/warewulf/internal/pkg/api/node" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/buildconfig" + "github.com/hpcng/warewulf/internal/pkg/version" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" +) + +type apiServer struct { + wwapiv1.UnimplementedWWApiServer +} + +var apiPrefix string +var apiVersion string + +func main() { + log.Println("Server running") + + // Read the config file. + config, err := apiconfig.NewServer(path.Join(buildconfig.SYSCONFDIR(), "warewulf/wwapid.conf")) + if err != nil { + log.Fatalf("err: %v", err) + } + + // Pull out config variables. + apiPrefix = config.ApiConfig.Prefix + apiVersion = config.ApiConfig.Version + servicePort := config.ApiConfig.Port + portString := fmt.Sprintf(":%d", servicePort) + + var opts []grpc.ServerOption + if !config.TlsConfig.Enabled { + insecureMode() + } else { + + // Setup TLS. + serverCert, err := tls.LoadX509KeyPair(config.TlsConfig.Cert, config.TlsConfig.Key) + if err != nil { + log.Fatalf("Failed to load server cert and key. err: %s\n", err) + } + + // Load the CA cert. + var cacert []byte + cacert, err = ioutil.ReadFile(config.TlsConfig.CaCert) + if err != nil { + log.Fatalf("Failed to load cacert. err: %s\n", err) + } + + // Put the CA cert into the cert pool. + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(cacert) { + log.Fatalf("Failed to append CA cert to certificate pool. %s.", err) + } + + // Create the TLS configuration + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{serverCert}, + RootCAs: certPool, + ClientCAs: certPool, + MinVersion: tls.VersionTLS13, + MaxVersion: tls.VersionTLS13, + } + + // Create TLS credentials from the TLS configuration + creds := credentials.NewTLS(tlsConfig) + opts = []grpc.ServerOption{grpc.Creds(creds)} + } + + listen, err := net.Listen("tcp", portString) + if err != nil { + log.Fatalln(err) + } + defer func() { + listen.Close() + }() + + grpcServer := grpc.NewServer(opts...) + wwapiv1.RegisterWWApiServer(grpcServer, &apiServer{}) + log.Fatalln(grpcServer.Serve(listen)) +} + +// private helpers + +// insecureMode creates a blocking prompt for customers running wwapid in insecure mode. +// It's a deterrent. Setup TLS. +func insecureMode() { + + fmt.Println("*** Running wwapid in INSECURE mode. *** THIS IS DANGEROUS! *** Enter y to continue. ***") + reader := bufio.NewReader(os.Stdin) + result, err := reader.ReadString('\n') + + if err != nil { + fmt.Printf("Fatal error: %v\n", err) + } + + if !(result == "y\n") { + os.Exit(1) + } + + fmt.Printf("wwapid running IN INSECURE MODE\n") +} + +// Api implementation. + +// ContainerBuild builds one or more containers. +func (s *apiServer) ContainerBuild(ctx context.Context, request *wwapiv1.ContainerBuildParameter) (response *wwapiv1.ContainerListResponse, err error) { + + // Parameter checks. + if request == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request") + } + + if request.ContainerNames == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request.ContainerNames") + } + + // Build the container. + err = container.ContainerBuild(request) + if err != nil { + return + } + + // Return the built containers. (A REST POST returns what is modified.) + var containers []*wwapiv1.ContainerInfo + containers, err = container.ContainerList() + if err != nil { + return + } + + response = &wwapiv1.ContainerListResponse{} + for i := 0; i < len(containers); i++ { + for j := 0; j < len(request.ContainerNames); j++ { + if containers[i].Name == request.ContainerNames[j] { + response.Containers = append(response.Containers, containers[i]) + } + } + } + return +} + +// ContainerDelete deletes one or more containers from Warewulf. +func (s *apiServer) ContainerDelete(ctx context.Context, request *wwapiv1.ContainerDeleteParameter) (response *emptypb.Empty, err error) { + + // Parameter checks. + if request == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request") + } + + if request.ContainerNames == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request.ContainerNames") + } + + err = container.ContainerDelete(request) + return +} + +func (s *apiServer) ContainerImport(ctx context.Context, request *wwapiv1.ContainerImportParameter) (response *wwapiv1.ContainerListResponse, err error) { + + // Import the container. + var containerName string + containerName, err = container.ContainerImport(request) + if err != nil { + return + } + + // Return the imported container to the client. + var containers []*wwapiv1.ContainerInfo + containers, err = container.ContainerList() + if err != nil { + return + } + + // Container name may have been shimmed in during import, + // which is why ContainerImport returns it. + for i := 0; i < len(containers); i++ { + if containerName == containers[i].Name { + response = &wwapiv1.ContainerListResponse{ + Containers: []*wwapiv1.ContainerInfo{containers[i]}, + } + return + } + } + return +} + +// ContainerList returns details about containers. +func (s *apiServer) ContainerList(ctx context.Context, request *emptypb.Empty) (response *wwapiv1.ContainerListResponse, err error) { + + var containers []*wwapiv1.ContainerInfo + containers, err = container.ContainerList() + if err != nil { + return + } + + response = &wwapiv1.ContainerListResponse{ + Containers: containers, + } + return +} + +// ContainerShow returns details about containers. +func (s *apiServer) ContainerShow(ctx context.Context, request *wwapiv1.ContainerShowParameter) (response *wwapiv1.ContainerShowResponse, err error) { + + // Parameter checks. + if request == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request") + } + + return container.ContainerShow(request) +} + +// NodeAdd adds one or more nodes for management by Warewulf and returns the added nodes. +func (s *apiServer) NodeAdd(ctx context.Context, request *wwapiv1.NodeAddParameter) (response *wwapiv1.NodeListResponse, err error) { + + // Parameter checks. + if request == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request") + } + + if request.NodeNames == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request.NodeNames") + } + + // Add the node(s). + err = node.NodeAdd(request) + if err != nil { + return + } + + // Return the added nodes as per REST. + return s.nodeListInternal(request.NodeNames) +} + +// NodeDelete deletes one or more nodes for removal of management by Warewulf. +func (s *apiServer) NodeDelete(ctx context.Context, request *wwapiv1.NodeDeleteParameter) (response *emptypb.Empty, err error) { + + response = new(emptypb.Empty) + + // Parameter checks. + if request == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request") + } + + if request.NodeNames == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request.NodeNames") + } + + err = node.NodeDelete(request) + return +} + +// NodeList returns details about zero or more nodes. +func (s *apiServer) NodeList(ctx context.Context, request *wwapiv1.NodeNames) (response *wwapiv1.NodeListResponse, err error) { + + // Parameter checks. request.NodeNames can be nil. + if request == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request") + } + + // Perform the list. + return s.nodeListInternal(request.NodeNames) +} + +// NodeSet updates fields for zero or more nodes and returns the updated nodes. +func (s *apiServer) NodeSet(ctx context.Context, request *wwapiv1.NodeSetParameter) (response *wwapiv1.NodeListResponse, err error) { + + // Parameter checks. + if request == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request") + } + + if request.NodeNames == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request.NodeNames") + } + + // Perform the NodeSet. + err = node.NodeSet(request) + if err != nil { + return + } + + // Return the updated nodes as per REST. + return s.nodeListInternal(request.NodeNames) +} + +func (s *apiServer) NodeStatus(ctx context.Context, request *wwapiv1.NodeNames) (response *wwapiv1.NodeStatusResponse, err error) { + + // Parameter checks. request.NodeNames can be nil. + if request == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request") + } + + return node.NodeStatus(request.NodeNames) +} + +// Version returns the versions of the wwapiv1 and warewulf as well as the api +// prefix for http routes. +func (s *apiServer) Version(ctx context.Context, request *emptypb.Empty) (response *wwapiv1.VersionResponse, err error) { + + response = &wwapiv1.VersionResponse{ + ApiPrefix: apiPrefix, + ApiVersion: apiVersion, + WarewulfVersion: version.GetVersion(), + } + return +} + +// Private helpers. + +// nodeListInternal calls NodeList and returns NodeListResponse. +// This does not contain parameter checks. +func (s *apiServer) nodeListInternal(nodeNames []string) (response *wwapiv1.NodeListResponse, err error) { + + var nodes []*wwapiv1.NodeInfo + nodes, err = node.NodeList(nodeNames) + if err != nil { + return + } + + response = &wwapiv1.NodeListResponse{ + Nodes: nodes, + } + return +} diff --git a/internal/app/api/wwapird/README.md b/internal/app/api/wwapird/README.md new file mode 100644 index 00000000..6c75ca0e --- /dev/null +++ b/internal/app/api/wwapird/README.md @@ -0,0 +1,9 @@ +# Warewulf API Rest Daemon + +wwapird is a client of wwapid serving a REST interface for the Warewulf API. For v1, the intent is to serve the same interface as wwctl serves. For this preview PR, we are serving much of ```wwctl node``` and ```wwctl container```. + +Initial security is by mTLS. For development we are generating our own keys. A good tutorial for this is here: https://www.handracs.info/blog/grpcmtlsgo/ + +The configuration file is wwapird.conf. + +Sample cURLs with and without mTLS are in curl.sh. This file is just a scratchpad for examples. \ No newline at end of file diff --git a/internal/app/api/wwapird/wwapird.go b/internal/app/api/wwapird/wwapird.go new file mode 100644 index 00000000..d7984914 --- /dev/null +++ b/internal/app/api/wwapird/wwapird.go @@ -0,0 +1,112 @@ +package main + +import ( + "context" + "crypto/tls" + "crypto/x509" + + "flag" + "fmt" + "log" + "io/ioutil" + "net/http" + + "github.com/golang/glog" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + + "github.com/hpcng/warewulf/internal/pkg/api/apiconfig" + + gw "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + + "github.com/hpcng/warewulf/internal/pkg/buildconfig" + "path" +) + +func run() error { + + log.Println("test0") + + // Read the config file. + config, err := apiconfig.NewClientServer(path.Join(buildconfig.SYSCONFDIR(), "warewulf/wwapird.conf")) + if err != nil { + glog.Fatalf("Failed to read config file, err: %v", err) + } + grpcServerEndpoint := fmt.Sprintf("%s:%v", config.ClientApiConfig.Server, config.ClientApiConfig.Port) + httpServerEndpoint := fmt.Sprintf(":%v", config.ServerApiConfig.Port) + + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + // Register gRPC server endpoint (we are the client) + // Note: Make sure the gRPC server is running properly and accessible + mux := runtime.NewServeMux() + + var opts []grpc.DialOption + if config.ClientTlsConfig.Enabled { + + // Load the client cert and its key + clientCert, err := tls.LoadX509KeyPair(config.ClientTlsConfig.Cert, config.ClientTlsConfig.Key) + if err != nil { + log.Fatalf("Failed to load client cert and key. %s.", err) + } + + // Load the CA cert. + var cacert []byte + cacert, err = ioutil.ReadFile(config.ClientTlsConfig.CaCert) + if err != nil { + log.Fatalf("Failed to load cacert. err: %s\n", err) + } + + // Put the CA cert into the cert pool. + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(cacert) { + log.Fatalf("Failed to append CA cert to certificate pool. %s.", err) + } + + // Create the TLS configuration + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{clientCert}, + RootCAs: certPool, + MinVersion: tls.VersionTLS13, + MaxVersion: tls.VersionTLS13, + } + + // Create TLS credentials from the TLS configuration + creds := credentials.NewTLS(tlsConfig) + opts = append(opts, grpc.DialOption(grpc.WithTransportCredentials(creds))) + + } else { + opts = []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} + } + + err = gw.RegisterWWApiHandlerFromEndpoint(ctx, mux, grpcServerEndpoint, opts) + if err != nil { + return err + } + + // Start HTTP server (and proxy calls to gRPC server endpoint) + if config.ServerTlsConfig.Enabled { + + return http.ListenAndServeTLS( + httpServerEndpoint, + config.ServerTlsConfig.ConcatCert, + config.ServerTlsConfig.Key, + mux) + } + + // Insecure + return http.ListenAndServe(httpServerEndpoint, mux) +} + +func main() { + flag.Parse() // Pretty sure glog wants this. + defer glog.Flush() + + if err := run(); err != nil { + glog.Fatal(err) + } +} \ No newline at end of file diff --git a/internal/app/api/wwapird_curls/curls.sh b/internal/app/api/wwapird_curls/curls.sh new file mode 100644 index 00000000..dcb2049c --- /dev/null +++ b/internal/app/api/wwapird_curls/curls.sh @@ -0,0 +1,57 @@ +#! /usr/bin/env bash + +# This file is a scratchpad for curling wwapird. + +# version +curl http://localhost:9871/version + +# secure version +curl --cacert /usr/local/etc/warewulf/keys/cacert.pem \ + --key /usr/local/etc/warewulf/keys/client.key \ + --cert /usr/local/etc/warewulf/keys/client.pem \ + https://localhost:9871/version + +# container list all +curl http://localhost:9871/v1/container + +# container import +curl -d '{"source": "docker://warewulf/rocky:8", "name": "rocky-8", "update": true, "default": true}' -H "Content-Type: application/json" -X POST http://localhost:9871/v1/container + +# container delete +curl -X DELETE http://localhost:9871/v1/container?containerNames=rocky-8 + +# container build +curl -d '{"containerNames": ["rocky-8"], "force": true}' -H "Content-Type: application/json" -X POST http://localhost:9871/v1/containerbuild + +# node list all +curl http://localhost:9871/v1/node + +# node list one +curl http://localhost:9871/v1/node?nodeNames=testnode1 # this works! case sensitive + +# This is a list of testnode[1-2] with URL escapes. +curl http://localhost:9871/v1/node?nodeNames=testnode%5B1-2%5D + +# node add single discoverable node +curl -d '{"nodeNames": ["testApiNode0"], "discoverable": true}' -H "Content-Type: application/json" -X POST http://localhost:9871/v1/node + +curl -d '{"nodeNames": ["testApiNode1"], "discoverable": true}' -H "Content-Type: application/json" -X POST http://localhost:9871/v1/node + + +# list the node we just added +curl http://localhost:9871/v1/node?nodeNames=testApiNode0 + +# This gets me a little farther, but still no param data: +curl -d '{"nodeNames": ["testApiNode0"], "ipmiIpAddr": "10.0.8.220", "updateMask": "ipmiIpAddr,nodeNames"}' -H "Content-Type: application/json" -X PATCH http://localhost:9871/v1/node + +# Node set with post: +curl -d '{"nodeNames": ["testApiNode0"], "ipmiIpaddr": "6.7.8.9"}' -H "Content-Type: application/json" -X POST http://localhost:9871/v1/nodeset + +# node status +curl http://localhost:9871/v1/nodestatus + +curl http://localhost:9871/v1/nodestatus?nodeNames=testApiNode0 + +# node delete single node +curl -X DELETE http://localhost:9871/v1/node?nodeNames=testApiNode0 +curl -X DELETE http://localhost:9871/v1/node?nodeNames=testApiNode1 \ No newline at end of file diff --git a/internal/app/wwctl/container/build/main.go b/internal/app/wwctl/container/build/main.go index f739a0b5..2ed3fd89 100644 --- a/internal/app/wwctl/container/build/main.go +++ b/internal/app/wwctl/container/build/main.go @@ -1,73 +1,17 @@ package build import ( - "fmt" - "os" - - "github.com/hpcng/warewulf/internal/pkg/container" - "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/wwlog" - "github.com/pkg/errors" + "github.com/hpcng/warewulf/internal/pkg/api/container" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" "github.com/spf13/cobra" ) func CobraRunE(cmd *cobra.Command, args []string) error { - var containers []string - - if BuildAll { - containers, _ = container.ListSources() - } else { - containers = args + cbp := &wwapiv1.ContainerBuildParameter{ + ContainerNames: args, + Force: BuildForce, + All: BuildAll, + Default: SetDefault, } - - if len(containers) == 0 { - fmt.Println(cmd.Help()) - os.Exit(0) - } - - for _, c := range containers { - if !container.ValidSource(c) { - wwlog.Printf(wwlog.ERROR, "VNFS name does not exist: %s\n", c) - os.Exit(1) - } - - err := container.Build(c, BuildForce) - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not build container %s: %s\n", c, err) - os.Exit(1) - } - } - - if SetDefault { - if len(containers) != 1 { - wwlog.Printf(wwlog.ERROR, "Can only set default for one container\n") - } else { - nodeDB, err := node.New() - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) - os.Exit(1) - } - - //TODO: Don't loop through profiles, instead have a nodeDB function that goes directly to the map - profiles, _ := nodeDB.FindAllProfiles() - for _, profile := range profiles { - wwlog.Printf(wwlog.DEBUG, "Looking for profile default: %s\n", profile.Id.Get()) - if profile.Id.Get() == "default" { - wwlog.Printf(wwlog.DEBUG, "Found profile default, setting container name to: %s\n", containers[0]) - profile.ContainerName.Set(containers[0]) - err := nodeDB.ProfileUpdate(profile) - if err != nil { - return errors.Wrap(err, "failed to update node profile") - } - } - } - err = nodeDB.Persist() - if err != nil { - return errors.Wrap(err, "failed to persist nodedb") - } - fmt.Printf("Set default profile to container: %s\n", containers[0]) - } - } - - return nil + return container.ContainerBuild(cbp) } diff --git a/internal/app/wwctl/container/delete/main.go b/internal/app/wwctl/container/delete/main.go index 1bbc084a..0f9c57af 100644 --- a/internal/app/wwctl/container/delete/main.go +++ b/internal/app/wwctl/container/delete/main.go @@ -1,45 +1,15 @@ package delete import ( - "fmt" - "os" - - "github.com/hpcng/warewulf/internal/pkg/container" - "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/internal/pkg/api/container" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" "github.com/spf13/cobra" ) -func CobraRunE(cmd *cobra.Command, args []string) error { +func CobraRunE(cmd *cobra.Command, args []string) (err error) { - nodeDB, err := node.New() - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not open nodeDB: %s\n", err) - os.Exit(1) + cdp := &wwapiv1.ContainerDeleteParameter{ + ContainerNames: args, } - - nodes, _ := nodeDB.FindAllNodes() - -ARG_LOOP: - for _, arg := range args { - for _, n := range nodes { - if n.ContainerName.Get() == arg { - wwlog.Printf(wwlog.ERROR, "Container is configured for nodes, skipping: %s\n", arg) - continue ARG_LOOP - } - } - - if !container.ValidSource(arg) { - wwlog.Printf(wwlog.ERROR, "Container name is not a valid source: %s\n", arg) - continue - } - err := container.DeleteSource(arg) - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not remove source: %s\n", arg) - } else { - fmt.Printf("Container has been deleted: %s\n", arg) - } - } - - return nil + return container.ContainerDelete(cdp) } diff --git a/internal/app/wwctl/container/imprt/main.go b/internal/app/wwctl/container/imprt/main.go index 09596279..1ec28322 100644 --- a/internal/app/wwctl/container/imprt/main.go +++ b/internal/app/wwctl/container/imprt/main.go @@ -1,179 +1,28 @@ package imprt import ( - "fmt" - "os" - "path" - "strconv" - "strings" - - "github.com/containers/image/v5/types" - "github.com/hpcng/warewulf/internal/pkg/container" - "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfd" - "github.com/hpcng/warewulf/internal/pkg/wwlog" - "github.com/pkg/errors" + "github.com/hpcng/warewulf/internal/pkg/api/container" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" "github.com/spf13/cobra" ) -func setOCICredentials(sCtx *types.SystemContext) error { - username, userSet := os.LookupEnv("WAREWULF_OCI_USERNAME") - password, passSet := os.LookupEnv("WAREWULF_OCI_PASSWORD") - if userSet || passSet { - if userSet && passSet { - sCtx.DockerAuthConfig = &types.DockerAuthConfig{ - Username: username, - Password: password, - } - } else { - return fmt.Errorf("oci username and password env vars must be specified together") - } - } - return nil -} - -func setNoHTTPSOpts(sCtx *types.SystemContext) error { - val, ok := os.LookupEnv("WAREWULF_OCI_NOHTTPS") - if !ok { - return nil - } - - noHTTPS, err := strconv.ParseBool(val) - if err != nil { - return fmt.Errorf("while parsing insecure http option: %v", err) - } - - // only set this if we want to disable, otherwise leave as undefined - if noHTTPS { - sCtx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(true) - } - sCtx.OCIInsecureSkipTLSVerify = noHTTPS - - return nil -} - -func getSystemContext() (sCtx *types.SystemContext, err error) { - sCtx = &types.SystemContext{} - - if err := setOCICredentials(sCtx); err != nil { - return nil, err - } - - if err := setNoHTTPSOpts(sCtx); err != nil { - return nil, err - } - - return sCtx, nil -} - -func CobraRunE(cmd *cobra.Command, args []string) error { - var name string - uri := args[0] +func CobraRunE(cmd *cobra.Command, args []string) (err error) { + // Shim in a name if none given. + name := "" if len(args) == 2 { name = args[1] - } else { - name = path.Base(uri) - wwlog.Info("Setting VNFS name: %s", name) } - if !container.ValidName(name) { - wwlog.Error("VNFS name contains illegal characters: %s", name) - os.Exit(1) + cip := &wwapiv1.ContainerImportParameter{ + Source: args[0], + Name: name, + Force: SetForce, + Update: SetUpdate, + Build: SetBuild, + Default: SetDefault, + SyncUser: SyncUser, } - - fullPath := container.SourceDir(name) - - if util.IsDir(fullPath) { - if SetForce { - wwlog.Info("Overwriting existing VNFS") - err := os.RemoveAll(fullPath) - if err != nil { - wwlog.ErrorExc(err, "") - os.Exit(1) - } - } else if SetUpdate { - wwlog.Info("Updating existing VNFS") - } else { - wwlog.Error("VNFS Name exists, specify --force, --update, or choose a different name: %s", name) - os.Exit(1) - } - } else if strings.HasPrefix(uri, "docker://") || strings.HasPrefix(uri, "docker-daemon://") || - strings.HasPrefix(uri, "file://") || util.IsFile(uri) { - sCtx, err := getSystemContext() - if err != nil { - wwlog.ErrorExc(err, "") - } - - err = container.ImportDocker(uri, name, sCtx) - if err != nil { - wwlog.Error("Could not import image: %s", err) - _ = container.DeleteSource(name) - os.Exit(1) - } - } else if util.IsDir(uri) { - err := container.ImportDirectory(uri, name) - if err != nil { - wwlog.Error("Could not import image: %s", err) - _ = container.DeleteSource(name) - os.Exit(1) - } - } else { - wwlog.Error("Invalid dir or uri: %s", uri) - os.Exit(1) - } - - wwlog.Info("Updating the container's /etc/resolv.conf") - err := util.CopyFile("/etc/resolv.conf", path.Join(container.RootFsDir(name), "/etc/resolv.conf")) - if err != nil { - wwlog.Warn("Could not copy /etc/resolv.conf into container: %s", err) - } - - err = container.SyncUids(name, !SyncUser) - if err != nil && !SyncUser { - wwlog.Error("Error in user sync, fix error and run 'syncuser' manually: %s", err) - os.Exit(1) - } - - wwlog.Info("Building container: %s", name) - err = container.Build(name, true) - if err != nil { - wwlog.Error("Could not build container %s: %s", name, err) - os.Exit(1) - } - - if SetDefault { - nodeDB, err := node.New() - if err != nil { - wwlog.Error("Could not open node configuration: %s", err) - os.Exit(1) - } - - //TODO: Don't loop through profiles, instead have a nodeDB function that goes directly to the map - profiles, _ := nodeDB.FindAllProfiles() - for _, profile := range profiles { - wwlog.Printf(wwlog.DEBUG, "Looking for profile default: %s", profile.Id.Get()) - if profile.Id.Get() == "default" { - wwlog.Printf(wwlog.DEBUG, "Found profile default, setting container name to: %s", name) - profile.ContainerName.Set(name) - err := nodeDB.ProfileUpdate(profile) - if err != nil { - return errors.Wrap(err, "failed to update profile") - } - } - } - err = nodeDB.Persist() - if err != nil { - return errors.Wrap(err, "failed to persist nodedb") - } - - wwlog.Info("Set default profile to container: %s", name) - err = warewulfd.DaemonReload() - if err != nil { - return errors.Wrap(err, "failed to reload warewulf daemon") - } - } - - return nil + _, err = container.ContainerImport(cip) + return } diff --git a/internal/app/wwctl/container/list/main.go b/internal/app/wwctl/container/list/main.go index 523dbb12..99a60859 100644 --- a/internal/app/wwctl/container/list/main.go +++ b/internal/app/wwctl/container/list/main.go @@ -2,40 +2,26 @@ package list import ( "fmt" - "os" - "github.com/hpcng/warewulf/internal/pkg/container" - "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/api/container" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" ) -func CobraRunE(cmd *cobra.Command, args []string) error { +func CobraRunE(cmd *cobra.Command, args []string) (err error) { - sources, err := container.ListSources() + containerInfo, err := container.ContainerList() if err != nil { wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) - } - - nodeDB, _ := node.New() - nodes, _ := nodeDB.FindAllNodes() - nodemap := make(map[string]int) - - for _, n := range nodes { - nodemap[n.ContainerName.Get()]++ + return } fmt.Printf("%-25s %-6s %-6s\n", "CONTAINER NAME", "NODES", "KERNEL VERSION") - for _, source := range sources { - if nodemap[source] == 0 { - nodemap[source] = 0 - } - - wwlog.Printf(wwlog.DEBUG, "Finding kernel version for: %s\n", source) - kernelVersion := container.KernelVersion(source) - fmt.Printf("%-25s %-6d %s\n", source, nodemap[source], kernelVersion) - + for i := 0; i < len(containerInfo); i++ { + fmt.Printf("%-25s %-6d %-6s\n", + containerInfo[i].Name, + containerInfo[i].NodeCount, + containerInfo[i].KernelVersion) } - return nil + return } diff --git a/internal/app/wwctl/container/show/main.go b/internal/app/wwctl/container/show/main.go index 30defabf..accc42b3 100644 --- a/internal/app/wwctl/container/show/main.go +++ b/internal/app/wwctl/container/show/main.go @@ -3,39 +3,31 @@ package show import ( "fmt" - "github.com/hpcng/warewulf/internal/pkg/container" - "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/api/container" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/spf13/cobra" ) -func CobraRunE(cmd *cobra.Command, args []string) error { - containerName := args[0] - if !container.ValidName(containerName) { - return fmt.Errorf("%s is not a valid container", containerName) +func CobraRunE(cmd *cobra.Command, args []string) (err error) { + + csp := &wwapiv1.ContainerShowParameter{ + ContainerName: args[0], } + + var r *wwapiv1.ContainerShowResponse + r, err = container.ContainerShow(csp) + if err != nil { + return + } + if !ShowAll { - fmt.Printf("%s\n", container.RootFsDir(containerName)) + fmt.Printf("%s\n", r.Rootfs) } else { - fmt.Printf("Name: %s\n", containerName) - fmt.Printf("Rootfs: %s\n", container.RootFsDir(containerName)) - kernelVersion := container.KernelVersion(containerName) - if kernelVersion != "" { - kernelVersion = "not found" - fmt.Printf("Kernelversion: %s\n", kernelVersion) - } - nodeDB, _ := node.New() - - nodes, _ := nodeDB.FindAllNodes() - var nodeList []string - for _, n := range nodes { - if n.ContainerName.Get() == containerName { - - nodeList = append(nodeList, n.Id.Get()) - } - } - fmt.Printf("Nr nodes: %d\n", len(nodeList)) - fmt.Printf("Nodes: %s\n", nodeList) - + fmt.Printf("Name: %s\n", r.Name) + fmt.Printf("Rootfs: %s\n", r.Rootfs) + fmt.Printf("Nr nodes: %d\n", len(r.Nodes)) + fmt.Printf("Nodes: %s\n", r.Nodes) } - return nil + return } diff --git a/internal/app/wwctl/node/add/main.go b/internal/app/wwctl/node/add/main.go index 54b1c4ba..0101729c 100644 --- a/internal/app/wwctl/node/add/main.go +++ b/internal/app/wwctl/node/add/main.go @@ -1,166 +1,24 @@ package add import ( - "net" - - "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfd" - "github.com/hpcng/warewulf/internal/pkg/wwlog" - "github.com/hpcng/warewulf/pkg/hostlist" - "github.com/pkg/errors" + "github.com/hpcng/warewulf/internal/pkg/api/node" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" "github.com/spf13/cobra" ) func CobraRunE(cmd *cobra.Command, args []string) error { - var count uint - nodeDB, err := node.New() - if err != nil { - return errors.Wrap(err, "failed to open node database") + + nap := wwapiv1.NodeAddParameter{ + Cluster: SetClusterName, + Discoverable: SetDiscoverable, + Gateway: SetGateway, + Hwaddr: SetHwaddr, + Ipaddr: SetIpaddr, + Netdev: SetNetDev, + Netmask: SetNetmask, + Netname: SetNetName, + Type: SetType, + NodeNames: args, } - - node_args := hostlist.Expand(args) - - for _, a := range node_args { - n, err := nodeDB.AddNode(a) - if err != nil { - return errors.Wrap(err, "failed to add node") - } - wwlog.Printf(wwlog.INFO, "Added node: %s\n", a) - - if SetClusterName != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting cluster name to: %s\n", n.Id.Get(), SetClusterName) - n.ClusterName.Set(SetClusterName) - err := nodeDB.NodeUpdate(n) - if err != nil { - return errors.Wrap(err, "failed to update node") - } - } - - if SetNetDev != "" { - if SetNetName == "" { - return errors.New("you must include the '--netname' option") - } - - if _, ok := n.NetDevs[SetNetName]; !ok { - var netdev node.NetDevEntry - n.NetDevs[SetNetName] = &netdev - } - - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Device to: %s\n", n.Id.Get(), SetNetName, SetNetDev) - - n.NetDevs[SetNetName].Device.Set(SetNetDev) - n.NetDevs[SetNetName].OnBoot.SetB(true) - - } - - if SetIpaddr != "" { - if SetNetName == "" { - return errors.New("you must include the '--netname' option") - } - - NewIpaddr := util.IncrementIPv4(SetIpaddr, count) - - if _, ok := n.NetDevs[SetNetName]; !ok { - var netdev node.NetDevEntry - n.NetDevs[SetNetName] = &netdev - } - - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Ipaddr to: %s\n", n.Id.Get(), SetNetName, NewIpaddr) - - n.NetDevs[SetNetName].Ipaddr.Set(NewIpaddr) - n.NetDevs[SetNetName].OnBoot.SetB(true) - - } - - if SetNetmask != "" { - if SetNetName == "" { - return errors.New("you must include the '--netname' option") - } - - if _, ok := n.NetDevs[SetNetName]; !ok { - return errors.New("network device does not exist: " + SetNetName) - } - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting netmask to: %s\n", n.Id.Get(), SetNetName, SetNetmask) - - n.NetDevs[SetNetName].Netmask.Set(SetNetmask) - } - - if SetGateway != "" { - if SetNetName == "" { - return errors.New("you must include the '--netname' option") - } - - if _, ok := n.NetDevs[SetNetName]; !ok { - return errors.New("network device does not exist: " + SetNetName) - } - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting gateway to: %s\n", n.Id.Get(), SetNetName, SetGateway) - - n.NetDevs[SetNetName].Gateway.Set(SetGateway) - } - - if SetHwaddr != "" { - if SetNetName == "" { - return errors.New("you must include the '--netname' option") - } - - if _, ok := n.NetDevs[SetNetName]; !ok { - return errors.New("network device does not exist: " + SetNetName) - } - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting HW address to: %s\n", n.Id.Get(), SetNetName, SetHwaddr) - - n.NetDevs[SetNetName].Hwaddr.Set(SetHwaddr) - n.NetDevs[SetNetName].OnBoot.SetB(true) - } - - if SetType != "" { - if SetNetName == "" { - return errors.New("you must include the '--netname' option") - } - - if _, ok := n.NetDevs[SetNetName]; !ok { - return errors.New("network device does not exist: " + SetNetName) - } - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Type to: %s\n", n.Id.Get(), SetNetName, SetType) - - n.NetDevs[SetNetName].Type.Set(SetType) - } - if SetIpaddr6 != "" { - if SetNetName == "" { - return errors.New("you must include the '--netname' option") - } - if _, ok := n.NetDevs[SetNetName]; !ok { - return errors.New("network device does not exist: " + SetNetName) - } - // just check if address is a valid ipv6 CIDR address - if _, _, err := net.ParseCIDR(SetIpaddr6); err != nil { - return errors.Errorf("%s is not a valid ipv6 address in CIDR notation\n", SetIpaddr6) - } - - } - if SetDiscoverable { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting node to discoverable\n", n.Id.Get()) - - n.Discoverable.SetB(true) - } - - err = nodeDB.NodeUpdate(n) - if err != nil { - return errors.Wrap(err, "failed to update nodedb") - } - - count++ - } - - err = nodeDB.Persist() - if err != nil { - return errors.Wrap(err, "failed to persist new node") - } - - err = warewulfd.DaemonReload() - if err != nil { - return errors.Wrap(err, "failed to reload warewulf daemon") - } - - return nil + return node.NodeAdd(&nap) } diff --git a/internal/app/wwctl/node/delete/main.go b/internal/app/wwctl/node/delete/main.go index 8ce3f614..b5a58308 100644 --- a/internal/app/wwctl/node/delete/main.go +++ b/internal/app/wwctl/node/delete/main.go @@ -2,92 +2,33 @@ package delete import ( "fmt" - "os" + apiNode "github.com/hpcng/warewulf/internal/pkg/api/node" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/api/util" "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/warewulfd" - "github.com/hpcng/warewulf/internal/pkg/wwlog" - "github.com/hpcng/warewulf/pkg/hostlist" - "github.com/manifoldco/promptui" - "github.com/pkg/errors" "github.com/spf13/cobra" ) -func CobraRunE(cmd *cobra.Command, args []string) error { - var count int - var nodeList []node.NodeInfo +func CobraRunE(cmd *cobra.Command, args []string) (err error) { - nodeDB, err := node.New() - if err != nil { - wwlog.Printf(wwlog.ERROR, "Failed to open node database: %s\n", err) - os.Exit(1) + ndp := wwapiv1.NodeDeleteParameter{ + Force: SetForce, + NodeNames: args, } - nodes, err := nodeDB.FindAllNodes() - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not get node list: %s\n", err) - os.Exit(1) - } - - args = hostlist.Expand(args) - - for _, r := range args { - var match bool - for _, n := range nodes { - if n.Id.Get() == r { - nodeList = append(nodeList, n) - match = true - } - } - - if !match { - fmt.Fprintf(os.Stderr, "ERROR: No match for node: %s\n", r) - } - } - - if len(nodeList) == 0 { - fmt.Printf("No nodes found\n") - os.Exit(1) - } - - for _, n := range nodeList { - err := nodeDB.DelNode(n.Id.Get()) + if !SetYes { + var nodeList []node.NodeInfo + // The checks run twice in the prompt case. + // Avoiding putting in a blocking prompt in an API. + nodeList, err = apiNode.NodeDeleteParameterCheck(&ndp, false) if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - } else { - count++ - fmt.Printf("Deleting node: %s\n", n.Id.Print()) + return + } + yes := util.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to delete %d nodes(s)", len(nodeList))) + if !yes { + return } } - - if SetYes { - err := nodeDB.Persist() - if err != nil { - return errors.Wrap(err, "failed to persist nodedb") - } - } else { - q := fmt.Sprintf("Are you sure you want to delete %d nodes(s)", count) - - prompt := promptui.Prompt{ - Label: q, - IsConfirm: true, - } - - result, _ := prompt.Run() - - if result == "y" || result == "yes" { - err := nodeDB.Persist() - if err != nil { - return errors.Wrap(err, "failed to persist nodedb") - } - - err = warewulfd.DaemonReload() - if err != nil { - return errors.Wrap(err, "failed to reload warewulf daemon") - } - - } - } - - return nil + return apiNode.NodeDelete(&ndp) } diff --git a/internal/app/wwctl/node/delete/root.go b/internal/app/wwctl/node/delete/root.go index 95a96f8f..dda17ffc 100644 --- a/internal/app/wwctl/node/delete/root.go +++ b/internal/app/wwctl/node/delete/root.go @@ -8,12 +8,12 @@ import ( var ( baseCmd = &cobra.Command{ DisableFlagsInUseLine: true, - Use: "delete [OPTIONS] NODE [NODE ...]", - Short: "Delete a node from Warewulf", - Long: "This command will remove NODE(s) from the Warewulf node configuration.", - Args: cobra.MinimumNArgs(1), - RunE: CobraRunE, - Aliases: []string{"rm", "del"}, + Use: "delete [OPTIONS] NODE [NODE ...]", + Short: "Delete a node from Warewulf", + Long: "This command will remove NODE(s) from the Warewulf node configuration.", + Args: cobra.MinimumNArgs(1), + RunE: CobraRunE, + Aliases: []string{"rm", "del"}, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { return nil, cobra.ShellCompDirectiveNoFileComp @@ -29,11 +29,11 @@ var ( }, } SetYes bool - SetForce string + SetForce bool // currently unused ) func init() { - baseCmd.PersistentFlags().StringVarP(&SetForce, "force", "f", "", "Force node delete") + baseCmd.PersistentFlags().BoolVarP(&SetForce, "force", "f", false, "Force node delete") baseCmd.PersistentFlags().BoolVarP(&SetYes, "yes", "y", false, "Set 'yes' to all questions asked") } @@ -41,4 +41,4 @@ func init() { // GetRootCommand returns the root cobra.Command for the application. func GetCommand() *cobra.Command { return baseCmd -} +} \ No newline at end of file diff --git a/internal/app/wwctl/node/list/main.go b/internal/app/wwctl/node/list/main.go index 304404fe..354dfae1 100644 --- a/internal/app/wwctl/node/list/main.go +++ b/internal/app/wwctl/node/list/main.go @@ -2,131 +2,127 @@ package list import ( "fmt" - "os" "sort" "strings" - "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/api/node" "github.com/hpcng/warewulf/internal/pkg/wwlog" - "github.com/hpcng/warewulf/pkg/hostlist" "github.com/spf13/cobra" ) -func CobraRunE(cmd *cobra.Command, args []string) error { - var err error +func CobraRunE(cmd *cobra.Command, args []string) (err error) { - nodeDB, err := node.New() + nodeInfo, err := node.NodeList(args) if err != nil { wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) - os.Exit(1) + return } - nodes, err := nodeDB.FindAllNodes() - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not get node list: %s\n", err) - os.Exit(1) - } - - args = hostlist.Expand(args) - if ShowAll { - for _, node := range node.FilterByName(nodes, args) { + for i := 0; i < len(nodeInfo); i++ { + ni := nodeInfo[i] + nodeName := ni.Id.Value + fmt.Printf("################################################################################\n") fmt.Printf("%-20s %-18s %-12s %s\n", "NODE", "FIELD", "PROFILE", "VALUE") - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "Id", node.Id.Source(), node.Id.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "Comment", node.Comment.Source(), node.Comment.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "Cluster", node.ClusterName.Source(), node.ClusterName.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "Profiles", "--", strings.Join(node.Profiles, ",")) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "Discoverable", node.Discoverable.Source(), node.Discoverable.PrintB()) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "Id", ni.Id.Source, ni.Id.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "Comment", ni.Comment.Source, ni.Comment.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "Cluster", ni.Cluster.Source, ni.Cluster.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "Profiles", "--", strings.Join(ni.Profiles, ",'")) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "Container", node.ContainerName.Source(), node.ContainerName.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "KernelOverride", node.Kernel.Override.Source(), node.Kernel.Override.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "KernelArgs", node.Kernel.Args.Source(), node.Kernel.Args.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "SystemOverlay", node.SystemOverlay.Source(), node.SystemOverlay.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "RuntimeOverlay", node.RuntimeOverlay.Source(), node.RuntimeOverlay.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "Ipxe", node.Ipxe.Source(), node.Ipxe.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "Init", node.Init.Source(), node.Init.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "Root", node.Root.Source(), node.Root.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "AssetKey", node.AssetKey.Source(), node.AssetKey.Print()) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "Discoverable", ni.Discoverable.Source, ni.Discoverable.Print) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "IpmiIpaddr", node.Ipmi.Ipaddr.Source(), node.Ipmi.Ipaddr.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "IpmiNetmask", node.Ipmi.Netmask.Source(), node.Ipmi.Netmask.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "IpmiPort", node.Ipmi.Port.Source(), node.Ipmi.Port.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "IpmiGateway", node.Ipmi.Gateway.Source(), node.Ipmi.Gateway.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "IpmiUserName", node.Ipmi.UserName.Source(), node.Ipmi.UserName.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "IpmiInterface", node.Ipmi.Interface.Source(), node.Ipmi.Interface.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "IpmiWrite", node.Ipmi.Interface.Source(), node.Ipmi.Write.PrintB()) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "Container", ni.Container.Source, ni.Container.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "KernelOverride", ni.KernelOverride.Source, ni.KernelOverride.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "KernelArgs", ni.KernelArgs.Source, ni.KernelArgs.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "SystemOverlay", ni.SystemOverlay.Source, ni.SystemOverlay.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "RuntimeOverlay", ni.RuntimeOverlay.Source, ni.RuntimeOverlay.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "Ipxe", ni.Ipxe.Source, ni.Ipxe.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "Init", ni.Init.Source, ni.Init.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "Root", ni.Root.Source, ni.Root.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "AssetKey", ni.AssetKey.Source, ni.AssetKey.Print) - for keyname, key := range node.Tags { - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), "Tag["+keyname+"]", key.Source(), key.Print()) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "IpmiIpaddr", ni.IpmiIpaddr.Source, ni.IpmiIpaddr.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "IpmiNetmask", ni.IpmiNetmask.Source, ni.IpmiNetmask.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "IpmiPort", ni.IpmiPort.Source, ni.IpmiPort.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "IpmiGateway", ni.IpmiGateway.Source, ni.IpmiGateway.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "IpmiUserName", ni.IpmiUserName.Source, ni.IpmiUserName.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "IpmiInterface", ni.IpmiInterface.Source, ni.IpmiInterface.Print) + + for keyname, key := range ni.Tags { + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, "Tag["+keyname+"]", key.Source, key.Print) } - for name, netdev := range node.NetDevs { - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), name+":DEVICE", netdev.Device.Source(), netdev.Device.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), name+":HWADDR", netdev.Hwaddr.Source(), netdev.Hwaddr.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), name+":IPADDR", netdev.Ipaddr.Source(), netdev.Ipaddr.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), name+":IPADDR6", netdev.Ipaddr.Source(), netdev.Ipaddr6.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), name+":NETMASK", netdev.Netmask.Source(), netdev.Netmask.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), name+":GATEWAY", netdev.Gateway.Source(), netdev.Gateway.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), name+":TYPE", netdev.Type.Source(), netdev.Type.Print()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), name+":ONBOOT", netdev.OnBoot.Source(), netdev.OnBoot.PrintB()) - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), name+":PRIMARY", netdev.Primary.Source(), netdev.Primary.PrintB()) + for name, netdev := range ni.NetDevs { + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, name+":DEVICE", netdev.Device.Source, netdev.Device.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, name+":HWADDR", netdev.Hwaddr.Source, netdev.Hwaddr.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, name+":IPADDR", netdev.Ipaddr.Source, netdev.Ipaddr.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, name+":NETMASK", netdev.Netmask.Source, netdev.Netmask.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, name+":GATEWAY", netdev.Gateway.Source, netdev.Gateway.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, name+":TYPE", netdev.Type.Source, netdev.Type.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, name+":ONBOOT", netdev.Onboot.Source, netdev.Onboot.Print) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, name+":DEFAULT", netdev.Primary.Source, netdev.Primary.Print) for keyname, key := range netdev.Tags { - fmt.Printf("%-20s %-18s %-12s %s\n", node.Id.Get(), name+":TAG["+keyname+"]", key.Source(), key.Print()) + fmt.Printf("%-20s %-18s %-12s %s\n", nodeName, name+":TAG["+keyname+"]", key.Source, key.Print) } } - } - } else if ShowNet { - fmt.Printf("%-22s %-6s %-18s %-15s %-15s %-15s\n", "NODE NAME", "DEVICE", "HWADDR", "IPADDR", "GATEWAY", "IPADDR6") + fmt.Printf("%-22s %-6s %-18s %-15s %-15s\n", "NODE NAME", "DEVICE", "HWADDR", "IPADDR", "GATEWAY") fmt.Println(strings.Repeat("=", 80)) - for _, node := range node.FilterByName(nodes, args) { - if len(node.NetDevs) > 0 { - for name, dev := range node.NetDevs { - fmt.Printf("%-22s %-6s %-18s %-15s %-15s %-15s\n", - node.Id.Get(), name, dev.Hwaddr.Print(), dev.Ipaddr.Print(), dev.Gateway.Print(), dev.Ipaddr6.Print()) + for i := 0; i < len(nodeInfo); i++ { + ni := nodeInfo[i] + nodeName := ni.Id.Value + + if len(ni.NetDevs) > 0 { + for name, dev := range ni.NetDevs { + fmt.Printf("%-22s %-6s %-18s %-15s %-15s\n", nodeName, name, dev.Hwaddr.Print, dev.Ipaddr.Print, dev.Gateway.Print) } } else { - fmt.Printf("%-22s %-6s %-18s %-15s %-15s %-15s\n", node.Id.Get(), "--", "--", "--", "--", "--") + fmt.Printf("%-22s %-6s %-18s %-15s %-15s\n", nodeName, "--", "--", "--", "--") } } - } else if ShowIpmi { fmt.Printf("%-22s %-16s %-10s %-20s %-20s %-14s\n", "NODE NAME", "IPMI IPADDR", "IPMI PORT", "IPMI USERNAME", "IPMI PASSWORD", "IPMI INTERFACE") fmt.Println(strings.Repeat("=", 108)) - for _, node := range node.FilterByName(nodes, args) { - fmt.Printf("%-22s %-16s %-10s %-20s %-20s %-14s\n", node.Id.Get(), node.Ipmi.Ipaddr.Print(), node.Ipmi.Port.Print(), node.Ipmi.UserName.Print(), node.Ipmi.Password.Print(), node.Ipmi.Interface.Print()) + for i := 0; i < len(nodeInfo); i++ { + ni := nodeInfo[i] + nodeName := ni.Id.Value + + fmt.Printf("%-22s %-16s %-10s %-20s %-20s %-14s\n", nodeName, ni.IpmiIpaddr.Print, ni.IpmiPort.Print, ni.IpmiUserName.Print, ni.IpmiPassword.Print, ni.IpmiInterface.Print) } } else if ShowLong { fmt.Printf("%-22s %-26s %-35s %s\n", "NODE NAME", "KERNEL OVERRIDE", "CONTAINER", "OVERLAYS (S/R)") fmt.Println(strings.Repeat("=", 120)) - for _, node := range node.FilterByName(nodes, args) { - fmt.Printf("%-22s %-26s %-35s %s\n", node.Id.Get(), node.Kernel.Override.Print(), node.ContainerName.Print(), node.SystemOverlay.Print()+"/"+node.RuntimeOverlay.Print()) + for i := 0; i < len(nodeInfo); i++ { + ni := nodeInfo[i] + nodeName := ni.Id.Value + + fmt.Printf("%-22s %-26s %-35s %s\n", nodeName, ni.KernelOverride.Print, ni.Container.Print, ni.SystemOverlay.Print+"/"+ni.RuntimeOverlay.Print) } } else { fmt.Printf("%-22s %-26s %s\n", "NODE NAME", "PROFILES", "NETWORK") fmt.Println(strings.Repeat("=", 80)) - for _, node := range node.FilterByName(nodes, args) { + for i := 0; i < len(nodeInfo); i++ { + ni := nodeInfo[i] + nodeName := ni.Id.Value + var netdevs []string - if len(node.NetDevs) > 0 { - for name, dev := range node.NetDevs { - netdevs = append(netdevs, fmt.Sprintf("%s:%s", name, dev.Ipaddr.Print())) + if len(ni.NetDevs) > 0 { + for name, dev := range ni.NetDevs { + netdevs = append(netdevs, fmt.Sprintf("%s:%s", name, dev.Ipaddr.Print)) } } sort.Strings(netdevs) - - fmt.Printf("%-22s %-26s %s\n", node.Id.Get(), strings.Join(node.Profiles, ","), strings.Join(netdevs, ", ")) + fmt.Printf("%-22s %-26s %s\n", nodeName, strings.Join(ni.Profiles, ","), strings.Join(netdevs, ", ")) } - } - - return nil + return } diff --git a/internal/app/wwctl/node/set/main.go b/internal/app/wwctl/node/set/main.go index 86eb563f..42f4bb48 100644 --- a/internal/app/wwctl/node/set/main.go +++ b/internal/app/wwctl/node/set/main.go @@ -2,413 +2,71 @@ package set import ( "fmt" - "os" - "strings" - "github.com/hpcng/warewulf/internal/pkg/node" - "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/warewulfd" - "github.com/hpcng/warewulf/internal/pkg/wwlog" - "github.com/manifoldco/promptui" - "github.com/pkg/errors" + "github.com/hpcng/warewulf/internal/pkg/api/node" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/api/util" "github.com/spf13/cobra" ) -func CobraRunE(cmd *cobra.Command, args []string) error { - var err error - var count uint - - nodeDB, err := node.New() - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) - os.Exit(1) +func CobraRunE(cmd *cobra.Command, args []string) (err error) { + set := wwapiv1.NodeSetParameter{ + Comment: SetComment, + Container: SetContainer, + KernelOverride: SetKernelOverride, + KernelArgs: SetKernelArgs, + Netname: SetNetName, + Netdev: SetNetDev, + Ipaddr: SetIpaddr, + Netmask: SetNetmask, + Gateway: SetGateway, + Hwaddr: SetHwaddr, + Type: SetType, + Onboot: SetNetOnBoot, + NetDefault: SetNetPrimary, + NetdevDelete: SetNetDevDel, + Cluster: SetClusterName, + Ipxe: SetIpxe, + InitOverlay: SetInitOverlay, + RuntimeOverlay: SetRuntimeOverlay, + SystemOverlay: SetSystemOverlay, + IpmiIpaddr: SetIpmiIpaddr, + IpmiNetmask: SetIpmiNetmask, + IpmiPort: SetIpmiPort, + IpmiGateway: SetIpmiGateway, + IpmiUsername: SetIpmiUsername, + IpmiPassword: SetIpmiPassword, + IpmiInterface: SetIpmiInterface, + IpmiWrite: SetIpmiWrite, + AllNodes: SetNodeAll, + Profile: SetProfile, + ProfileAdd: SetAddProfile, + ProfileDelete: SetDelProfile, + Force: SetForce, + Init: SetInit, + Discoverable: SetDiscoverable, + Undiscoverable: SetUndiscoverable, + Root: SetRoot, + Tags: SetTags, + TagsDelete: SetDelTags, + AssetKey: SetAssetKey, + NodeNames: args, + NetTags: SetNetTags, + NetDeleteTags: SetNetDelTags, } - nodes, err := nodeDB.FindAllNodes() - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not get node list: %s\n", err) - os.Exit(1) - } - - if SetNodeAll || (len(args) == 0 && len(nodes) > 0) { - fmt.Printf("\n*** WARNING: This command will modify all nodes! ***\n\n") - } else { - nodes = node.FilterByName(nodes, args) - } - - if len(nodes) == 0 { - fmt.Printf("No nodes found\n") - os.Exit(1) - } - - for _, n := range nodes { - wwlog.Printf(wwlog.VERBOSE, "Evaluating node: %s\n", n.Id.Get()) - - if SetComment != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting comment to: %s\n", n.Id.Get(), SetComment) - n.Comment.Set(SetComment) - } - - if SetContainer != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting container name to: %s\n", n.Id.Get(), SetContainer) - n.ContainerName.Set(SetContainer) - } - - if SetInit != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting init command to: %s\n", n.Id.Get(), SetInit) - n.Init.Set(SetInit) - } - - if SetRoot != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting root to: %s\n", n.Id.Get(), SetRoot) - n.Root.Set(SetRoot) - } - - if SetAssetKey != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting asset key to: %s\n", n.Id.Get(), SetAssetKey) - n.AssetKey.Set(SetAssetKey) - } - - if SetKernelOverride != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting kernel override to: %s\n", n.Id.Get(), SetKernelOverride) - n.Kernel.Override.Set(SetKernelOverride) - } - - if SetKernelArgs != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting kernel args to: %s\n", n.Id.Get(), SetKernelArgs) - n.Kernel.Args.Set(SetKernelArgs) - } - - if SetClusterName != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting cluster name to: %s\n", n.Id.Get(), SetClusterName) - n.ClusterName.Set(SetClusterName) - } - - if SetIpxe != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting iPXE template to: %s\n", n.Id.Get(), SetIpxe) - n.Ipxe.Set(SetIpxe) - } - - if len(SetRuntimeOverlay) != 0 { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting runtime overlay to: %s\n", n.Id.Get(), SetRuntimeOverlay) - n.RuntimeOverlay.SetSlice(SetRuntimeOverlay) - } - - if len(SetSystemOverlay) != 0 { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting system overlay to: %s\n", n.Id.Get(), SetSystemOverlay) - n.SystemOverlay.SetSlice(SetSystemOverlay) - } - - if SetIpmiIpaddr != "" { - NewIpaddr := util.IncrementIPv4(SetIpmiIpaddr, count) - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI IP address to: %s\n", n.Id.Get(), NewIpaddr) - n.Ipmi.Ipaddr.Set(NewIpaddr) - } - - if SetIpmiNetmask != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI netmask to: %s\n", n.Id.Get(), SetIpmiNetmask) - n.Ipmi.Netmask.Set(SetIpmiNetmask) - } - - if SetIpmiPort != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI port to: %s\n", n.Id.Get(), SetIpmiPort) - n.Ipmi.Port.Set(SetIpmiPort) - } - - if SetIpmiGateway != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI gateway to: %s\n", n.Id.Get(), SetIpmiGateway) - n.Ipmi.Gateway.Set(SetIpmiGateway) - } - - if SetIpmiUsername != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI IP username to: %s\n", n.Id.Get(), SetIpmiUsername) - n.Ipmi.UserName.Set(SetIpmiUsername) - } - - if SetIpmiPassword != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI IP password to: %s\n", n.Id.Get(), SetIpmiPassword) - n.Ipmi.Password.Set(SetIpmiPassword) - } - - if SetIpmiInterface != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI IP interface to: %s\n", n.Id.Get(), SetIpmiInterface) - n.Ipmi.Interface.Set(SetIpmiInterface) - } - - if SetIpmiWrite == "yes" || SetNetOnBoot == "y" || SetNetOnBoot == "1" || SetNetOnBoot == "true" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting Ipmiwrite to %s\n", n.Id.Get(), SetIpmiWrite) - n.Ipmi.Write.SetB(true) - } else { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting Ipmiwrite to %s\n", n.Id.Get(), SetIpmiWrite) - n.Ipmi.Write.SetB(false) - } - - if SetDiscoverable { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting node to discoverable\n", n.Id.Get()) - n.Discoverable.SetB(true) - } - - if SetUndiscoverable { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting node to undiscoverable\n", n.Id.Get()) - n.Discoverable.SetB(false) - } - - if SetProfile != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting profiles to: %s\n", n.Id.Get(), SetProfile) - n.Profiles = []string{SetProfile} - } - - if len(SetAddProfile) > 0 { - for _, p := range SetAddProfile { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, adding profile '%s'\n", n.Id.Get(), p) - n.Profiles = util.SliceAddUniqueElement(n.Profiles, p) - } - } - - if len(SetDelProfile) > 0 { - for _, p := range SetDelProfile { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, deleting profile '%s'\n", n.Id.Get(), p) - n.Profiles = util.SliceRemoveElement(n.Profiles, p) - } - } - - if SetNetName != "" { - if _, ok := n.NetDevs[SetNetName]; !ok { - var nd node.NetDevEntry - nd.Tags = make(map[string]*node.Entry) - - n.NetDevs[SetNetName] = &nd - if SetNetDev == "" { - n.NetDevs[SetNetName].Device.Set(SetNetName) - } - } - var def bool = true - - SetNetOnBoot = "yes" - - for _, n := range n.NetDevs { - if n.Primary.GetB() { - def = false - } - } - - if def { - SetNetPrimary = "yes" - } - - } - - if SetNetDev != "" { - if SetNetName == "" { - wwlog.Printf(wwlog.ERROR, "You must include the '--netname' option\n") - os.Exit(1) - } - - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting net Device to: %s\n", n.Id.Get(), SetNetName, SetNetDev) - n.NetDevs[SetNetName].Device.Set(SetNetDev) - } - - if SetIpaddr != "" { - if SetNetName == "" { - wwlog.Printf(wwlog.ERROR, "You must include the '--netname' option\n") - os.Exit(1) - } - - NewIpaddr := util.IncrementIPv4(SetIpaddr, count) - - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Ipaddr to: %s\n", n.Id.Get(), SetNetName, NewIpaddr) - n.NetDevs[SetNetName].Ipaddr.Set(NewIpaddr) - } - - if SetNetmask != "" { - if SetNetName == "" { - wwlog.Printf(wwlog.ERROR, "You must include the '--netname' option\n") - os.Exit(1) - } - - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting netmask to: %s\n", n.Id.Get(), SetNetName, SetNetmask) - n.NetDevs[SetNetName].Netmask.Set(SetNetmask) - } - - if SetGateway != "" { - if SetNetName == "" { - wwlog.Printf(wwlog.ERROR, "You must include the '--netname' option\n") - os.Exit(1) - } - - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting gateway to: %s\n", n.Id.Get(), SetNetName, SetGateway) - n.NetDevs[SetNetName].Gateway.Set(SetGateway) - } - - if SetHwaddr != "" { - if SetNetName == "" { - wwlog.Printf(wwlog.ERROR, "You must include the '--netname' option\n") - os.Exit(1) - } - - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting HW address to: %s\n", n.Id.Get(), SetNetName, SetHwaddr) - n.NetDevs[SetNetName].Hwaddr.Set(SetHwaddr) - } - - if SetType != "" { - if SetNetName == "" { - wwlog.Printf(wwlog.ERROR, "You must include the '--netname' option\n") - os.Exit(1) - } - - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Type %s\n", n.Id.Get(), SetNetName, SetType) - n.NetDevs[SetNetName].Type.Set(SetType) - } - - if SetNetOnBoot != "" { - if SetNetName == "" { - wwlog.Printf(wwlog.ERROR, "You must include the '--netname' option\n") - os.Exit(1) - } - - if SetNetOnBoot == "yes" || SetNetOnBoot == "y" || SetNetOnBoot == "1" || SetNetOnBoot == "true" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting ONBOOT\n", n.Id.Get(), SetNetName) - n.NetDevs[SetNetName].OnBoot.SetB(true) - } else { - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Unsetting ONBOOT\n", n.Id.Get(), SetNetName) - n.NetDevs[SetNetName].OnBoot.SetB(false) - } - } - - if SetNetPrimary != "" { - if SetNetName == "" { - wwlog.Printf(wwlog.ERROR, "You must include the '--netname' option\n") - os.Exit(1) - } - - if SetNetPrimary == "yes" || SetNetPrimary == "y" || SetNetPrimary == "1" || SetNetPrimary == "true" { - - // Set all other devices to non-default - for _, n := range n.NetDevs { - n.Primary.SetB(false) - } - - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting PRIMARY\n", n.Id.Get(), SetNetName) - n.NetDevs[SetNetName].Primary.SetB(true) - } else { - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Unsetting PRIMARY\n", n.Id.Get(), SetNetName) - n.NetDevs[SetNetName].Primary.SetB(false) - } - } - - if SetNetDevDel { - if SetNetName == "" { - wwlog.Printf(wwlog.ERROR, "You must include the '--netname' option\n") - os.Exit(1) - } - - if _, ok := n.NetDevs[SetNetName]; !ok { - wwlog.Printf(wwlog.ERROR, "Network device name doesn't exist: %s\n", SetNetName) - os.Exit(1) - } - - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Deleting network device: %s\n", n.Id.Get(), SetNetName) - delete(n.NetDevs, SetNetName) - } - - if len(SetTags) > 0 { - for _, t := range SetTags { - keyval := strings.SplitN(t, "=", 2) - key := keyval[0] - val := keyval[1] - - if _, ok := n.Tags[key]; !ok { - var nd node.Entry - n.Tags[key] = &nd - } - - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting Tag '%s'='%s'\n", n.Id.Get(), key, val) - n.Tags[key].Set(val) - } - } - if len(SetDelTags) > 0 { - for _, t := range SetDelTags { - keyval := strings.SplitN(t, "=", 1) - key := keyval[0] - - if _, ok := n.Tags[key]; !ok { - wwlog.Printf(wwlog.WARN, "Key does not exist: %s\n", key) - os.Exit(1) - } - - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Deleting tag: %s\n", n.Id.Get(), key) - delete(n.Tags, key) - } - } - if len(SetNetTags) > 0 { - for _, t := range SetNetTags { - keyval := strings.SplitN(t, "=", 2) - key := keyval[0] - val := keyval[1] - if _, ok := n.NetDevs[SetNetName].Tags[key]; !ok { - var nd node.Entry - n.NetDevs[SetNetName].Tags[key] = &nd - } - - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting NETTAG '%s'='%s'\n", n.Id.Get(), SetNetName, key, val) - n.NetDevs[SetNetName].Tags[key].Set(val) - } - - } - if len(SetNetDelTags) > 0 { - for _, t := range SetNetDelTags { - keyval := strings.SplitN(t, "=", 1) - key := keyval[0] - if _, ok := n.NetDevs[SetNetName].Tags[key]; !ok { - wwlog.Printf(wwlog.WARN, "Node: %s:%s Key %s does not exist\n", n.Id.Get(), SetNetName, key) - os.Exit(1) - } - - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Deleting Tag %s\n", n.Id.Get(), SetNetName, key) - delete(n.NetDevs[SetNetName].Tags, key) - } - } - err := nodeDB.NodeUpdate(n) + if !SetYes { + var nodeCount uint + // The checks run twice in the prompt case. + // Avoiding putting in a blocking prompt in an API. + _, nodeCount, err = node.NodeSetParameterCheck(&set, false) if err != nil { - wwlog.Printf(wwlog.ERROR, "%s\n", err) - os.Exit(1) + return } - - count++ - } - - if SetYes { - err := nodeDB.Persist() - if err != nil { - return errors.Wrap(err, "failed to persist nodedb") - } - - err = warewulfd.DaemonReload() - if err != nil { - return errors.Wrap(err, "failed to reload warewulf daemon") - } - } else { - q := fmt.Sprintf("Are you sure you want to modify %d nodes(s)", len(nodes)) - - prompt := promptui.Prompt{ - Label: q, - IsConfirm: true, - } - - result, _ := prompt.Run() - - if result == "y" || result == "yes" { - err := nodeDB.Persist() - if err != nil { - return errors.Wrap(err, "failed to persist nodedb") - } - - err = warewulfd.DaemonReload() - if err != nil { - return errors.Wrap(err, "failed to reload warewulf daemon") - } + yes := util.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d nodes(s)", nodeCount)) + if !yes { + return } } - - return nil + return node.NodeSet(&set) } diff --git a/internal/app/wwctl/node/status/main.go b/internal/app/wwctl/node/status/main.go index 31ef1d18..771157a9 100644 --- a/internal/app/wwctl/node/status/main.go +++ b/internal/app/wwctl/node/status/main.go @@ -1,15 +1,15 @@ package nodestatus import ( - "encoding/json" "fmt" - "net/http" "os" "sort" "strings" "time" "github.com/fatih/color" + "github.com/hpcng/warewulf/internal/pkg/api/node" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" "github.com/hpcng/warewulf/internal/pkg/warewulfconf" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/hpcng/warewulf/pkg/hostlist" @@ -17,19 +17,7 @@ import ( "golang.org/x/term" ) -type allStatus struct { - Nodes map[string]*NodeStatus `json:"nodes"` -} - -type NodeStatus struct { - NodeName string `json:"node name"` - Stage string `json:"stage"` - Sent string `json:"sent"` - Ipaddr string `json:"ipaddr"` - Lastseen int64 `json:"last seen"` -} - -func CobraRunE(cmd *cobra.Command, args []string) error { +func CobraRunE(cmd *cobra.Command, args []string) (err error) { controller, err := warewulfconf.New() if err != nil { @@ -42,30 +30,16 @@ func CobraRunE(cmd *cobra.Command, args []string) error { os.Exit(1) } - statusURL := fmt.Sprintf("http://%s:%d/status", controller.Ipaddr, controller.Warewulf.Port) - for { var elipsis bool var height int var count int rightnow := time.Now().Unix() - wwlog.Printf(wwlog.VERBOSE, "Connecting to: %s\n", statusURL) - - resp, err := http.Get(statusURL) + var nodeStatusResponse *wwapiv1.NodeStatusResponse + nodeStatusResponse, err = node.NodeStatus([]string{}) if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not connect to Warewulf server: %s\n", err) - os.Exit(1) - } - defer resp.Body.Close() - - decoder := json.NewDecoder(resp.Body) - var nodeStatus allStatus - - err = decoder.Decode(&nodeStatus) - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not decode JSON: %s\n", err) - os.Exit(1) + return err } if SetWatch { @@ -80,65 +54,50 @@ func CobraRunE(cmd *cobra.Command, args []string) error { fmt.Printf("%-20s %-20s %-25s %-10s\n", "NODENAME", "STAGE", "SENT", "LASTSEEN (s)") fmt.Printf("%s\n", strings.Repeat("=", 80)) - keys := make([]*NodeStatus, 0, len(nodeStatus.Nodes)) - wwlog.Printf(wwlog.VERBOSE, "Building sort index\n") + var statuses []*wwapiv1.NodeStatus if len(args) > 0 { - tmpMap := make(map[string]bool) nodeList := hostlist.Expand(args) - - for _, name := range nodeList { - tmpMap[name] = true - } - - for name := range nodeStatus.Nodes { - if _, ok := tmpMap[name]; ok { - keys = append(keys, nodeStatus.Nodes[name]) + for i := 0; i < len(nodeStatusResponse.NodeStatus); i++ { + for j := 0; j < len(nodeList); j++ { + if nodeStatusResponse.NodeStatus[i].NodeName == nodeList[j] { + statuses = append(statuses, nodeStatusResponse.NodeStatus[i]) + break + } } } } else { - - for name := range nodeStatus.Nodes { - keys = append(keys, nodeStatus.Nodes[name]) + for i := 0; i < len(nodeStatusResponse.NodeStatus); i++ { + statuses = append(statuses, nodeStatusResponse.NodeStatus[i]) } } wwlog.Printf(wwlog.VERBOSE, "Sorting index\n") if SetSortLast { - sort.Slice(keys, func(i, j int) bool { - if keys[i].Lastseen > keys[j].Lastseen { + sort.Slice(statuses, func(i, j int) bool { + if statuses[i].Lastseen > statuses[j].Lastseen { return true - } else if keys[i].Lastseen < keys[j].Lastseen { + } else if statuses[i].Lastseen < statuses[j].Lastseen { return false } else { - if keys[i].NodeName < keys[j].NodeName { - return true - } else { - return false - } + return statuses[i].NodeName < statuses[j].NodeName } - //return keys[i].Lastseen > keys[j].Lastseen }) - } else { - sort.Slice(keys, func(i, j int) bool { - return keys[i].NodeName < keys[j].NodeName - }) - } - - if SetSortReverse { - var tmpsort []*NodeStatus - + } else if SetSortReverse { wwlog.Printf(wwlog.VERBOSE, "Reversing sort order\n") + sort.Slice(statuses, func(i, j int) bool { + return statuses[i].NodeName > statuses[j].NodeName + }) - for l := len(keys) - 1; l >= 0; l-- { - tmpsort = append(tmpsort, keys[l]) - } - - keys = tmpsort + } else { + sort.Slice(statuses, func(i, j int) bool { + return statuses[i].NodeName < statuses[j].NodeName + }) } wwlog.Printf(wwlog.VERBOSE, "Printing results\n") - for _, o := range keys { + for i := 0; i < len(statuses); i++ { + o := statuses[i] if SetTime > 0 && o.Lastseen < SetTime { continue } @@ -158,7 +117,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { color.HiBlack("%-20s %-20s %-25s %-10s\n", o.NodeName, "--", "--", "--") } if count+4 >= height && SetWatch { - if count+1 != len(keys) { + if count+1 != len(statuses) { elipsis = true } break @@ -175,6 +134,5 @@ func CobraRunE(cmd *cobra.Command, args []string) error { break } } - - return nil + return } diff --git a/internal/app/wwctl/version/main.go b/internal/app/wwctl/version/main.go index 17e446de..ce4b9c9d 100644 --- a/internal/app/wwctl/version/main.go +++ b/internal/app/wwctl/version/main.go @@ -3,29 +3,16 @@ package version import ( "fmt" - "github.com/hpcng/warewulf/internal/pkg/buildconfig" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" "github.com/hpcng/warewulf/internal/pkg/version" "github.com/spf13/cobra" ) func CobraRunE(cmd *cobra.Command, args []string) error { - if ListFull { - fmt.Printf("%s=%s\n", "VERSION", version.GetVersion()) - fmt.Printf("%s=%s\n", "BINDIR", buildconfig.BINDIR()) - fmt.Printf("%s=%s\n", "DATADIR", buildconfig.DATADIR()) - fmt.Printf("%s=%s\n", "SYSCONFDIR", buildconfig.SYSCONFDIR()) - fmt.Printf("%s=%s\n", "LOCALSTATEDIR", buildconfig.LOCALSTATEDIR()) - fmt.Printf("%s=%s\n", "SRVDIR", buildconfig.SRVDIR()) - fmt.Printf("%s=%s\n", "TFTPDIR", buildconfig.TFTPDIR()) - fmt.Printf("%s=%s\n", "SYSTEMDDIR", buildconfig.SYSTEMDDIR()) - fmt.Printf("%s=%s\n", "WWOVERLAYDIR", buildconfig.WWOVERLAYDIR()) - fmt.Printf("%s=%s\n", "WWCHROOTDIR", buildconfig.WWCHROOTDIR()) - fmt.Printf("%s=%s\n", "WWPROVISIONDIR", buildconfig.WWPROVISIONDIR()) - fmt.Printf("%s=%s\n", "BASEVERSION", buildconfig.VERSION()) - fmt.Printf("%s=%s\n", "RELEASE", buildconfig.RELEASE()) - fmt.Printf("%s=%s\n", "WWCLIENTDIR", buildconfig.WWCLIENTDIR()) - } else { - fmt.Println(version.GetVersion()) - } + + fmt.Println("wwctl version:\t", version.GetVersion()) + + var wwVersionResponse *wwapiv1.VersionResponse = version.Version() + fmt.Println("rpc version:", wwVersionResponse.String()) return nil } diff --git a/internal/pkg/api/apiconfig/client.go b/internal/pkg/api/apiconfig/client.go new file mode 100644 index 00000000..df397975 --- /dev/null +++ b/internal/pkg/api/apiconfig/client.go @@ -0,0 +1,41 @@ +package apiconfig + +import ( + "gopkg.in/yaml.v2" + "io/ioutil" + "log" +) + +// ClientApiConfig contains configuration parameters for an API server. +type ClientApiConfig struct { + // Server is the hostname or IP address of the server to connect to. + Server string `yaml:"prefix"` + // Port is the where the API server listens. + Port uint32 `yaml:"port"` +} + +// ClientConfig is the full client configuration. +type ClientConfig struct { + ApiConfig ClientApiConfig `yaml:"api"` + TlsConfig TlsConfig `yaml:"tls"` +} + +// NewClient loads the client config from the given configFilePath. +func NewClient(configFilePath string) (config ClientConfig, err error) { + + log.Printf("Loading api client configuration from: %v\n", configFilePath) + + var fileBytes []byte + fileBytes, err = ioutil.ReadFile(configFilePath) + if err != nil { + return + } + + err = yaml.Unmarshal(fileBytes, &config) + if err != nil { + return + } + + log.Printf("api client config: %#v\n", config) + return +} diff --git a/internal/pkg/api/apiconfig/client_server.go b/internal/pkg/api/apiconfig/client_server.go new file mode 100644 index 00000000..89e035ca --- /dev/null +++ b/internal/pkg/api/apiconfig/client_server.go @@ -0,0 +1,37 @@ +package apiconfig + +import ( + "gopkg.in/yaml.v2" + "io/ioutil" + "log" +) + +// ClientServerConfig is the full client server configuration. +// wwapird is a client of wwapid. +// wwapird serves REST. (WareWulf API Rest Daemon) +type ClientServerConfig struct { + ClientApiConfig ClientApiConfig `yaml:"clientapi"` + ServerApiConfig ServerApiConfig `yaml:"serverapi"` + ClientTlsConfig TlsConfig `yaml:"clienttls"` + ServerTlsConfig TlsConfig `yaml:"servertls"` +} + +// NewClientServer loads the client config from the given configFilePath. +func NewClientServer(configFilePath string) (config ClientServerConfig, err error) { + + log.Printf("Loading api client server configuration from: %v\n", configFilePath) + + var fileBytes []byte + fileBytes, err = ioutil.ReadFile(configFilePath) + if err != nil { + return + } + + err = yaml.Unmarshal(fileBytes, &config) + if err != nil { + return + } + + log.Printf("api client server config: %#v\n", config) + return +} diff --git a/internal/pkg/api/apiconfig/container/container.go b/internal/pkg/api/apiconfig/container/container.go new file mode 100644 index 00000000..e4242e7d --- /dev/null +++ b/internal/pkg/api/apiconfig/container/container.go @@ -0,0 +1,379 @@ +package container + +import ( + "fmt" + "os" + "path" + "strconv" + "strings" + + "github.com/containers/image/v5/types" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/container" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/warewulfd" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/pkg/errors" +) + +// TODO: SynchUser is not NYI in the API: See https://github.com/hpcng/warewulf/blob/main/internal/app/wwctl/container/syncuser/main.go + +func ContainerBuild(cbp *wwapiv1.ContainerBuildParameter) (err error) { + + if cbp == nil { + return fmt.Errorf("ContainerBuildParameter is nil") + } + + var containers []string + + if cbp.All { + containers, err = container.ListSources() + } else { + containers = cbp.ContainerNames + } + + if len(containers) == 0 { + return + } + + for _, c := range containers { + if !container.ValidSource(c) { + err = fmt.Errorf("VNFS name does not exist: %s", c) + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return + } + + err = container.Build(c, cbp.Force) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not build container %s: %s\n", c, err) + return + } + } + + if cbp.Default { + if len(containers) != 1 { + wwlog.Printf(wwlog.ERROR, "Can only set default for one container\n") + } else { + var nodeDB node.NodeYaml + nodeDB, err = node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) + return + } + + //TODO: Don't loop through profiles, instead have a nodeDB function that goes directly to the map + profiles, _ := nodeDB.FindAllProfiles() + for _, profile := range profiles { + wwlog.Printf(wwlog.DEBUG, "Looking for profile default: %s\n", profile.Id.Get()) + if profile.Id.Get() == "default" { + wwlog.Printf(wwlog.DEBUG, "Found profile default, setting container name to: %s\n", containers[0]) + profile.ContainerName.Set(containers[0]) + err := nodeDB.ProfileUpdate(profile) + if err != nil { + return errors.Wrap(err, "failed to update node profile") + } + } + } + // TODO: Need a wrapper and flock around this. Sometimes we restart warewulfd and sometimes we don't. + err = nodeDB.Persist() + if err != nil { + return errors.Wrap(err, "failed to persist nodedb") + } + fmt.Printf("Set default profile to container: %s\n", containers[0]) + } + } + return +} + +func ContainerDelete(cdp *wwapiv1.ContainerDeleteParameter) (err error) { + + if cdp == nil { + return fmt.Errorf("ContainerDeleteParameter is nil") + } + + nodeDB, err := node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not open nodeDB: %s\n", err) + return + } + + nodes, err := nodeDB.FindAllNodes() + if err != nil { + return + } + +ARG_LOOP: + for i := 0; i < len(cdp.ContainerNames); i++ { + //_, arg := range args { + containerName := cdp.ContainerNames[i] + for _, n := range nodes { + if n.ContainerName.Get() == containerName { + wwlog.Printf(wwlog.ERROR, "Container is configured for nodes, skipping: %s\n", containerName) + continue ARG_LOOP + } + } + + if !container.ValidSource(containerName) { + wwlog.Printf(wwlog.ERROR, "Container name is not a valid source: %s\n", containerName) + continue + } + err := container.DeleteSource(containerName) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not remove source: %s\n", containerName) + } else { + fmt.Printf("Container has been deleted: %s\n", containerName) + } + } + + return +} + +func ContainerImport(cip *wwapiv1.ContainerImportParameter) (containerName string, err error) { + + if cip == nil { + err = fmt.Errorf("NodeAddParameter is nil") + return + } + + if cip.Name == "" { + name := path.Base(cip.Source) + fmt.Printf("Setting VNFS name: %s\n", name) + cip.Name = name + } + if !container.ValidName(cip.Name) { + err = fmt.Errorf("VNFS name contains illegal characters: %s", cip.Name) + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return + } + + containerName = cip.Name + fullPath := container.SourceDir(cip.Name) + + if util.IsDir(fullPath) { + if cip.Force { + fmt.Printf("Overwriting existing VNFS\n") + err = os.RemoveAll(fullPath) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return + } + } else if cip.Update { + fmt.Printf("Updating existing VNFS\n") + } else { + err = fmt.Errorf("VNFS Name exists, specify --force, --update, or choose a different name: %s", cip.Name) + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return + } + } else if strings.HasPrefix(cip.Source, "docker://") || strings.HasPrefix(cip.Source, "docker-daemon://") { + var sCtx *types.SystemContext + sCtx, err = getSystemContext() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + // return was missing here. Was that deliberate? + } + + err = container.ImportDocker(cip.Source, cip.Name, sCtx) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not import image: %s\n", err) + _ = container.DeleteSource(cip.Name) + return + } + } else if util.IsDir(cip.Source) { + err = container.ImportDirectory(cip.Source, cip.Name) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not import image: %s\n", err) + _ = container.DeleteSource(cip.Name) + return + } + } else { + err = fmt.Errorf("Invalid dir or uri: %s", cip.Source) + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return + } + + fmt.Printf("Updating the container's /etc/resolv.conf\n") + err = util.CopyFile("/etc/resolv.conf", path.Join(container.RootFsDir(cip.Name), "/etc/resolv.conf")) + if err != nil { + wwlog.Printf(wwlog.WARN, "Could not copy /etc/resolv.conf into container: %s\n", err) + } + + fmt.Printf("Building container: %s\n", cip.Name) + err = container.Build(cip.Name, true) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not build container %s: %s\n", cip.Name, err) + return + } + + if cip.Default { + var nodeDB node.NodeYaml + nodeDB, err = node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) + return + } + + //TODO: Don't loop through profiles, instead have a nodeDB function that goes directly to the map + profiles, _ := nodeDB.FindAllProfiles() + for _, profile := range profiles { + wwlog.Printf(wwlog.DEBUG, "Looking for profile default: %s\n", profile.Id.Get()) + if profile.Id.Get() == "default" { + wwlog.Printf(wwlog.DEBUG, "Found profile default, setting container name to: %s\n", cip.Name) + profile.ContainerName.Set(cip.Name) + err = nodeDB.ProfileUpdate(profile) + if err != nil { + err = errors.Wrap(err, "failed to update profile") + return + } + } + } + // TODO: We need this in a function with a flock around it. + // Also need to understand if the daemon restart is only to + // reload the config or if there is something more. + err = nodeDB.Persist() + if err != nil { + err = errors.Wrap(err, "failed to persist nodedb") + return + } + + fmt.Printf("Set default profile to container: %s\n", cip.Name) + err = warewulfd.DaemonReload() + if err != nil { + err = errors.Wrap(err, "failed to reload warewulf daemon") + return + } + } + return +} + +func ContainerList() (containerInfo []*wwapiv1.ContainerInfo, err error) { + var sources []string + + sources, err = container.ListSources() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return + } + + nodeDB, err := node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return + } + + nodes, err := nodeDB.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return + } + + nodemap := make(map[string]int) + for _, n := range nodes { + nodemap[n.ContainerName.Get()]++ + } + + for _, source := range sources { + if nodemap[source] == 0 { + nodemap[source] = 0 + } + + wwlog.Printf(wwlog.DEBUG, "Finding kernel version for: %s\n", source) + kernelVersion := container.KernelVersion(source) + + containerInfo = append(containerInfo, &wwapiv1.ContainerInfo{ + Name: source, + NodeCount: uint32(nodemap[source]), + KernelVersion: kernelVersion, + }) + } + return +} + +func ContainerShow(csp *wwapiv1.ContainerShowParameter) (response *wwapiv1.ContainerShowResponse, err error) { + + containerName := csp.ContainerName + + if !container.ValidName(containerName) { + err = fmt.Errorf("%s is not a valid container", containerName) + return + } + + rootFsDir := container.RootFsDir(containerName) + + nodeDB, err := node.New() + if err != nil { + return + } + + nodes, err := nodeDB.FindAllNodes() + if err != nil { + return + } + + var nodeList []string + for _, n := range nodes { + if n.ContainerName.Get() == containerName { + + nodeList = append(nodeList, n.Id.Get()) + } + } + + response = &wwapiv1.ContainerShowResponse{ + Name: containerName, + Rootfs: rootFsDir, + Nodes: nodeList, + } + return +} + +// Private helpers + +func setOCICredentials(sCtx *types.SystemContext) error { + username, userSet := os.LookupEnv("WAREWULF_OCI_USERNAME") + password, passSet := os.LookupEnv("WAREWULF_OCI_PASSWORD") + if userSet || passSet { + if userSet && passSet { + sCtx.DockerAuthConfig = &types.DockerAuthConfig{ + Username: username, + Password: password, + } + } else { + return fmt.Errorf("oci username and password env vars must be specified together") + } + } + return nil +} + +func setNoHTTPSOpts(sCtx *types.SystemContext) error { + val, ok := os.LookupEnv("WAREWULF_OCI_NOHTTPS") + if !ok { + return nil + } + + noHTTPS, err := strconv.ParseBool(val) + if err != nil { + return fmt.Errorf("while parsing insecure http option: %v", err) + } + + // only set this if we want to disable, otherwise leave as undefined + if noHTTPS { + sCtx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(true) + } + sCtx.OCIInsecureSkipTLSVerify = noHTTPS + + return nil +} + +func getSystemContext() (sCtx *types.SystemContext, err error) { + sCtx = &types.SystemContext{} + + if err := setOCICredentials(sCtx); err != nil { + return nil, err + } + + if err := setNoHTTPSOpts(sCtx); err != nil { + return nil, err + } + + return sCtx, nil +} \ No newline at end of file diff --git a/internal/pkg/api/apiconfig/server.go b/internal/pkg/api/apiconfig/server.go new file mode 100644 index 00000000..80f7006c --- /dev/null +++ b/internal/pkg/api/apiconfig/server.go @@ -0,0 +1,43 @@ +package apiconfig + +import ( + "gopkg.in/yaml.v2" + "io/ioutil" + "log" +) + +// ServerApiConfig contains configuration parameters for an API server. +type ServerApiConfig struct { + // Version contains the full version of the API server, eg: 1.0.0. + Version string `yaml:"version"` + // Prefix contains the version url prefix for the API server, eg: v1. + Prefix string `yaml:"prefix"` + // Port is the where the API server listens. + Port uint32 `yaml:"port"` +} + +// ServerConfig is the full server configuration. +type ServerConfig struct { + ApiConfig ServerApiConfig `yaml:"api"` + TlsConfig TlsConfig `yaml:"tls"` +} + +// NewServer loads the server config from the given configFilePath. +func NewServer(configFilePath string) (config ServerConfig, err error) { + + log.Printf("Loading api server configuration from: %v\n", configFilePath) + + var fileBytes []byte + fileBytes, err = ioutil.ReadFile(configFilePath) + if err != nil { + return + } + + err = yaml.Unmarshal(fileBytes, &config) + if err != nil { + return + } + + log.Printf("api server config: %#v\n", config) + return +} diff --git a/internal/pkg/api/apiconfig/tls.go b/internal/pkg/api/apiconfig/tls.go new file mode 100644 index 00000000..1228cc8c --- /dev/null +++ b/internal/pkg/api/apiconfig/tls.go @@ -0,0 +1,20 @@ +package apiconfig + +// TlsConfig contains TLS configuration parameters for a client or server. +type TlsConfig struct { + // Enabled is true when secure. + Enabled bool `yaml:"enabled"` + // Cert is the path to the client or server certificate file. + Cert string `yaml:"cert,omitempty"` + // Key is the path to the client or server key file. + Key string `yaml:"key,omitempty"` + // CaCert is the path the CA certificate file. + CaCert string `yaml:"cacert,omitempty"` + // ConcatCert is for wwapird. http.ListenAndServeTLS wants the following + // cert file, so in our case this file contains `cat ${Cert} ${CaCert}` + // + // If the certificate is signed by a certificate authority, the certFile + // should be the concatenation of the server's certificate, any + // intermediates, and the CA's certificate + ConcatCert string `yaml:"concatcert,omitempty"` +} \ No newline at end of file diff --git a/internal/pkg/api/container/container.go b/internal/pkg/api/container/container.go new file mode 100644 index 00000000..4c0296b7 --- /dev/null +++ b/internal/pkg/api/container/container.go @@ -0,0 +1,395 @@ +package container + +import ( + "fmt" + "os" + "path" + "strconv" + "strings" + + "github.com/containers/image/v5/types" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/container" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/warewulfd" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/pkg/errors" +) + +func ContainerBuild(cbp *wwapiv1.ContainerBuildParameter) (err error) { + + if cbp == nil { + return fmt.Errorf("ContainerBuildParameter is nil") + } + + var containers []string + + if cbp.All { + containers, err = container.ListSources() + } else { + containers = cbp.ContainerNames + } + + if len(containers) == 0 { + return + } + + for _, c := range containers { + if !container.ValidSource(c) { + err = fmt.Errorf("VNFS name does not exist: %s", c) + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return + } + + err = container.Build(c, cbp.Force) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not build container %s: %s\n", c, err) + return + } + } + + if cbp.Default { + if len(containers) != 1 { + wwlog.Printf(wwlog.ERROR, "Can only set default for one container\n") + } else { + var nodeDB node.NodeYaml + nodeDB, err = node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) + return + } + + //TODO: Don't loop through profiles, instead have a nodeDB function that goes directly to the map + profiles, _ := nodeDB.FindAllProfiles() + for _, profile := range profiles { + wwlog.Printf(wwlog.DEBUG, "Looking for profile default: %s\n", profile.Id.Get()) + if profile.Id.Get() == "default" { + wwlog.Printf(wwlog.DEBUG, "Found profile default, setting container name to: %s\n", containers[0]) + profile.ContainerName.Set(containers[0]) + err := nodeDB.ProfileUpdate(profile) + if err != nil { + return errors.Wrap(err, "failed to update node profile") + } + } + } + // TODO: Need a wrapper and flock around this. Sometimes we restart warewulfd and sometimes we don't. + err = nodeDB.Persist() + if err != nil { + return errors.Wrap(err, "failed to persist nodedb") + } + fmt.Printf("Set default profile to container: %s\n", containers[0]) + } + } + return +} + +func ContainerDelete(cdp *wwapiv1.ContainerDeleteParameter) (err error) { + + if cdp == nil { + return fmt.Errorf("ContainerDeleteParameter is nil") + } + + nodeDB, err := node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not open nodeDB: %s\n", err) + return + } + + nodes, err := nodeDB.FindAllNodes() + if err != nil { + return + } + +ARG_LOOP: + for i := 0; i < len(cdp.ContainerNames); i++ { + //_, arg := range args { + containerName := cdp.ContainerNames[i] + for _, n := range nodes { + if n.ContainerName.Get() == containerName { + wwlog.Printf(wwlog.ERROR, "Container is configured for nodes, skipping: %s\n", containerName) + continue ARG_LOOP + } + } + + if !container.ValidSource(containerName) { + wwlog.Printf(wwlog.ERROR, "Container name is not a valid source: %s\n", containerName) + continue + } + err := container.DeleteSource(containerName) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not remove source: %s\n", containerName) + } else { + fmt.Printf("Container has been deleted: %s\n", containerName) + } + } + + return +} + +func ContainerImport(cip *wwapiv1.ContainerImportParameter) (containerName string, err error) { + + if cip == nil { + err = fmt.Errorf("NodeAddParameter is nil") + return + } + + if cip.Name == "" { + name := path.Base(cip.Source) + wwlog.Info("Setting VNFS name: %s", name) + cip.Name = name + } + if !container.ValidName(cip.Name) { + err = fmt.Errorf("VNFS name contains illegal characters: %s", cip.Name) + wwlog.Error(err.Error()) + return + } + + containerName = cip.Name + fullPath := container.SourceDir(cip.Name) + + if util.IsDir(fullPath) { + if cip.Force { + wwlog.Info("Overwriting existing VNFS") + err = os.RemoveAll(fullPath) + if err != nil { + wwlog.ErrorExc(err, "") + return + } + } else if cip.Update { + wwlog.Info("Updating existing VNFS") + } else { + err = fmt.Errorf("VNFS Name exists, specify --force, --update, or choose a different name: %s", cip.Name) + wwlog.Error(err.Error()) + return + } + } else if strings.HasPrefix(cip.Source, "docker://") || strings.HasPrefix(cip.Source, "docker-daemon://") { + var sCtx *types.SystemContext + sCtx, err = getSystemContext() + if err != nil { + wwlog.ErrorExc(err, "") + // TODO: mhink - return was missing here. Was that deliberate? + } + + err = container.ImportDocker(cip.Source, cip.Name, sCtx) + if err != nil { + err = fmt.Errorf("could not import image: %s", err.Error()) + wwlog.Error(err.Error()) + _ = container.DeleteSource(cip.Name) + return + } + } else if util.IsDir(cip.Source) { + err = container.ImportDirectory(cip.Source, cip.Name) + if err != nil { + err = fmt.Errorf("could not import image: %s", err.Error()) + wwlog.Error(err.Error()) + _ = container.DeleteSource(cip.Name) + return + } + } else { + err = fmt.Errorf("invalid dir or uri: %s", cip.Source) + wwlog.Error(err.Error()) + return + } + + wwlog.Info("Updating the container's /etc/resolv.conf") + err = util.CopyFile("/etc/resolv.conf", path.Join(container.RootFsDir(cip.Name), "/etc/resolv.conf")) + if err != nil { + wwlog.Printf(wwlog.WARN, "Could not copy /etc/resolv.conf into container: %s\n", err) + } + + err = container.SyncUids(cip.Name, !cip.SyncUser) + if err != nil && !cip.SyncUser { + err = fmt.Errorf("error in user sync, fix error and run 'syncuser' manually: %s", err) + wwlog.Error(err.Error()) + return + } + + wwlog.Info("Building container: %s", cip.Name) + err = container.Build(cip.Name, true) + if err != nil { + err = fmt.Errorf("could not build container %s: %s", cip.Name, err.Error()) + wwlog.Error(err.Error()) + return + } + + if cip.Default { + var nodeDB node.NodeYaml + nodeDB, err = node.New() + if err != nil { + err = fmt.Errorf("could not open node configuration: %s", err.Error()) + wwlog.Error(err.Error()) + return + } + + //TODO: Don't loop through profiles, instead have a nodeDB function that goes directly to the map + profiles, _ := nodeDB.FindAllProfiles() + for _, profile := range profiles { + wwlog.Printf(wwlog.DEBUG, "Looking for profile default: %s", profile.Id.Get()) + if profile.Id.Get() == "default" { + wwlog.Printf(wwlog.DEBUG, "Found profile default, setting container name to: %s", cip.Name) + profile.ContainerName.Set(cip.Name) + err = nodeDB.ProfileUpdate(profile) + if err != nil { + err = errors.Wrap(err, "failed to update profile") + return + } + } + } + // TODO: We need this in a function with a flock around it. + // Also need to understand if the daemon restart is only to + // reload the config or if there is something more. + err = nodeDB.Persist() + if err != nil { + err = errors.Wrap(err, "failed to persist nodedb") + return + } + + wwlog.Info("Set default profile to container: %s", cip.Name) + err = warewulfd.DaemonReload() + if err != nil { + err = errors.Wrap(err, "failed to reload warewulf daemon") + return + } + } + return +} + +func ContainerList() (containerInfo []*wwapiv1.ContainerInfo, err error) { + var sources []string + + sources, err = container.ListSources() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return + } + + nodeDB, err := node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return + } + + nodes, err := nodeDB.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return + } + + nodemap := make(map[string]int) + for _, n := range nodes { + nodemap[n.ContainerName.Get()]++ + } + + for _, source := range sources { + if nodemap[source] == 0 { + nodemap[source] = 0 + } + + wwlog.Printf(wwlog.DEBUG, "Finding kernel version for: %s\n", source) + kernelVersion := container.KernelVersion(source) + + containerInfo = append(containerInfo, &wwapiv1.ContainerInfo{ + Name: source, + NodeCount: uint32(nodemap[source]), + KernelVersion: kernelVersion, + }) + } + return +} + +func ContainerShow(csp *wwapiv1.ContainerShowParameter) (response *wwapiv1.ContainerShowResponse, err error) { + + containerName := csp.ContainerName + + if !container.ValidName(containerName) { + err = fmt.Errorf("%s is not a valid container", containerName) + return + } + + rootFsDir := container.RootFsDir(containerName) + + kernelVersion := container.KernelVersion(containerName) + if kernelVersion != "" { + kernelVersion = "not found" + fmt.Printf("Kernelversion: %s\n", kernelVersion) + } + + nodeDB, err := node.New() + if err != nil { + return + } + + nodes, err := nodeDB.FindAllNodes() + if err != nil { + return + } + + var nodeList []string + for _, n := range nodes { + if n.ContainerName.Get() == containerName { + + nodeList = append(nodeList, n.Id.Get()) + } + } + + response = &wwapiv1.ContainerShowResponse{ + Name: containerName, + Rootfs: rootFsDir, + Nodes: nodeList, + KernelVersion: kernelVersion, + } + return +} + +// Private helpers + +func setOCICredentials(sCtx *types.SystemContext) error { + username, userSet := os.LookupEnv("WAREWULF_OCI_USERNAME") + password, passSet := os.LookupEnv("WAREWULF_OCI_PASSWORD") + if userSet || passSet { + if userSet && passSet { + sCtx.DockerAuthConfig = &types.DockerAuthConfig{ + Username: username, + Password: password, + } + } else { + return fmt.Errorf("oci username and password env vars must be specified together") + } + } + return nil +} + +func setNoHTTPSOpts(sCtx *types.SystemContext) error { + val, ok := os.LookupEnv("WAREWULF_OCI_NOHTTPS") + if !ok { + return nil + } + + noHTTPS, err := strconv.ParseBool(val) + if err != nil { + return fmt.Errorf("while parsing insecure http option: %v", err) + } + + // only set this if we want to disable, otherwise leave as undefined + if noHTTPS { + sCtx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(true) + } + sCtx.OCIInsecureSkipTLSVerify = noHTTPS + + return nil +} + +func getSystemContext() (sCtx *types.SystemContext, err error) { + sCtx = &types.SystemContext{} + + if err := setOCICredentials(sCtx); err != nil { + return nil, err + } + + if err := setNoHTTPSOpts(sCtx); err != nil { + return nil, err + } + + return sCtx, nil +} diff --git a/internal/pkg/api/node/node.go b/internal/pkg/api/node/node.go new file mode 100644 index 00000000..cf1faaaa --- /dev/null +++ b/internal/pkg/api/node/node.go @@ -0,0 +1,988 @@ +package node + +import ( + "encoding/json" + "fmt" + "net" + "net/http" + "os" + "strconv" + "strings" + + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/warewulfconf" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" + "github.com/pkg/errors" + + "github.com/hpcng/warewulf/internal/pkg/warewulfd" +) + +// NodeAdd adds nodes for management by Warewulf. +func NodeAdd(nap *wwapiv1.NodeAddParameter) (err error) { + + if nap == nil { + return fmt.Errorf("NodeAddParameter is nil") + } + + var count uint + nodeDB, err := node.New() + if err != nil { + return errors.Wrap(err, "failed to open node database") + } + + node_args := hostlist.Expand(nap.NodeNames) + + for _, a := range node_args { + var n node.NodeInfo + n, err = nodeDB.AddNode(a) + if err != nil { + return errors.Wrap(err, "failed to add node") + } + wwlog.Printf(wwlog.INFO, "Added node: %s\n", a) + + if nap.Cluster != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting cluster name to: %s\n", n.Id.Get(), nap.Cluster) + n.ClusterName.Set(nap.Cluster) + err = nodeDB.NodeUpdate(n) + if err != nil { + return errors.Wrap(err, "failed to update node") + } + } + + if nap.Netdev != "" { + err = checkNetNameRequired(nap.Netname) + if err != nil { + return + } + + if _, ok := n.NetDevs[nap.Netname]; !ok { + var netdev node.NetDevEntry + n.NetDevs[nap.Netname] = &netdev + } + + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Device to: %s\n", n.Id.Get(), nap.Netname, nap.Netdev) + + n.NetDevs[nap.Netname].Device.Set(nap.Netdev) + n.NetDevs[nap.Netname].OnBoot.SetB(true) + } + + if nap.Ipaddr != "" { + err = checkNetNameRequired(nap.Netname) + if err != nil { + return + } + + NewIpaddr := util.IncrementIPv4(nap.Ipaddr, count) + + if _, ok := n.NetDevs[nap.Netname]; !ok { + var netdev node.NetDevEntry + n.NetDevs[nap.Netname] = &netdev + } + + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Ipaddr to: %s\n", n.Id.Get(), nap.Netname, NewIpaddr) + + n.NetDevs[nap.Netname].Ipaddr.Set(NewIpaddr) + n.NetDevs[nap.Netname].OnBoot.SetB(true) + } + + if nap.Netmask != "" { + err = checkNetNameRequired(nap.Netname) + if err != nil { + return + } + + if _, ok := n.NetDevs[nap.Netname]; !ok { + return errors.New("network device does not exist: " + nap.Netname) + } + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting netmask to: %s\n", n.Id.Get(), nap.Netname, nap.Netmask) + + n.NetDevs[nap.Netname].Netmask.Set(nap.Netmask) + } + + if nap.Gateway != "" { + err = checkNetNameRequired(nap.Netname) + if err != nil { + return + } + + if _, ok := n.NetDevs[nap.Netname]; !ok { + return errors.New("network device does not exist: " + nap.Netname) + } + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting gateway to: %s\n", n.Id.Get(), nap.Netname, nap.Gateway) + + n.NetDevs[nap.Netname].Gateway.Set(nap.Gateway) + } + + if nap.Hwaddr != "" { + if nap.Netname == "" { + return errors.New("you must include the '--netname' option") + } + + if _, ok := n.NetDevs[nap.Netname]; !ok { + return errors.New("network device does not exist: " + nap.Netname) + } + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting HW address to: %s\n", n.Id.Get(), nap.Netname, nap.Hwaddr) + + n.NetDevs[nap.Netname].Hwaddr.Set(nap.Hwaddr) + n.NetDevs[nap.Netname].OnBoot.SetB(true) + } + + if nap.Type != "" { + if nap.Netname == "" { + return errors.New("you must include the '--netname' option") + } + + if _, ok := n.NetDevs[nap.Netname]; !ok { + return errors.New("network device does not exist: " + nap.Netname) + } + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Type to: %s\n", n.Id.Get(), nap.Netname, nap.Type) + + n.NetDevs[nap.Netname].Type.Set(nap.Type) + } + + if nap.Ipaddr6 != "" { + if nap.Netname == "" { + return errors.New("you must include the '--netname' option") + } + if _, ok := n.NetDevs[nap.Netname]; !ok { + return errors.New("network device does not exist: " + nap.Netname) + } + // just check if address is a valid ipv6 CIDR address + if _, _, err := net.ParseCIDR(nap.Ipaddr6); err != nil { + return errors.Errorf("%s is not a valid ipv6 address in CIDR notation", nap.Ipaddr6) + } + } + + if nap.Discoverable { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting node to discoverable\n", n.Id.Get()) + + n.Discoverable.SetB(true) + } + + err = nodeDB.NodeUpdate(n) + if err != nil { + return errors.Wrap(err, "failed to update nodedb") + } + + count++ + } // end for + + err = nodeDB.Persist() + if err != nil { + return errors.Wrap(err, "failed to persist new node") + } + + err = warewulfd.DaemonReload() + if err != nil { + return errors.Wrap(err, "failed to reload warewulf daemon") + } + return +} + +// NodeDelete adds nodes for management by Warewulf. +func NodeDelete(ndp *wwapiv1.NodeDeleteParameter) (err error) { + + var nodeList []node.NodeInfo + nodeList, err = NodeDeleteParameterCheck(ndp, false) + if err != nil { + return + } + + nodeDB, err := node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Failed to open node database: %s\n", err) + return + } + + for _, n := range nodeList { + err := nodeDB.DelNode(n.Id.Get()) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + } else { + //count++ + fmt.Printf("Deleting node: %s\n", n.Id.Print()) + } + } + + err = nodeDB.Persist() + if err != nil { + return errors.Wrap(err, "failed to persist nodedb") + } + + err = warewulfd.DaemonReload() + if err != nil { + return errors.Wrap(err, "failed to reload warewulf daemon") + } + return +} + +// NodeDeleteParameterCheck does error checking on NodeDeleteParameter. +// Output to the console if console is true. +// Returns the nodes to delete. +func NodeDeleteParameterCheck(ndp *wwapiv1.NodeDeleteParameter, console bool) (nodeList []node.NodeInfo, err error) { + + if ndp == nil { + err = fmt.Errorf("NodeDeleteParameter is nil") + return + } + + nodeDB, err := node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Failed to open node database: %s\n", err) + return + } + + nodes, err := nodeDB.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not get node list: %s\n", err) + return + } + + node_args := hostlist.Expand(ndp.NodeNames) + + for _, r := range node_args { + var match bool + for _, n := range nodes { + if n.Id.Get() == r { + nodeList = append(nodeList, n) + match = true + } + } + + if !match { + fmt.Fprintf(os.Stderr, "ERROR: No match for node: %s\n", r) + } + } + + if len(nodeList) == 0 { + fmt.Printf("No nodes found\n") + } + return +} + +// NodeList lists all to none of the nodes managed by Warewulf. +func NodeList(nodeNames []string) (nodeInfo []*wwapiv1.NodeInfo, err error) { + + // nil is okay for nodeNames + + nodeDB, err := node.New() + if err != nil { + return + } + + nodes, err := nodeDB.FindAllNodes() + if err != nil { + return + } + + nodeNames = hostlist.Expand(nodeNames) + + // Translate to the protobuf structure so wwapiv1 can use this across the wire. + // This is the same logic as was in wwctl. + for _, node := range node.FilterByName(nodes, nodeNames) { + + var ni wwapiv1.NodeInfo + + ni.Id = &wwapiv1.NodeField{ + Source: node.Id.Source(), + Value: node.Id.Get(), + Print: node.Id.Print(), + } + + ni.Comment = &wwapiv1.NodeField{ + Source: node.Comment.Source(), + Value: node.Comment.Get(), + Print: node.Comment.Print(), + } + + ni.Cluster = &wwapiv1.NodeField{ + Source: node.ClusterName.Source(), + Value: node.ClusterName.Get(), + Print: node.ClusterName.Print(), + } + + ni.Profiles = node.Profiles + + ni.Discoverable = &wwapiv1.NodeField{ + Source: node.Discoverable.Source(), + Value: strconv.FormatBool(node.Discoverable.GetB()), + Print: node.Discoverable.PrintB(), + } + + ni.Container = &wwapiv1.NodeField{ + Source: node.ContainerName.Source(), + Value: node.ContainerName.Get(), + Print: node.ContainerName.Print(), + } + + ni.KernelOverride = &wwapiv1.NodeField{ + Source: node.Kernel.Override.Source(), + Value: node.Kernel.Override.Get(), + Print: node.Kernel.Override.Print(), + } + + ni.KernelArgs = &wwapiv1.NodeField{ + Source: node.Kernel.Args.Source(), + Value: node.Kernel.Args.Get(), + Print: node.Kernel.Args.Print(), + } + + ni.SystemOverlay = &wwapiv1.NodeField{ + Source: node.SystemOverlay.Source(), + Value: node.SystemOverlay.Get(), + Print: node.SystemOverlay.Print(), + } + + ni.RuntimeOverlay = &wwapiv1.NodeField{ + Source: node.RuntimeOverlay.Source(), + Value: node.RuntimeOverlay.Get(), + Print: node.RuntimeOverlay.Print(), + } + + ni.Ipxe = &wwapiv1.NodeField{ + Source: node.Ipxe.Source(), + Value: node.Ipxe.Get(), + Print: node.Ipxe.Print(), + } + + ni.Init = &wwapiv1.NodeField{ + Source: node.Init.Source(), + Value: node.Init.Get(), + Print: node.Init.Print(), + } + + ni.Root = &wwapiv1.NodeField{ + Source: node.Root.Source(), + Value: node.Root.Get(), + Print: node.Root.Print(), + } + + ni.AssetKey = &wwapiv1.NodeField{ + Source: node.AssetKey.Source(), + Value: node.AssetKey.Get(), + Print: node.AssetKey.Print(), + } + + ni.IpmiIpaddr = &wwapiv1.NodeField{ + Source: node.Ipmi.Ipaddr.Source(), + Value: node.Ipmi.Ipaddr.Get(), + Print: node.Ipmi.Ipaddr.Print(), + } + + ni.IpmiNetmask = &wwapiv1.NodeField{ + Source: node.Ipmi.Netmask.Source(), + Value: node.Ipmi.Netmask.Get(), + Print: node.Ipmi.Netmask.Print(), + } + + ni.IpmiPort = &wwapiv1.NodeField{ + Source: node.Ipmi.Port.Source(), + Value: node.Ipmi.Port.Get(), + Print: node.Ipmi.Port.Print(), + } + + ni.IpmiGateway = &wwapiv1.NodeField{ + Source: node.Ipmi.Gateway.Source(), + Value: node.Ipmi.Gateway.Get(), + Print: node.Ipmi.Gateway.Print(), + } + + ni.IpmiUserName = &wwapiv1.NodeField{ + Source: node.Ipmi.UserName.Source(), + Value: node.Ipmi.UserName.Get(), + Print: node.Ipmi.UserName.Print(), + } + + ni.IpmiPassword = &wwapiv1.NodeField{ + Source: node.Ipmi.Password.Source(), + Value: node.Ipmi.Password.Get(), + Print: node.Ipmi.Password.Print(), // TODO: Password was removed from pprinted output, at least in some places. + } + + ni.IpmiInterface = &wwapiv1.NodeField{ + Source: node.Ipmi.Interface.Source(), + Value: node.Ipmi.Interface.Get(), + Print: node.Ipmi.Interface.Print(), + } + + for keyname, keyvalue := range node.Tags { + ni.Tags[keyname].Source = keyvalue.Source() + ni.Tags[keyname].Value = keyvalue.Get() + ni.Tags[keyname].Print = keyvalue.Print() + } + + ni.NetDevs = map[string]*wwapiv1.NetDev{} + for name, netdev := range node.NetDevs { + + ni.NetDevs[name] = &wwapiv1.NetDev{ + Device: &wwapiv1.NodeField{ + Source: netdev.Device.Source(), + Value: netdev.Device.Get(), + Print: netdev.Device.Print(), + }, + Hwaddr: &wwapiv1.NodeField{ + Source: netdev.Hwaddr.Source(), + Value: netdev.Hwaddr.Get(), + Print: netdev.Hwaddr.Print(), + }, + Ipaddr: &wwapiv1.NodeField{ + Source: netdev.Ipaddr.Source(), + Value: netdev.Ipaddr.Get(), + Print: netdev.Ipaddr.Print(), + }, + Netmask: &wwapiv1.NodeField{ + Source: netdev.Netmask.Source(), + Value: netdev.Netmask.Get(), + Print: netdev.Netmask.Print(), + }, + Gateway: &wwapiv1.NodeField{ + Source: netdev.Gateway.Source(), + Value: netdev.Gateway.Get(), + Print: netdev.Gateway.Print(), + }, + Type: &wwapiv1.NodeField{ + Source: netdev.Type.Source(), + Value: netdev.Type.Get(), + Print: netdev.Type.Print(), + }, + Onboot: &wwapiv1.NodeField{ + Source: netdev.OnBoot.Source(), + Value: strconv.FormatBool(netdev.OnBoot.GetB()), + Print: netdev.OnBoot.PrintB(), + }, + Primary: &wwapiv1.NodeField{ + Source: netdev.Primary.Source(), + Value: strconv.FormatBool(netdev.Primary.GetB()), + Print: netdev.Primary.PrintB(), + }, + } + } + nodeInfo = append(nodeInfo, &ni) + } + return +} + +// NodeSet is the wwapiv1 implmentation for updating node fields. +func NodeSet(set *wwapiv1.NodeSetParameter) (err error) { + + if set == nil { + return fmt.Errorf("NodeAddParameter is nil") + } + + var nodeDB node.NodeYaml + nodeDB, _, err = NodeSetParameterCheck(set, false) + if err != nil { + return + } + return nodeDbSave(&nodeDB) +} + +// NodeSetParameterCheck does error checking on NodeSetParameter. +// Output to the console if console is true. +// TODO: Determine if the console switch does wwlog or not. +// - console may end up being textOutput? +func NodeSetParameterCheck(set *wwapiv1.NodeSetParameter, console bool) (nodeDB node.NodeYaml, nodeCount uint, err error) { + + if set == nil { + err = fmt.Errorf("Node set parameter is nil") + if console { + fmt.Printf("%v\n", err) + return + } + } + + if set.NodeNames == nil { + err = fmt.Errorf("Node set parameter: NodeNames is nil") + if console { + fmt.Printf("%v\n", err) + return + } + } + + nodeDB, err = node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) + return + } + + nodes, err := nodeDB.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not get node list: %s\n", err) + return + } + + // Note: This does not do expansion on the nodes. + + if set.AllNodes || (len(set.NodeNames) == 0 && len(nodes) > 0) { + if console { + fmt.Printf("\n*** WARNING: This command will modify all nodes! ***\n\n") + } + } else { + nodes = node.FilterByName(nodes, set.NodeNames) + } + + if len(nodes) == 0 { + if console { + fmt.Printf("No nodes found\n") + } + return + } + + for _, n := range nodes { + wwlog.Printf(wwlog.VERBOSE, "Evaluating node: %s\n", n.Id.Get()) + + if set.Comment != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting comment to: %s\n", n.Id.Get(), set.Comment) + n.Comment.Set(set.Comment) + } + + if set.Container != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting container name to: %s\n", n.Id.Get(), set.Container) + n.ContainerName.Set(set.Container) + } + + if set.Init != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting init command to: %s\n", n.Id.Get(), set.Init) + n.Init.Set(set.Init) + } + + if set.Root != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting root to: %s\n", n.Id.Get(), set.Root) + n.Root.Set(set.Root) + } + + if set.AssetKey != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting asset key to: %s\n", n.Id.Get(), set.AssetKey) + n.AssetKey.Set(set.AssetKey) + } + + if set.KernelOverride != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting kernel override to: %s\n", n.Id.Get(), set.KernelOverride) + n.Kernel.Override.Set(set.KernelOverride) + } + + if set.KernelArgs != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting kernel args to: %s\n", n.Id.Get(), set.KernelArgs) + n.Kernel.Args.Set(set.KernelArgs) + } + + if set.Cluster != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting cluster name to: %s\n", n.Id.Get(), set.Cluster) + n.ClusterName.Set(set.Cluster) + } + + if set.Ipxe != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting iPXE template to: %s\n", n.Id.Get(), set.Ipxe) + n.Ipxe.Set(set.Ipxe) + } + + if len(set.RuntimeOverlay) != 0 { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting runtime overlay to: %s\n", n.Id.Get(), set.RuntimeOverlay) + n.RuntimeOverlay.SetSlice(set.RuntimeOverlay) + } + + if len(set.SystemOverlay) != 0 { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting system overlay to: %s\n", n.Id.Get(), set.SystemOverlay) + n.SystemOverlay.SetSlice(set.SystemOverlay) + } + + if set.IpmiIpaddr != "" { + newIpaddr := util.IncrementIPv4(set.IpmiIpaddr, nodeCount) + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI IP address to: %s\n", n.Id.Get(), newIpaddr) + n.Ipmi.Ipaddr.Set(newIpaddr) + } + + if set.IpmiNetmask != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI netmask to: %s\n", n.Id.Get(), set.IpmiNetmask) + n.Ipmi.Netmask.Set(set.IpmiNetmask) + } + + if set.IpmiPort != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI port to: %s\n", n.Id.Get(), set.IpmiPort) + n.Ipmi.Port.Set(set.IpmiPort) + } + + if set.IpmiGateway != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI gateway to: %s\n", n.Id.Get(), set.IpmiGateway) + n.Ipmi.Gateway.Set(set.IpmiGateway) + } + + if set.IpmiUsername != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI IP username to: %s\n", n.Id.Get(), set.IpmiUsername) + n.Ipmi.UserName.Set(set.IpmiUsername) + } + + if set.IpmiPassword != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI IP password to: %s\n", n.Id.Get(), set.IpmiPassword) + n.Ipmi.Password.Set(set.IpmiPassword) + } + + if set.IpmiInterface != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI IP interface to: %s\n", n.Id.Get(), set.IpmiInterface) + n.Ipmi.Interface.Set(set.IpmiInterface) + } + + if set.IpmiWrite == "yes" || set.Onboot == "y" || set.Onboot == "1" || set.Onboot == "true" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting Ipmiwrite to %s\n", n.Id.Get(), set.IpmiWrite) + n.Ipmi.Write.SetB(true) + } else { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting Ipmiwrite to %s\n", n.Id.Get(), set.IpmiWrite) + n.Ipmi.Write.SetB(false) + } + + if set.Discoverable { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting node to discoverable\n", n.Id.Get()) + n.Discoverable.SetB(true) + } + + if set.Undiscoverable { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting node to undiscoverable\n", n.Id.Get()) + n.Discoverable.SetB(false) + } + + if set.Profile != "" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting profiles to: %s\n", n.Id.Get(), set.Profile) + n.Profiles = []string{set.Profile} + } + + if len(set.ProfileAdd) > 0 { + for _, p := range set.ProfileAdd { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, adding profile '%s'\n", n.Id.Get(), p) + n.Profiles = util.SliceAddUniqueElement(n.Profiles, p) + } + } + + if len(set.ProfileDelete) > 0 { + for _, p := range set.ProfileDelete { + wwlog.Printf(wwlog.VERBOSE, "Node: %s, deleting profile '%s'\n", n.Id.Get(), p) + n.Profiles = util.SliceRemoveElement(n.Profiles, p) + } + } + + if set.Netname != "" { + if _, ok := n.NetDevs[set.Netname]; !ok { + var nd node.NetDevEntry + nd.Tags = make(map[string]*node.Entry) + + n.NetDevs[set.Netname] = &nd + + if set.Netdev == "" { + n.NetDevs[set.Netname].Device.Set(set.Netname) + } + } + var def bool = true + + // NOTE: This is overriding parameters passed in by the caller. + set.Onboot = "yes" + + for _, n := range n.NetDevs { + if n.Primary.GetB() { + def = false + } + } + + if def { + set.NetDefault = "yes" + } + } + + if set.Netdev != "" { + err = checkNetNameRequired(set.Netname) + if err != nil { + return + } + + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting net Device to: %s\n", n.Id.Get(), set.Netname, set.Netdev) + n.NetDevs[set.Netname].Device.Set(set.Netdev) + } + + if set.Ipaddr != "" { + err = checkNetNameRequired(set.Netname) + if err != nil { + return + } + + newIpaddr := util.IncrementIPv4(set.Ipaddr, nodeCount) + + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Ipaddr to: %s\n", n.Id.Get(), set.Netname, newIpaddr) + n.NetDevs[set.Netname].Ipaddr.Set(newIpaddr) + } + + if set.Netmask != "" { + err = checkNetNameRequired(set.Netname) + if err != nil { + return + } + + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting netmask to: %s\n", n.Id.Get(), set.Netname, set.Netmask) + n.NetDevs[set.Netname].Netmask.Set(set.Netmask) + } + + if set.Gateway != "" { + err = checkNetNameRequired(set.Netname) + if err != nil { + return + } + + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting gateway to: %s\n", n.Id.Get(), set.Netname, set.Gateway) + n.NetDevs[set.Netname].Gateway.Set(set.Gateway) + } + + if set.Hwaddr != "" { + err = checkNetNameRequired(set.Netname) + if err != nil { + return + } + + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting HW address to: %s\n", n.Id.Get(), set.Netname, set.Hwaddr) + n.NetDevs[set.Netname].Hwaddr.Set(strings.ToLower(set.Hwaddr)) + } + + if set.Type != "" { + err = checkNetNameRequired(set.Netname) + if err != nil { + return + } + + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Type %s\n", n.Id.Get(), set.Netname, set.Type) + n.NetDevs[set.Netname].Type.Set(set.Type) + } + + if set.Onboot != "" { + err = checkNetNameRequired(set.Netname) + if err != nil { + return + } + + if set.Onboot == "yes" || set.Onboot == "y" || set.Onboot == "1" || set.Onboot == "true" { + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting ONBOOT\n", n.Id.Get(), set.Netname) + n.NetDevs[set.Netname].OnBoot.SetB(true) + } else { + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Unsetting ONBOOT\n", n.Id.Get(), set.Netname) + n.NetDevs[set.Netname].OnBoot.SetB(false) + } + } + + if set.NetDefault != "" { + if set.Netname == "" { + err = fmt.Errorf("You must include the '--netname' option") + wwlog.Printf(wwlog.ERROR, fmt.Sprintf("%v\n", err.Error())) + return + } + + if set.NetDefault == "yes" || set.NetDefault == "y" || set.NetDefault == "1" || set.NetDefault == "true" { + + // Set all other devices to non-default + for _, n := range n.NetDevs { + n.Primary.SetB(false) + } + + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting PRIMARY\n", n.Id.Get(), set.Netname) + n.NetDevs[set.Netname].Primary.SetB(true) + } else { + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Unsetting PRIMARY\n", n.Id.Get(), set.Netname) + n.NetDevs[set.Netname].Primary.SetB(false) + } + } + + if set.NetdevDelete { + err = checkNetNameRequired(set.Netname) + if err != nil { + return + } + + if _, ok := n.NetDevs[set.Netname]; !ok { + err = fmt.Errorf("Network device name doesn't exist: %s", set.Netname) + wwlog.Printf(wwlog.ERROR, fmt.Sprintf("%v\n", err.Error())) + return + } + + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Deleting network device: %s\n", n.Id.Get(), set.Netname) + delete(n.NetDevs, set.Netname) + } + + if len(set.Tags) > 0 { + for _, t := range set.Tags { + keyval := strings.SplitN(t, "=", 2) + key := keyval[0] + val := keyval[1] + + if _, ok := n.Tags[key]; !ok { + var nd node.Entry + n.Tags[key] = &nd + } + + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting Tag '%s'='%s'\n", n.Id.Get(), key, val) + n.Tags[key].Set(val) + } + } + if len(set.TagsDelete) > 0 { + for _, t := range set.TagsDelete { + keyval := strings.SplitN(t, "=", 1) + key := keyval[0] + + if _, ok := n.Tags[key]; !ok { + wwlog.Printf(wwlog.WARN, "Key does not exist: %s\n", key) + os.Exit(1) + } + + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Deleting tag: %s\n", n.Id.Get(), key) + delete(n.Tags, key) + } + } + + if len(set.NetTags) > 0 { + for _, t := range set.NetTags { + keyval := strings.SplitN(t, "=", 2) + key := keyval[0] + val := keyval[1] + if _, ok := n.NetDevs[set.Netname].Tags[key]; !ok { + var nd node.Entry + n.NetDevs[set.Netname].Tags[key] = &nd + } + + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting NETTAG '%s'='%s'\n", n.Id.Get(), set.Netname, key, val) + n.NetDevs[set.Netname].Tags[key].Set(val) + } + + } + if len(set.NetDeleteTags) > 0 { + for _, t := range set.NetDeleteTags { + keyval := strings.SplitN(t, "=", 1) + key := keyval[0] + if _, ok := n.NetDevs[set.Netname].Tags[key]; !ok { + wwlog.Printf(wwlog.WARN, "Node: %s:%s Key %s does not exist\n", n.Id.Get(), set.Netname, key) + os.Exit(1) + } + + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Deleting Tag %s\n", n.Id.Get(), set.Netname, key) + delete(n.NetDevs[set.Netname].Tags, key) + } + } + + err := nodeDB.NodeUpdate(n) + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + os.Exit(1) + } + + nodeCount++ + } + return +} + +// NodeStatus returns the imaging state for nodes. +// This requires warewulfd. +func NodeStatus(nodeNames []string) (nodeStatusResponse *wwapiv1.NodeStatusResponse, err error) { + + // Local structs for translating json from warewulfd. + type nodeStatusInternal struct { + NodeName string `json:"node name"` + Stage string `json:"stage"` + Sent string `json:"sent"` + Ipaddr string `json:"ipaddr"` + Lastseen int64 `json:"last seen"` + } + + // all status is a map with one key (nodes) + // and maps of [nodeName]NodeStatus underneath. + type allStatus struct { + Nodes map[string]*nodeStatusInternal `json:"nodes"` + } + + controller, err := warewulfconf.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s\n", err) + return + } + + if controller.Ipaddr == "" { + err = fmt.Errorf("The Warewulf Server IP Address is not properly configured") + wwlog.Printf(wwlog.ERROR, fmt.Sprintf("%v\n", err.Error())) + return + } + + statusURL := fmt.Sprintf("http://%s:%d/status", controller.Ipaddr, controller.Warewulf.Port) + wwlog.Printf(wwlog.VERBOSE, "Connecting to: %s\n", statusURL) + + resp, err := http.Get(statusURL) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not connect to Warewulf server: %s\n", err) + return + } + defer resp.Body.Close() + + decoder := json.NewDecoder(resp.Body) + var wwNodeStatus allStatus + + err = decoder.Decode(&wwNodeStatus) + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not decode JSON: %s\n", err) + return + } + + // Translate struct and filter. + nodeStatusResponse = &wwapiv1.NodeStatusResponse{} + + if len(nodeNames) == 0 { + for _, v := range wwNodeStatus.Nodes { + nodeStatusResponse.NodeStatus = append(nodeStatusResponse.NodeStatus, + &wwapiv1.NodeStatus{ + NodeName: v.NodeName, + Stage: v.Stage, + Sent: v.Sent, + Ipaddr: v.Ipaddr, + Lastseen: v.Lastseen, + }) + } + } else { + nodeList := hostlist.Expand(nodeNames) + for _, v := range wwNodeStatus.Nodes { + for j := 0; j < len(nodeList); j++ { + if v.NodeName == nodeList[j] { + nodeStatusResponse.NodeStatus = append(nodeStatusResponse.NodeStatus, + &wwapiv1.NodeStatus{ + NodeName: v.NodeName, + Stage: v.Stage, + Sent: v.Sent, + Ipaddr: v.Ipaddr, + Lastseen: v.Lastseen, + }) + break + } + } + } + } + return +} + +// checkNetNameRequired is a helper for determining if netname is set. +// Certain settings require it. +func checkNetNameRequired(netname string) (err error) { + if netname == "" { + err = fmt.Errorf("You must include the '--netname' option") + wwlog.Printf(wwlog.ERROR, fmt.Sprintf("%v\n", err.Error())) + } + return +} + +// nodeDbSave persists the nodeDB to disk and restarts warewulfd. +// TODO: We will likely need locking around anything changing nodeDB +// or restarting warewulfd. Determine if the reason for restart is +// just to reinitialize warewulfd with the new nodeDB or if there is +// something more to it. +func nodeDbSave(nodeDB *node.NodeYaml) (err error) { + err = nodeDB.Persist() + if err != nil { + return errors.Wrap(err, "failed to persist nodedb") + } + + err = warewulfd.DaemonReload() + if err != nil { + return errors.Wrap(err, "failed to reload warewulf daemon") + } + return +} diff --git a/internal/pkg/api/routes/v1/routes.proto b/internal/pkg/api/routes/v1/routes.proto new file mode 100644 index 00000000..33998534 --- /dev/null +++ b/internal/pkg/api/routes/v1/routes.proto @@ -0,0 +1,317 @@ +// Routes for the wwapi (WareWulf API). +// TODO: Try protoc-gen-doc for generating documentation. + +syntax = "proto3"; + +option go_package = "internal/pkg/api/routes/wwapiv1;wwapiv1"; + +package wwapi.v1; + +import "google/protobuf/empty.proto"; +import "google/api/annotations.proto"; + +// Container + +// ContainerBuildParameter contains input for building zero or more containers. +message ContainerBuildParameter { + repeated string containerNames = 1; + bool force = 2; + bool all = 3; + bool default = 4; +} + +// ContainerDeleteParameter contains input for removing containers from Warewulf +// management. +message ContainerDeleteParameter { + repeated string containerNames = 1; +} + +// ContainerImportParameter has all input for importing a container. +message ContainerImportParameter{ + string source = 1; // container source uri + string name = 2; // container name + bool force = 3; + bool update = 4; + bool build = 5; + bool default = 6; + bool syncUser = 7; +} + +// ContainerInfo has data on each container. This is emitted in the +// ContainerListResponse. +message ContainerInfo { + string name = 1; + uint32 nodeCount = 2; + string kernelVersion = 3; +} + +// ContainerListResponse has all information that ContainerList provides. +message ContainerListResponse { + repeated ContainerInfo containers = 1; +} + +// ContainerShowParameter is the input for ContainerShow. +message ContainerShowParameter { + string containerName = 1; +} + +// ContainerShowResponse has all information emitted on ContainerShow. +message ContainerShowResponse { + string Name = 1; + string Rootfs = 2; + repeated string Nodes = 3; + string KernelVersion = 4; +} + +// ContainerSyncUserParameter is the input for ContainerSyncUser. +message ContainerSyncUserParameter { + string containerName = 1; +} + +// Nodes + +// NodeNames is an array of node ids. +message NodeNames { + repeated string nodeNames = 1; +} + +// NodeField contains data output on NodeList. +message NodeField { + string source = 1; + string value = 2; // TODO: Variable name okay? + string print = 3; // Empty values printed as -- in wwctl. +} + +// NetDev is network devices (NICs) on a node. +message NetDev { + NodeField type = 1; + NodeField onboot = 2; + NodeField device = 3; + NodeField hwaddr = 4; + NodeField ipaddr = 5; + NodeField netmask = 6; + NodeField gateway = 7; + NodeField primary = 8; + map Tags = 9; +} + +// NodeInfo contains details about a node managed by Warewulf/ +message NodeInfo { + NodeField id = 1; + NodeField comment = 2; + NodeField cluster = 3; + NodeField discoverable = 4; + NodeField container = 5; + NodeField kernelOverride = 6; + NodeField kernelArgs = 7; + NodeField systemOverlay = 8; + NodeField runtimeOverlay = 9; + NodeField ipxe = 10; + NodeField init = 11; + NodeField root = 12; + NodeField assetKey = 13; + NodeField ipmiIpaddr = 14; + NodeField ipmiNetmask = 15; + NodeField ipmiPort = 16; + NodeField ipmiGateway = 17; + NodeField ipmiUserName = 18; + NodeField ipmiInterface = 19; + NodeField ipmiPassword = 20; + + repeated string profiles = 21; + repeated string groupProfiles = 22; + + map NetDevs = 23; + map Tags = 24; + map Keys = 25; // TODO: We may not need this. Tags may be it. Ask Greg. +} + +// NodeListResponse is the output of NodeList. +message NodeListResponse { + repeated NodeInfo nodes = 1; +} + +// NodeAddParameter contains all input for adding a node to be managed by +// Warewulf. +message NodeAddParameter { + string cluster = 1; + bool discoverable = 2; + string gateway = 3; + string hwaddr = 4; + string ipaddr = 5; + string netdev = 6; + string netmask = 7; + string netname = 8; + string type = 9; + repeated string nodeNames = 10; + string ipaddr6 = 11; +} + +// NodeDeleteParameter contains input for removing nodes from Warewulf +// management. +message NodeDeleteParameter { + bool force = 1; + repeated string nodeNames = 2; +} + +// NodeSetParameter contains all fields for updating aspects of nodes managed +// by Warewulf. +message NodeSetParameter { + string comment = 1; + string container = 2; + string kernelOverride = 3; + string kernelArgs = 4; + string netname = 5; + string netdev = 6; + string ipaddr = 7; + string netmask = 8; + string gateway = 9; + string hwaddr = 10; + string type = 11; + string onboot = 12; + string netDefault = 13; + bool netdevDelete = 14; + string cluster = 15; + string ipxe = 16; + string initOverlay = 17; + repeated string runtimeOverlay = 18; + repeated string systemOverlay = 19; + string ipmiIpaddr = 20; + string ipmiNetmask = 21; + string ipmiPort = 22; + string ipmiGateway = 23; + string ipmiUsername = 24; + string ipmiPassword = 25; + string ipmiInterface = 26; + bool allNodes = 27; + string profile = 28; + repeated string profileAdd = 29; + repeated string profileDelete = 30; + bool force = 31; + string init = 32; + bool discoverable = 33; + bool undiscoverable = 34; + string root = 35; + repeated string tags = 36; + repeated string tagsDelete = 37; + string assetKey = 38; + repeated string nodeNames = 39; + string ipmiWrite = 40; + repeated string NetTags = 41; + repeated string NetDeleteTags = 42; +} + +// NodeStatus contains information about the imaging status per node. +message NodeStatus { + string nodeName = 1; // Name (Id) of the node. + string stage = 2; // Stage of imaging. + string sent = 3; // Last overlay sent. + string ipaddr = 4; // Node IP address. + int64 lastseen = 5; // Time in seconds since the node was last seen. +} + +// NodeStatusResponse contains NodeStatus for zero or more nodes. +message NodeStatusResponse { + repeated NodeStatus nodeStatus = 1; +} + +// Version + +// VersionReponse contains versions of the software. +message VersionResponse { + string apiPrefix = 1; + string apiVersion = 2; + string warewulfVersion = 3; +} + +// WWApi defines the wwapid service web interface. +service WWApi { + + // Containers + + // ContainerBuild builds zero or more containers. + rpc ContainerBuild(ContainerBuildParameter) returns (ContainerListResponse) { + option (google.api.http) = { + post: "/v1/containerbuild" + body: "*" + }; + } + + // ContainerDelete removes one or more container from Warewulf management. + rpc ContainerDelete(ContainerDeleteParameter) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/container" + }; + } + + // ContainerImport imports a container to Warewulf. + rpc ContainerImport(ContainerImportParameter) returns (ContainerListResponse) { + option(google.api.http) = { + post: "/v1/container" + body: "*" + }; + } + + // ContainerList lists ContainerInfo for each container. + rpc ContainerList(google.protobuf.Empty) returns (ContainerListResponse) { + option (google.api.http) = { + get: "/v1/container" + }; + } + + // ContainerShow lists ContainerShow for each container. + rpc ContainerShow(ContainerShowParameter) returns (ContainerShowResponse) { + option (google.api.http) = { + get: "/v1/containershow" + }; + } + + // Nodes + + // NodeAdd adds one or more nodes for management by Warewulf and returns + // the added nodes. Node fields may be shimmed in per profiles. + rpc NodeAdd(NodeAddParameter) returns (NodeListResponse) { + option (google.api.http) = { + post: "/v1/node" + body: "*" + }; + } + + // NodeDelete removes one or more nodes from Warewulf management. + rpc NodeDelete(NodeDeleteParameter) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/node" + }; + } + + // NodeList lists some or all nodes managed by Warewulf. + rpc NodeList(NodeNames) returns (NodeListResponse) { + option (google.api.http) = { + get: "/v1/node" + }; + } + + // NodeSet updates node fields for one or more nodes. + rpc NodeSet(NodeSetParameter) returns (NodeListResponse) { + option (google.api.http) = { + post: "/v1/nodeset" // TODO: This should be a patch. Had trouble getting patch to work at all. + body: "*" + }; + } + + // NodeStatus returns the imaging state for nodes. + // This requires warewulfd. + rpc NodeStatus(NodeNames) returns (NodeStatusResponse) { + option (google.api.http) = { + get: "/v1/nodestatus" + }; + } + + // Version returns the wwapi version, the api prefix, and the Warewulf + // version. This is also useful for testing if the service is up. + rpc Version(google.protobuf.Empty) returns (VersionResponse) { + option (google.api.http) = { + get: "/version" + }; + } +} \ No newline at end of file diff --git a/internal/pkg/api/routes/wwapiv1/routes.pb.go b/internal/pkg/api/routes/wwapiv1/routes.pb.go new file mode 100644 index 00000000..fcfc7adb --- /dev/null +++ b/internal/pkg/api/routes/wwapiv1/routes.pb.go @@ -0,0 +1,2548 @@ +// Routes for the wwapi (WareWulf API). +// TODO: Try protoc-gen-doc for generating documentation. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.21.1 +// source: routes.proto + +package wwapiv1 + +import ( + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// ContainerBuildParameter contains input for building zero or more containers. +type ContainerBuildParameter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ContainerNames []string `protobuf:"bytes,1,rep,name=containerNames,proto3" json:"containerNames,omitempty"` + Force bool `protobuf:"varint,2,opt,name=force,proto3" json:"force,omitempty"` + All bool `protobuf:"varint,3,opt,name=all,proto3" json:"all,omitempty"` + Default bool `protobuf:"varint,4,opt,name=default,proto3" json:"default,omitempty"` +} + +func (x *ContainerBuildParameter) Reset() { + *x = ContainerBuildParameter{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContainerBuildParameter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContainerBuildParameter) ProtoMessage() {} + +func (x *ContainerBuildParameter) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContainerBuildParameter.ProtoReflect.Descriptor instead. +func (*ContainerBuildParameter) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{0} +} + +func (x *ContainerBuildParameter) GetContainerNames() []string { + if x != nil { + return x.ContainerNames + } + return nil +} + +func (x *ContainerBuildParameter) GetForce() bool { + if x != nil { + return x.Force + } + return false +} + +func (x *ContainerBuildParameter) GetAll() bool { + if x != nil { + return x.All + } + return false +} + +func (x *ContainerBuildParameter) GetDefault() bool { + if x != nil { + return x.Default + } + return false +} + +// ContainerDeleteParameter contains input for removing containers from Warewulf +// management. +type ContainerDeleteParameter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ContainerNames []string `protobuf:"bytes,1,rep,name=containerNames,proto3" json:"containerNames,omitempty"` +} + +func (x *ContainerDeleteParameter) Reset() { + *x = ContainerDeleteParameter{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContainerDeleteParameter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContainerDeleteParameter) ProtoMessage() {} + +func (x *ContainerDeleteParameter) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContainerDeleteParameter.ProtoReflect.Descriptor instead. +func (*ContainerDeleteParameter) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{1} +} + +func (x *ContainerDeleteParameter) GetContainerNames() []string { + if x != nil { + return x.ContainerNames + } + return nil +} + +// ContainerImportParameter has all input for importing a container. +type ContainerImportParameter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` // container source uri + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // container name + Force bool `protobuf:"varint,3,opt,name=force,proto3" json:"force,omitempty"` + Update bool `protobuf:"varint,4,opt,name=update,proto3" json:"update,omitempty"` + Build bool `protobuf:"varint,5,opt,name=build,proto3" json:"build,omitempty"` + Default bool `protobuf:"varint,6,opt,name=default,proto3" json:"default,omitempty"` + SyncUser bool `protobuf:"varint,7,opt,name=syncUser,proto3" json:"syncUser,omitempty"` +} + +func (x *ContainerImportParameter) Reset() { + *x = ContainerImportParameter{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContainerImportParameter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContainerImportParameter) ProtoMessage() {} + +func (x *ContainerImportParameter) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContainerImportParameter.ProtoReflect.Descriptor instead. +func (*ContainerImportParameter) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{2} +} + +func (x *ContainerImportParameter) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *ContainerImportParameter) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ContainerImportParameter) GetForce() bool { + if x != nil { + return x.Force + } + return false +} + +func (x *ContainerImportParameter) GetUpdate() bool { + if x != nil { + return x.Update + } + return false +} + +func (x *ContainerImportParameter) GetBuild() bool { + if x != nil { + return x.Build + } + return false +} + +func (x *ContainerImportParameter) GetDefault() bool { + if x != nil { + return x.Default + } + return false +} + +func (x *ContainerImportParameter) GetSyncUser() bool { + if x != nil { + return x.SyncUser + } + return false +} + +// ContainerInfo has data on each container. This is emitted in the +// ContainerListResponse. +type ContainerInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + NodeCount uint32 `protobuf:"varint,2,opt,name=nodeCount,proto3" json:"nodeCount,omitempty"` + KernelVersion string `protobuf:"bytes,3,opt,name=kernelVersion,proto3" json:"kernelVersion,omitempty"` +} + +func (x *ContainerInfo) Reset() { + *x = ContainerInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContainerInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContainerInfo) ProtoMessage() {} + +func (x *ContainerInfo) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContainerInfo.ProtoReflect.Descriptor instead. +func (*ContainerInfo) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{3} +} + +func (x *ContainerInfo) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ContainerInfo) GetNodeCount() uint32 { + if x != nil { + return x.NodeCount + } + return 0 +} + +func (x *ContainerInfo) GetKernelVersion() string { + if x != nil { + return x.KernelVersion + } + return "" +} + +// ContainerListResponse has all information that ContainerList provides. +type ContainerListResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Containers []*ContainerInfo `protobuf:"bytes,1,rep,name=containers,proto3" json:"containers,omitempty"` +} + +func (x *ContainerListResponse) Reset() { + *x = ContainerListResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContainerListResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContainerListResponse) ProtoMessage() {} + +func (x *ContainerListResponse) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContainerListResponse.ProtoReflect.Descriptor instead. +func (*ContainerListResponse) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{4} +} + +func (x *ContainerListResponse) GetContainers() []*ContainerInfo { + if x != nil { + return x.Containers + } + return nil +} + +// ContainerShowParameter is the input for ContainerShow. +type ContainerShowParameter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ContainerName string `protobuf:"bytes,1,opt,name=containerName,proto3" json:"containerName,omitempty"` +} + +func (x *ContainerShowParameter) Reset() { + *x = ContainerShowParameter{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContainerShowParameter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContainerShowParameter) ProtoMessage() {} + +func (x *ContainerShowParameter) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContainerShowParameter.ProtoReflect.Descriptor instead. +func (*ContainerShowParameter) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{5} +} + +func (x *ContainerShowParameter) GetContainerName() string { + if x != nil { + return x.ContainerName + } + return "" +} + +// ContainerShowResponse has all information emitted on ContainerShow. +type ContainerShowResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` + Rootfs string `protobuf:"bytes,2,opt,name=Rootfs,proto3" json:"Rootfs,omitempty"` + Nodes []string `protobuf:"bytes,3,rep,name=Nodes,proto3" json:"Nodes,omitempty"` + KernelVersion string `protobuf:"bytes,4,opt,name=KernelVersion,proto3" json:"KernelVersion,omitempty"` +} + +func (x *ContainerShowResponse) Reset() { + *x = ContainerShowResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContainerShowResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContainerShowResponse) ProtoMessage() {} + +func (x *ContainerShowResponse) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContainerShowResponse.ProtoReflect.Descriptor instead. +func (*ContainerShowResponse) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{6} +} + +func (x *ContainerShowResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ContainerShowResponse) GetRootfs() string { + if x != nil { + return x.Rootfs + } + return "" +} + +func (x *ContainerShowResponse) GetNodes() []string { + if x != nil { + return x.Nodes + } + return nil +} + +func (x *ContainerShowResponse) GetKernelVersion() string { + if x != nil { + return x.KernelVersion + } + return "" +} + +// ContainerSyncUserParameter is the input for ContainerSyncUser. +type ContainerSyncUserParameter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ContainerName string `protobuf:"bytes,1,opt,name=containerName,proto3" json:"containerName,omitempty"` +} + +func (x *ContainerSyncUserParameter) Reset() { + *x = ContainerSyncUserParameter{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContainerSyncUserParameter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContainerSyncUserParameter) ProtoMessage() {} + +func (x *ContainerSyncUserParameter) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContainerSyncUserParameter.ProtoReflect.Descriptor instead. +func (*ContainerSyncUserParameter) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{7} +} + +func (x *ContainerSyncUserParameter) GetContainerName() string { + if x != nil { + return x.ContainerName + } + return "" +} + +// NodeNames is an array of node ids. +type NodeNames struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NodeNames []string `protobuf:"bytes,1,rep,name=nodeNames,proto3" json:"nodeNames,omitempty"` +} + +func (x *NodeNames) Reset() { + *x = NodeNames{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeNames) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeNames) ProtoMessage() {} + +func (x *NodeNames) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeNames.ProtoReflect.Descriptor instead. +func (*NodeNames) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{8} +} + +func (x *NodeNames) GetNodeNames() []string { + if x != nil { + return x.NodeNames + } + return nil +} + +// NodeField contains data output on NodeList. +type NodeField struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` // TODO: Variable name okay? + Print string `protobuf:"bytes,3,opt,name=print,proto3" json:"print,omitempty"` // Empty values printed as -- in wwctl. +} + +func (x *NodeField) Reset() { + *x = NodeField{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeField) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeField) ProtoMessage() {} + +func (x *NodeField) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeField.ProtoReflect.Descriptor instead. +func (*NodeField) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{9} +} + +func (x *NodeField) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *NodeField) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +func (x *NodeField) GetPrint() string { + if x != nil { + return x.Print + } + return "" +} + +// NetDev is network devices (NICs) on a node. +type NetDev struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type *NodeField `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Onboot *NodeField `protobuf:"bytes,2,opt,name=onboot,proto3" json:"onboot,omitempty"` + Device *NodeField `protobuf:"bytes,3,opt,name=device,proto3" json:"device,omitempty"` + Hwaddr *NodeField `protobuf:"bytes,4,opt,name=hwaddr,proto3" json:"hwaddr,omitempty"` + Ipaddr *NodeField `protobuf:"bytes,5,opt,name=ipaddr,proto3" json:"ipaddr,omitempty"` + Netmask *NodeField `protobuf:"bytes,6,opt,name=netmask,proto3" json:"netmask,omitempty"` + Gateway *NodeField `protobuf:"bytes,7,opt,name=gateway,proto3" json:"gateway,omitempty"` + Primary *NodeField `protobuf:"bytes,8,opt,name=primary,proto3" json:"primary,omitempty"` + Tags map[string]*NodeField `protobuf:"bytes,9,rep,name=Tags,proto3" json:"Tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *NetDev) Reset() { + *x = NetDev{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NetDev) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NetDev) ProtoMessage() {} + +func (x *NetDev) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NetDev.ProtoReflect.Descriptor instead. +func (*NetDev) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{10} +} + +func (x *NetDev) GetType() *NodeField { + if x != nil { + return x.Type + } + return nil +} + +func (x *NetDev) GetOnboot() *NodeField { + if x != nil { + return x.Onboot + } + return nil +} + +func (x *NetDev) GetDevice() *NodeField { + if x != nil { + return x.Device + } + return nil +} + +func (x *NetDev) GetHwaddr() *NodeField { + if x != nil { + return x.Hwaddr + } + return nil +} + +func (x *NetDev) GetIpaddr() *NodeField { + if x != nil { + return x.Ipaddr + } + return nil +} + +func (x *NetDev) GetNetmask() *NodeField { + if x != nil { + return x.Netmask + } + return nil +} + +func (x *NetDev) GetGateway() *NodeField { + if x != nil { + return x.Gateway + } + return nil +} + +func (x *NetDev) GetPrimary() *NodeField { + if x != nil { + return x.Primary + } + return nil +} + +func (x *NetDev) GetTags() map[string]*NodeField { + if x != nil { + return x.Tags + } + return nil +} + +// NodeInfo contains details about a node managed by Warewulf/ +type NodeInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id *NodeField `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Comment *NodeField `protobuf:"bytes,2,opt,name=comment,proto3" json:"comment,omitempty"` + Cluster *NodeField `protobuf:"bytes,3,opt,name=cluster,proto3" json:"cluster,omitempty"` + Discoverable *NodeField `protobuf:"bytes,4,opt,name=discoverable,proto3" json:"discoverable,omitempty"` + Container *NodeField `protobuf:"bytes,5,opt,name=container,proto3" json:"container,omitempty"` + KernelOverride *NodeField `protobuf:"bytes,6,opt,name=kernelOverride,proto3" json:"kernelOverride,omitempty"` + KernelArgs *NodeField `protobuf:"bytes,7,opt,name=kernelArgs,proto3" json:"kernelArgs,omitempty"` + SystemOverlay *NodeField `protobuf:"bytes,8,opt,name=systemOverlay,proto3" json:"systemOverlay,omitempty"` + RuntimeOverlay *NodeField `protobuf:"bytes,9,opt,name=runtimeOverlay,proto3" json:"runtimeOverlay,omitempty"` + Ipxe *NodeField `protobuf:"bytes,10,opt,name=ipxe,proto3" json:"ipxe,omitempty"` + Init *NodeField `protobuf:"bytes,11,opt,name=init,proto3" json:"init,omitempty"` + Root *NodeField `protobuf:"bytes,12,opt,name=root,proto3" json:"root,omitempty"` + AssetKey *NodeField `protobuf:"bytes,13,opt,name=assetKey,proto3" json:"assetKey,omitempty"` + IpmiIpaddr *NodeField `protobuf:"bytes,14,opt,name=ipmiIpaddr,proto3" json:"ipmiIpaddr,omitempty"` + IpmiNetmask *NodeField `protobuf:"bytes,15,opt,name=ipmiNetmask,proto3" json:"ipmiNetmask,omitempty"` + IpmiPort *NodeField `protobuf:"bytes,16,opt,name=ipmiPort,proto3" json:"ipmiPort,omitempty"` + IpmiGateway *NodeField `protobuf:"bytes,17,opt,name=ipmiGateway,proto3" json:"ipmiGateway,omitempty"` + IpmiUserName *NodeField `protobuf:"bytes,18,opt,name=ipmiUserName,proto3" json:"ipmiUserName,omitempty"` + IpmiInterface *NodeField `protobuf:"bytes,19,opt,name=ipmiInterface,proto3" json:"ipmiInterface,omitempty"` + IpmiPassword *NodeField `protobuf:"bytes,20,opt,name=ipmiPassword,proto3" json:"ipmiPassword,omitempty"` + Profiles []string `protobuf:"bytes,21,rep,name=profiles,proto3" json:"profiles,omitempty"` + GroupProfiles []string `protobuf:"bytes,22,rep,name=groupProfiles,proto3" json:"groupProfiles,omitempty"` + NetDevs map[string]*NetDev `protobuf:"bytes,23,rep,name=NetDevs,proto3" json:"NetDevs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Tags map[string]*NodeField `protobuf:"bytes,24,rep,name=Tags,proto3" json:"Tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Keys map[string]*NodeField `protobuf:"bytes,25,rep,name=Keys,proto3" json:"Keys,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // TODO: We may not need this. Tags may be it. Ask Greg. +} + +func (x *NodeInfo) Reset() { + *x = NodeInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeInfo) ProtoMessage() {} + +func (x *NodeInfo) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeInfo.ProtoReflect.Descriptor instead. +func (*NodeInfo) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{11} +} + +func (x *NodeInfo) GetId() *NodeField { + if x != nil { + return x.Id + } + return nil +} + +func (x *NodeInfo) GetComment() *NodeField { + if x != nil { + return x.Comment + } + return nil +} + +func (x *NodeInfo) GetCluster() *NodeField { + if x != nil { + return x.Cluster + } + return nil +} + +func (x *NodeInfo) GetDiscoverable() *NodeField { + if x != nil { + return x.Discoverable + } + return nil +} + +func (x *NodeInfo) GetContainer() *NodeField { + if x != nil { + return x.Container + } + return nil +} + +func (x *NodeInfo) GetKernelOverride() *NodeField { + if x != nil { + return x.KernelOverride + } + return nil +} + +func (x *NodeInfo) GetKernelArgs() *NodeField { + if x != nil { + return x.KernelArgs + } + return nil +} + +func (x *NodeInfo) GetSystemOverlay() *NodeField { + if x != nil { + return x.SystemOverlay + } + return nil +} + +func (x *NodeInfo) GetRuntimeOverlay() *NodeField { + if x != nil { + return x.RuntimeOverlay + } + return nil +} + +func (x *NodeInfo) GetIpxe() *NodeField { + if x != nil { + return x.Ipxe + } + return nil +} + +func (x *NodeInfo) GetInit() *NodeField { + if x != nil { + return x.Init + } + return nil +} + +func (x *NodeInfo) GetRoot() *NodeField { + if x != nil { + return x.Root + } + return nil +} + +func (x *NodeInfo) GetAssetKey() *NodeField { + if x != nil { + return x.AssetKey + } + return nil +} + +func (x *NodeInfo) GetIpmiIpaddr() *NodeField { + if x != nil { + return x.IpmiIpaddr + } + return nil +} + +func (x *NodeInfo) GetIpmiNetmask() *NodeField { + if x != nil { + return x.IpmiNetmask + } + return nil +} + +func (x *NodeInfo) GetIpmiPort() *NodeField { + if x != nil { + return x.IpmiPort + } + return nil +} + +func (x *NodeInfo) GetIpmiGateway() *NodeField { + if x != nil { + return x.IpmiGateway + } + return nil +} + +func (x *NodeInfo) GetIpmiUserName() *NodeField { + if x != nil { + return x.IpmiUserName + } + return nil +} + +func (x *NodeInfo) GetIpmiInterface() *NodeField { + if x != nil { + return x.IpmiInterface + } + return nil +} + +func (x *NodeInfo) GetIpmiPassword() *NodeField { + if x != nil { + return x.IpmiPassword + } + return nil +} + +func (x *NodeInfo) GetProfiles() []string { + if x != nil { + return x.Profiles + } + return nil +} + +func (x *NodeInfo) GetGroupProfiles() []string { + if x != nil { + return x.GroupProfiles + } + return nil +} + +func (x *NodeInfo) GetNetDevs() map[string]*NetDev { + if x != nil { + return x.NetDevs + } + return nil +} + +func (x *NodeInfo) GetTags() map[string]*NodeField { + if x != nil { + return x.Tags + } + return nil +} + +func (x *NodeInfo) GetKeys() map[string]*NodeField { + if x != nil { + return x.Keys + } + return nil +} + +// NodeListResponse is the output of NodeList. +type NodeListResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Nodes []*NodeInfo `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"` +} + +func (x *NodeListResponse) Reset() { + *x = NodeListResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeListResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeListResponse) ProtoMessage() {} + +func (x *NodeListResponse) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeListResponse.ProtoReflect.Descriptor instead. +func (*NodeListResponse) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{12} +} + +func (x *NodeListResponse) GetNodes() []*NodeInfo { + if x != nil { + return x.Nodes + } + return nil +} + +// NodeAddParameter contains all input for adding a node to be managed by +// Warewulf. +type NodeAddParameter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Cluster string `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` + Discoverable bool `protobuf:"varint,2,opt,name=discoverable,proto3" json:"discoverable,omitempty"` + Gateway string `protobuf:"bytes,3,opt,name=gateway,proto3" json:"gateway,omitempty"` + Hwaddr string `protobuf:"bytes,4,opt,name=hwaddr,proto3" json:"hwaddr,omitempty"` + Ipaddr string `protobuf:"bytes,5,opt,name=ipaddr,proto3" json:"ipaddr,omitempty"` + Netdev string `protobuf:"bytes,6,opt,name=netdev,proto3" json:"netdev,omitempty"` + Netmask string `protobuf:"bytes,7,opt,name=netmask,proto3" json:"netmask,omitempty"` + Netname string `protobuf:"bytes,8,opt,name=netname,proto3" json:"netname,omitempty"` + Type string `protobuf:"bytes,9,opt,name=type,proto3" json:"type,omitempty"` + NodeNames []string `protobuf:"bytes,10,rep,name=nodeNames,proto3" json:"nodeNames,omitempty"` + Ipaddr6 string `protobuf:"bytes,11,opt,name=ipaddr6,proto3" json:"ipaddr6,omitempty"` +} + +func (x *NodeAddParameter) Reset() { + *x = NodeAddParameter{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeAddParameter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeAddParameter) ProtoMessage() {} + +func (x *NodeAddParameter) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeAddParameter.ProtoReflect.Descriptor instead. +func (*NodeAddParameter) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{13} +} + +func (x *NodeAddParameter) GetCluster() string { + if x != nil { + return x.Cluster + } + return "" +} + +func (x *NodeAddParameter) GetDiscoverable() bool { + if x != nil { + return x.Discoverable + } + return false +} + +func (x *NodeAddParameter) GetGateway() string { + if x != nil { + return x.Gateway + } + return "" +} + +func (x *NodeAddParameter) GetHwaddr() string { + if x != nil { + return x.Hwaddr + } + return "" +} + +func (x *NodeAddParameter) GetIpaddr() string { + if x != nil { + return x.Ipaddr + } + return "" +} + +func (x *NodeAddParameter) GetNetdev() string { + if x != nil { + return x.Netdev + } + return "" +} + +func (x *NodeAddParameter) GetNetmask() string { + if x != nil { + return x.Netmask + } + return "" +} + +func (x *NodeAddParameter) GetNetname() string { + if x != nil { + return x.Netname + } + return "" +} + +func (x *NodeAddParameter) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *NodeAddParameter) GetNodeNames() []string { + if x != nil { + return x.NodeNames + } + return nil +} + +func (x *NodeAddParameter) GetIpaddr6() string { + if x != nil { + return x.Ipaddr6 + } + return "" +} + +// NodeDeleteParameter contains input for removing nodes from Warewulf +// management. +type NodeDeleteParameter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Force bool `protobuf:"varint,1,opt,name=force,proto3" json:"force,omitempty"` + NodeNames []string `protobuf:"bytes,2,rep,name=nodeNames,proto3" json:"nodeNames,omitempty"` +} + +func (x *NodeDeleteParameter) Reset() { + *x = NodeDeleteParameter{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeDeleteParameter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeDeleteParameter) ProtoMessage() {} + +func (x *NodeDeleteParameter) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeDeleteParameter.ProtoReflect.Descriptor instead. +func (*NodeDeleteParameter) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{14} +} + +func (x *NodeDeleteParameter) GetForce() bool { + if x != nil { + return x.Force + } + return false +} + +func (x *NodeDeleteParameter) GetNodeNames() []string { + if x != nil { + return x.NodeNames + } + return nil +} + +// NodeSetParameter contains all fields for updating aspects of nodes managed +// by Warewulf. +type NodeSetParameter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Comment string `protobuf:"bytes,1,opt,name=comment,proto3" json:"comment,omitempty"` + Container string `protobuf:"bytes,2,opt,name=container,proto3" json:"container,omitempty"` + KernelOverride string `protobuf:"bytes,3,opt,name=kernelOverride,proto3" json:"kernelOverride,omitempty"` + KernelArgs string `protobuf:"bytes,4,opt,name=kernelArgs,proto3" json:"kernelArgs,omitempty"` + Netname string `protobuf:"bytes,5,opt,name=netname,proto3" json:"netname,omitempty"` + Netdev string `protobuf:"bytes,6,opt,name=netdev,proto3" json:"netdev,omitempty"` + Ipaddr string `protobuf:"bytes,7,opt,name=ipaddr,proto3" json:"ipaddr,omitempty"` + Netmask string `protobuf:"bytes,8,opt,name=netmask,proto3" json:"netmask,omitempty"` + Gateway string `protobuf:"bytes,9,opt,name=gateway,proto3" json:"gateway,omitempty"` + Hwaddr string `protobuf:"bytes,10,opt,name=hwaddr,proto3" json:"hwaddr,omitempty"` + Type string `protobuf:"bytes,11,opt,name=type,proto3" json:"type,omitempty"` + Onboot string `protobuf:"bytes,12,opt,name=onboot,proto3" json:"onboot,omitempty"` + NetDefault string `protobuf:"bytes,13,opt,name=netDefault,proto3" json:"netDefault,omitempty"` + NetdevDelete bool `protobuf:"varint,14,opt,name=netdevDelete,proto3" json:"netdevDelete,omitempty"` + Cluster string `protobuf:"bytes,15,opt,name=cluster,proto3" json:"cluster,omitempty"` + Ipxe string `protobuf:"bytes,16,opt,name=ipxe,proto3" json:"ipxe,omitempty"` + InitOverlay string `protobuf:"bytes,17,opt,name=initOverlay,proto3" json:"initOverlay,omitempty"` + RuntimeOverlay []string `protobuf:"bytes,18,rep,name=runtimeOverlay,proto3" json:"runtimeOverlay,omitempty"` + SystemOverlay []string `protobuf:"bytes,19,rep,name=systemOverlay,proto3" json:"systemOverlay,omitempty"` + IpmiIpaddr string `protobuf:"bytes,20,opt,name=ipmiIpaddr,proto3" json:"ipmiIpaddr,omitempty"` + IpmiNetmask string `protobuf:"bytes,21,opt,name=ipmiNetmask,proto3" json:"ipmiNetmask,omitempty"` + IpmiPort string `protobuf:"bytes,22,opt,name=ipmiPort,proto3" json:"ipmiPort,omitempty"` + IpmiGateway string `protobuf:"bytes,23,opt,name=ipmiGateway,proto3" json:"ipmiGateway,omitempty"` + IpmiUsername string `protobuf:"bytes,24,opt,name=ipmiUsername,proto3" json:"ipmiUsername,omitempty"` + IpmiPassword string `protobuf:"bytes,25,opt,name=ipmiPassword,proto3" json:"ipmiPassword,omitempty"` + IpmiInterface string `protobuf:"bytes,26,opt,name=ipmiInterface,proto3" json:"ipmiInterface,omitempty"` + AllNodes bool `protobuf:"varint,27,opt,name=allNodes,proto3" json:"allNodes,omitempty"` + Profile string `protobuf:"bytes,28,opt,name=profile,proto3" json:"profile,omitempty"` + ProfileAdd []string `protobuf:"bytes,29,rep,name=profileAdd,proto3" json:"profileAdd,omitempty"` + ProfileDelete []string `protobuf:"bytes,30,rep,name=profileDelete,proto3" json:"profileDelete,omitempty"` + Force bool `protobuf:"varint,31,opt,name=force,proto3" json:"force,omitempty"` + Init string `protobuf:"bytes,32,opt,name=init,proto3" json:"init,omitempty"` + Discoverable bool `protobuf:"varint,33,opt,name=discoverable,proto3" json:"discoverable,omitempty"` + Undiscoverable bool `protobuf:"varint,34,opt,name=undiscoverable,proto3" json:"undiscoverable,omitempty"` + Root string `protobuf:"bytes,35,opt,name=root,proto3" json:"root,omitempty"` + Tags []string `protobuf:"bytes,36,rep,name=tags,proto3" json:"tags,omitempty"` + TagsDelete []string `protobuf:"bytes,37,rep,name=tagsDelete,proto3" json:"tagsDelete,omitempty"` + AssetKey string `protobuf:"bytes,38,opt,name=assetKey,proto3" json:"assetKey,omitempty"` + NodeNames []string `protobuf:"bytes,39,rep,name=nodeNames,proto3" json:"nodeNames,omitempty"` + IpmiWrite string `protobuf:"bytes,40,opt,name=ipmiWrite,proto3" json:"ipmiWrite,omitempty"` + NetTags []string `protobuf:"bytes,41,rep,name=NetTags,proto3" json:"NetTags,omitempty"` + NetDeleteTags []string `protobuf:"bytes,42,rep,name=NetDeleteTags,proto3" json:"NetDeleteTags,omitempty"` +} + +func (x *NodeSetParameter) Reset() { + *x = NodeSetParameter{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeSetParameter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeSetParameter) ProtoMessage() {} + +func (x *NodeSetParameter) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeSetParameter.ProtoReflect.Descriptor instead. +func (*NodeSetParameter) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{15} +} + +func (x *NodeSetParameter) GetComment() string { + if x != nil { + return x.Comment + } + return "" +} + +func (x *NodeSetParameter) GetContainer() string { + if x != nil { + return x.Container + } + return "" +} + +func (x *NodeSetParameter) GetKernelOverride() string { + if x != nil { + return x.KernelOverride + } + return "" +} + +func (x *NodeSetParameter) GetKernelArgs() string { + if x != nil { + return x.KernelArgs + } + return "" +} + +func (x *NodeSetParameter) GetNetname() string { + if x != nil { + return x.Netname + } + return "" +} + +func (x *NodeSetParameter) GetNetdev() string { + if x != nil { + return x.Netdev + } + return "" +} + +func (x *NodeSetParameter) GetIpaddr() string { + if x != nil { + return x.Ipaddr + } + return "" +} + +func (x *NodeSetParameter) GetNetmask() string { + if x != nil { + return x.Netmask + } + return "" +} + +func (x *NodeSetParameter) GetGateway() string { + if x != nil { + return x.Gateway + } + return "" +} + +func (x *NodeSetParameter) GetHwaddr() string { + if x != nil { + return x.Hwaddr + } + return "" +} + +func (x *NodeSetParameter) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *NodeSetParameter) GetOnboot() string { + if x != nil { + return x.Onboot + } + return "" +} + +func (x *NodeSetParameter) GetNetDefault() string { + if x != nil { + return x.NetDefault + } + return "" +} + +func (x *NodeSetParameter) GetNetdevDelete() bool { + if x != nil { + return x.NetdevDelete + } + return false +} + +func (x *NodeSetParameter) GetCluster() string { + if x != nil { + return x.Cluster + } + return "" +} + +func (x *NodeSetParameter) GetIpxe() string { + if x != nil { + return x.Ipxe + } + return "" +} + +func (x *NodeSetParameter) GetInitOverlay() string { + if x != nil { + return x.InitOverlay + } + return "" +} + +func (x *NodeSetParameter) GetRuntimeOverlay() []string { + if x != nil { + return x.RuntimeOverlay + } + return nil +} + +func (x *NodeSetParameter) GetSystemOverlay() []string { + if x != nil { + return x.SystemOverlay + } + return nil +} + +func (x *NodeSetParameter) GetIpmiIpaddr() string { + if x != nil { + return x.IpmiIpaddr + } + return "" +} + +func (x *NodeSetParameter) GetIpmiNetmask() string { + if x != nil { + return x.IpmiNetmask + } + return "" +} + +func (x *NodeSetParameter) GetIpmiPort() string { + if x != nil { + return x.IpmiPort + } + return "" +} + +func (x *NodeSetParameter) GetIpmiGateway() string { + if x != nil { + return x.IpmiGateway + } + return "" +} + +func (x *NodeSetParameter) GetIpmiUsername() string { + if x != nil { + return x.IpmiUsername + } + return "" +} + +func (x *NodeSetParameter) GetIpmiPassword() string { + if x != nil { + return x.IpmiPassword + } + return "" +} + +func (x *NodeSetParameter) GetIpmiInterface() string { + if x != nil { + return x.IpmiInterface + } + return "" +} + +func (x *NodeSetParameter) GetAllNodes() bool { + if x != nil { + return x.AllNodes + } + return false +} + +func (x *NodeSetParameter) GetProfile() string { + if x != nil { + return x.Profile + } + return "" +} + +func (x *NodeSetParameter) GetProfileAdd() []string { + if x != nil { + return x.ProfileAdd + } + return nil +} + +func (x *NodeSetParameter) GetProfileDelete() []string { + if x != nil { + return x.ProfileDelete + } + return nil +} + +func (x *NodeSetParameter) GetForce() bool { + if x != nil { + return x.Force + } + return false +} + +func (x *NodeSetParameter) GetInit() string { + if x != nil { + return x.Init + } + return "" +} + +func (x *NodeSetParameter) GetDiscoverable() bool { + if x != nil { + return x.Discoverable + } + return false +} + +func (x *NodeSetParameter) GetUndiscoverable() bool { + if x != nil { + return x.Undiscoverable + } + return false +} + +func (x *NodeSetParameter) GetRoot() string { + if x != nil { + return x.Root + } + return "" +} + +func (x *NodeSetParameter) GetTags() []string { + if x != nil { + return x.Tags + } + return nil +} + +func (x *NodeSetParameter) GetTagsDelete() []string { + if x != nil { + return x.TagsDelete + } + return nil +} + +func (x *NodeSetParameter) GetAssetKey() string { + if x != nil { + return x.AssetKey + } + return "" +} + +func (x *NodeSetParameter) GetNodeNames() []string { + if x != nil { + return x.NodeNames + } + return nil +} + +func (x *NodeSetParameter) GetIpmiWrite() string { + if x != nil { + return x.IpmiWrite + } + return "" +} + +func (x *NodeSetParameter) GetNetTags() []string { + if x != nil { + return x.NetTags + } + return nil +} + +func (x *NodeSetParameter) GetNetDeleteTags() []string { + if x != nil { + return x.NetDeleteTags + } + return nil +} + +// NodeStatus contains information about the imaging status per node. +type NodeStatus struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NodeName string `protobuf:"bytes,1,opt,name=nodeName,proto3" json:"nodeName,omitempty"` // Name (Id) of the node. + Stage string `protobuf:"bytes,2,opt,name=stage,proto3" json:"stage,omitempty"` // Stage of imaging. + Sent string `protobuf:"bytes,3,opt,name=sent,proto3" json:"sent,omitempty"` // Last overlay sent. + Ipaddr string `protobuf:"bytes,4,opt,name=ipaddr,proto3" json:"ipaddr,omitempty"` // Node IP address. + Lastseen int64 `protobuf:"varint,5,opt,name=lastseen,proto3" json:"lastseen,omitempty"` // Time in seconds since the node was last seen. +} + +func (x *NodeStatus) Reset() { + *x = NodeStatus{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeStatus) ProtoMessage() {} + +func (x *NodeStatus) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeStatus.ProtoReflect.Descriptor instead. +func (*NodeStatus) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{16} +} + +func (x *NodeStatus) GetNodeName() string { + if x != nil { + return x.NodeName + } + return "" +} + +func (x *NodeStatus) GetStage() string { + if x != nil { + return x.Stage + } + return "" +} + +func (x *NodeStatus) GetSent() string { + if x != nil { + return x.Sent + } + return "" +} + +func (x *NodeStatus) GetIpaddr() string { + if x != nil { + return x.Ipaddr + } + return "" +} + +func (x *NodeStatus) GetLastseen() int64 { + if x != nil { + return x.Lastseen + } + return 0 +} + +// NodeStatusResponse contains NodeStatus for zero or more nodes. +type NodeStatusResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NodeStatus []*NodeStatus `protobuf:"bytes,1,rep,name=nodeStatus,proto3" json:"nodeStatus,omitempty"` +} + +func (x *NodeStatusResponse) Reset() { + *x = NodeStatusResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeStatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeStatusResponse) ProtoMessage() {} + +func (x *NodeStatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeStatusResponse.ProtoReflect.Descriptor instead. +func (*NodeStatusResponse) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{17} +} + +func (x *NodeStatusResponse) GetNodeStatus() []*NodeStatus { + if x != nil { + return x.NodeStatus + } + return nil +} + +// VersionReponse contains versions of the software. +type VersionResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ApiPrefix string `protobuf:"bytes,1,opt,name=apiPrefix,proto3" json:"apiPrefix,omitempty"` + ApiVersion string `protobuf:"bytes,2,opt,name=apiVersion,proto3" json:"apiVersion,omitempty"` + WarewulfVersion string `protobuf:"bytes,3,opt,name=warewulfVersion,proto3" json:"warewulfVersion,omitempty"` +} + +func (x *VersionResponse) Reset() { + *x = VersionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VersionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VersionResponse) ProtoMessage() {} + +func (x *VersionResponse) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VersionResponse.ProtoReflect.Descriptor instead. +func (*VersionResponse) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{18} +} + +func (x *VersionResponse) GetApiPrefix() string { + if x != nil { + return x.ApiPrefix + } + return "" +} + +func (x *VersionResponse) GetApiVersion() string { + if x != nil { + return x.ApiVersion + } + return "" +} + +func (x *VersionResponse) GetWarewulfVersion() string { + if x != nil { + return x.WarewulfVersion + } + return "" +} + +var File_routes_proto protoreflect.FileDescriptor + +var file_routes_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, + 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0x83, 0x01, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, + 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, + 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x22, 0x42, 0x0a, 0x18, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xc0, 0x01, + 0x0a, 0x18, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6d, 0x70, 0x6f, 0x72, + 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x79, 0x6e, 0x63, 0x55, 0x73, 0x65, 0x72, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x79, 0x6e, 0x63, 0x55, 0x73, 0x65, 0x72, + 0x22, 0x67, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6b, 0x65, 0x72, 0x6e, + 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x50, 0x0a, 0x15, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x22, 0x3e, 0x0a, 0x16, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x68, 0x6f, 0x77, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x7f, 0x0a, 0x15, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x52, 0x6f, 0x6f, 0x74, + 0x66, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x52, 0x6f, 0x6f, 0x74, 0x66, 0x73, + 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x4b, + 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x42, 0x0a, 0x1a, + 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x79, 0x6e, 0x63, 0x55, 0x73, 0x65, + 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, + 0x22, 0x29, 0x0a, 0x09, 0x4e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x4f, 0x0a, 0x09, 0x4e, + 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x22, 0xf0, 0x03, 0x0a, + 0x06, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x12, 0x27, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x2b, 0x0a, 0x06, 0x6f, 0x6e, 0x62, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x6f, 0x6e, 0x62, 0x6f, 0x6f, 0x74, 0x12, 0x2b, 0x0a, + 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x68, 0x77, + 0x61, 0x64, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, + 0x06, 0x68, 0x77, 0x61, 0x64, 0x64, 0x72, 0x12, 0x2b, 0x0a, 0x06, 0x69, 0x70, 0x61, 0x64, 0x64, + 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x69, 0x70, + 0x61, 0x64, 0x64, 0x72, 0x12, 0x2d, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x6d, + 0x61, 0x73, 0x6b, 0x12, 0x2d, 0x0a, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x12, 0x2d, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, + 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x12, 0x2e, 0x0a, 0x04, 0x54, 0x61, 0x67, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x44, 0x65, + 0x76, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x54, 0x61, 0x67, + 0x73, 0x1a, 0x4c, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0xdb, 0x0b, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x23, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x2d, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, + 0x12, 0x2d, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, + 0x37, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x31, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x0e, 0x6b, + 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, + 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0e, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, + 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x33, 0x0a, 0x0a, 0x6b, 0x65, 0x72, 0x6e, + 0x65, 0x6c, 0x41, 0x72, 0x67, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, + 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x52, 0x0a, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x41, 0x72, 0x67, 0x73, 0x12, 0x39, 0x0a, + 0x0d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0d, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x12, 0x3b, 0x0a, 0x0e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x76, + 0x65, 0x72, 0x6c, 0x61, 0x79, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x70, 0x78, 0x65, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, + 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x04, 0x69, 0x70, 0x78, 0x65, 0x12, 0x27, + 0x0a, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, + 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, + 0x12, 0x2f, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x4b, 0x65, + 0x79, 0x12, 0x33, 0x0a, 0x0a, 0x69, 0x70, 0x6d, 0x69, 0x49, 0x70, 0x61, 0x64, 0x64, 0x72, 0x18, + 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0a, 0x69, 0x70, 0x6d, 0x69, + 0x49, 0x70, 0x61, 0x64, 0x64, 0x72, 0x12, 0x35, 0x0a, 0x0b, 0x69, 0x70, 0x6d, 0x69, 0x4e, 0x65, + 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x52, 0x0b, 0x69, 0x70, 0x6d, 0x69, 0x4e, 0x65, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x12, 0x2f, 0x0a, + 0x08, 0x69, 0x70, 0x6d, 0x69, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x52, 0x08, 0x69, 0x70, 0x6d, 0x69, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x35, + 0x0a, 0x0b, 0x69, 0x70, 0x6d, 0x69, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x11, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, + 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0b, 0x69, 0x70, 0x6d, 0x69, 0x47, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x37, 0x0a, 0x0c, 0x69, 0x70, 0x6d, 0x69, 0x55, 0x73, 0x65, + 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x52, 0x0c, 0x69, 0x70, 0x6d, 0x69, 0x55, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x39, + 0x0a, 0x0d, 0x69, 0x70, 0x6d, 0x69, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, + 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0d, 0x69, 0x70, 0x6d, 0x69, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x0c, 0x69, 0x70, 0x6d, + 0x69, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0c, 0x69, 0x70, 0x6d, 0x69, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x15, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x24, + 0x0a, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, + 0x16, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x73, 0x18, + 0x17, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x73, 0x12, + 0x30, 0x0a, 0x04, 0x54, 0x61, 0x67, 0x73, 0x18, 0x18, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x54, 0x61, 0x67, + 0x73, 0x12, 0x30, 0x0a, 0x04, 0x4b, 0x65, 0x79, 0x73, 0x18, 0x19, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x4b, + 0x65, 0x79, 0x73, 0x1a, 0x4c, 0x0a, 0x0c, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x1a, 0x4c, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x4c, 0x0a, 0x09, 0x4b, 0x65, 0x79, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3c, 0x0a, + 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x28, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0xb2, 0x02, 0x0a, 0x10, + 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69, + 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0c, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x77, 0x61, 0x64, + 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x77, 0x61, 0x64, 0x64, 0x72, + 0x12, 0x16, 0x0a, 0x06, 0x69, 0x70, 0x61, 0x64, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x69, 0x70, 0x61, 0x64, 0x64, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x65, 0x74, 0x64, + 0x65, 0x76, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, + 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, + 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x64, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x70, 0x61, 0x64, 0x64, 0x72, + 0x36, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x70, 0x61, 0x64, 0x64, 0x72, 0x36, + 0x22, 0x49, 0x0a, 0x13, 0x4e, 0x6f, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x1c, 0x0a, + 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xf6, 0x09, 0x0a, 0x10, + 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0e, 0x6b, 0x65, 0x72, 0x6e, + 0x65, 0x6c, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, + 0x12, 0x1e, 0x0a, 0x0a, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x41, 0x72, 0x67, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x41, 0x72, 0x67, 0x73, + 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x65, + 0x74, 0x64, 0x65, 0x76, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x65, 0x74, 0x64, + 0x65, 0x76, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x70, 0x61, 0x64, 0x64, 0x72, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x61, 0x64, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, + 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, + 0x6d, 0x61, 0x73, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x16, + 0x0a, 0x06, 0x68, 0x77, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x68, 0x77, 0x61, 0x64, 0x64, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x6e, + 0x62, 0x6f, 0x6f, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x6e, 0x62, 0x6f, + 0x6f, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x65, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x78, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x69, 0x70, 0x78, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x69, 0x74, 0x4f, 0x76, 0x65, 0x72, + 0x6c, 0x61, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x69, 0x74, 0x4f, + 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x18, 0x12, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x12, 0x24, + 0x0a, 0x0d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x18, + 0x13, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4f, 0x76, 0x65, + 0x72, 0x6c, 0x61, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x70, 0x6d, 0x69, 0x49, 0x70, 0x61, 0x64, + 0x64, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x70, 0x6d, 0x69, 0x49, 0x70, + 0x61, 0x64, 0x64, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x70, 0x6d, 0x69, 0x4e, 0x65, 0x74, 0x6d, + 0x61, 0x73, 0x6b, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x70, 0x6d, 0x69, 0x4e, + 0x65, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x70, 0x6d, 0x69, 0x50, 0x6f, + 0x72, 0x74, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x70, 0x6d, 0x69, 0x50, 0x6f, + 0x72, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x70, 0x6d, 0x69, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x70, 0x6d, 0x69, 0x47, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x70, 0x6d, 0x69, 0x55, 0x73, 0x65, 0x72, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x70, 0x6d, 0x69, + 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x70, 0x6d, 0x69, + 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x69, 0x70, 0x6d, 0x69, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x24, 0x0a, 0x0d, + 0x69, 0x70, 0x6d, 0x69, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x1a, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x69, 0x70, 0x6d, 0x69, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x1b, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x18, + 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x41, 0x64, 0x64, 0x18, 0x1d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x41, 0x64, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x1e, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, + 0x6f, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x20, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, + 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x26, 0x0a, 0x0e, + 0x75, 0x6e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x22, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x75, 0x6e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x23, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, + 0x18, 0x24, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, + 0x74, 0x61, 0x67, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x25, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0a, 0x74, 0x61, 0x67, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x18, 0x26, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x27, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x64, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x70, 0x6d, 0x69, 0x57, 0x72, + 0x69, 0x74, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x70, 0x6d, 0x69, 0x57, + 0x72, 0x69, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x54, 0x61, 0x67, 0x73, 0x18, + 0x29, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x54, 0x61, 0x67, 0x73, 0x12, 0x24, + 0x0a, 0x0d, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x73, 0x18, + 0x2a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x54, 0x61, 0x67, 0x73, 0x22, 0x86, 0x01, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x70, 0x61, + 0x64, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x61, 0x64, 0x64, + 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x73, 0x65, 0x65, 0x6e, 0x22, 0x4a, 0x0a, + 0x12, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, 0x6e, + 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x79, 0x0a, 0x0f, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x61, 0x70, 0x69, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x61, 0x70, 0x69, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x70, + 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x77, 0x61, + 0x72, 0x65, 0x77, 0x75, 0x6c, 0x66, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x61, 0x72, 0x65, 0x77, 0x75, 0x6c, 0x66, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x32, 0xa6, 0x08, 0x0a, 0x05, 0x57, 0x57, 0x41, 0x70, 0x69, 0x12, 0x73, + 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x12, 0x21, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, + 0x74, 0x65, 0x72, 0x1a, 0x1f, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, + 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x3a, 0x01, 0x2a, 0x12, 0x64, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x2a, 0x0d, 0x2f, 0x76, 0x31, 0x2f, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x70, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x22, 0x2e, 0x77, + 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x1a, 0x1f, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x63, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0d, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1f, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, + 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x6d, 0x0a, 0x0d, + 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x68, 0x6f, 0x77, 0x12, 0x20, 0x2e, + 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x53, 0x68, 0x6f, 0x77, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, + 0x1f, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x56, 0x0a, 0x07, 0x4e, + 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x12, 0x1a, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x1a, 0x1a, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x22, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, + 0x3a, 0x01, 0x2a, 0x12, 0x55, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x12, 0x1d, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, + 0x2a, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x4d, 0x0a, 0x08, 0x4e, 0x6f, + 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x1a, 0x1a, 0x2e, 0x77, 0x77, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x12, + 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x59, 0x0a, 0x07, 0x4e, 0x6f, 0x64, + 0x65, 0x53, 0x65, 0x74, 0x12, 0x1a, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x1a, 0x1a, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x10, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x65, + 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x1a, 0x1c, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, + 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x4e, 0x0a, + 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x1a, 0x19, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x29, 0x5a, + 0x27, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x77, 0x77, 0x61, 0x70, 0x69, 0x76, 0x31, + 0x3b, 0x77, 0x77, 0x61, 0x70, 0x69, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_routes_proto_rawDescOnce sync.Once + file_routes_proto_rawDescData = file_routes_proto_rawDesc +) + +func file_routes_proto_rawDescGZIP() []byte { + file_routes_proto_rawDescOnce.Do(func() { + file_routes_proto_rawDescData = protoimpl.X.CompressGZIP(file_routes_proto_rawDescData) + }) + return file_routes_proto_rawDescData +} + +var file_routes_proto_msgTypes = make([]protoimpl.MessageInfo, 23) +var file_routes_proto_goTypes = []interface{}{ + (*ContainerBuildParameter)(nil), // 0: wwapi.v1.ContainerBuildParameter + (*ContainerDeleteParameter)(nil), // 1: wwapi.v1.ContainerDeleteParameter + (*ContainerImportParameter)(nil), // 2: wwapi.v1.ContainerImportParameter + (*ContainerInfo)(nil), // 3: wwapi.v1.ContainerInfo + (*ContainerListResponse)(nil), // 4: wwapi.v1.ContainerListResponse + (*ContainerShowParameter)(nil), // 5: wwapi.v1.ContainerShowParameter + (*ContainerShowResponse)(nil), // 6: wwapi.v1.ContainerShowResponse + (*ContainerSyncUserParameter)(nil), // 7: wwapi.v1.ContainerSyncUserParameter + (*NodeNames)(nil), // 8: wwapi.v1.NodeNames + (*NodeField)(nil), // 9: wwapi.v1.NodeField + (*NetDev)(nil), // 10: wwapi.v1.NetDev + (*NodeInfo)(nil), // 11: wwapi.v1.NodeInfo + (*NodeListResponse)(nil), // 12: wwapi.v1.NodeListResponse + (*NodeAddParameter)(nil), // 13: wwapi.v1.NodeAddParameter + (*NodeDeleteParameter)(nil), // 14: wwapi.v1.NodeDeleteParameter + (*NodeSetParameter)(nil), // 15: wwapi.v1.NodeSetParameter + (*NodeStatus)(nil), // 16: wwapi.v1.NodeStatus + (*NodeStatusResponse)(nil), // 17: wwapi.v1.NodeStatusResponse + (*VersionResponse)(nil), // 18: wwapi.v1.VersionResponse + nil, // 19: wwapi.v1.NetDev.TagsEntry + nil, // 20: wwapi.v1.NodeInfo.NetDevsEntry + nil, // 21: wwapi.v1.NodeInfo.TagsEntry + nil, // 22: wwapi.v1.NodeInfo.KeysEntry + (*emptypb.Empty)(nil), // 23: google.protobuf.Empty +} +var file_routes_proto_depIdxs = []int32{ + 3, // 0: wwapi.v1.ContainerListResponse.containers:type_name -> wwapi.v1.ContainerInfo + 9, // 1: wwapi.v1.NetDev.type:type_name -> wwapi.v1.NodeField + 9, // 2: wwapi.v1.NetDev.onboot:type_name -> wwapi.v1.NodeField + 9, // 3: wwapi.v1.NetDev.device:type_name -> wwapi.v1.NodeField + 9, // 4: wwapi.v1.NetDev.hwaddr:type_name -> wwapi.v1.NodeField + 9, // 5: wwapi.v1.NetDev.ipaddr:type_name -> wwapi.v1.NodeField + 9, // 6: wwapi.v1.NetDev.netmask:type_name -> wwapi.v1.NodeField + 9, // 7: wwapi.v1.NetDev.gateway:type_name -> wwapi.v1.NodeField + 9, // 8: wwapi.v1.NetDev.primary:type_name -> wwapi.v1.NodeField + 19, // 9: wwapi.v1.NetDev.Tags:type_name -> wwapi.v1.NetDev.TagsEntry + 9, // 10: wwapi.v1.NodeInfo.id:type_name -> wwapi.v1.NodeField + 9, // 11: wwapi.v1.NodeInfo.comment:type_name -> wwapi.v1.NodeField + 9, // 12: wwapi.v1.NodeInfo.cluster:type_name -> wwapi.v1.NodeField + 9, // 13: wwapi.v1.NodeInfo.discoverable:type_name -> wwapi.v1.NodeField + 9, // 14: wwapi.v1.NodeInfo.container:type_name -> wwapi.v1.NodeField + 9, // 15: wwapi.v1.NodeInfo.kernelOverride:type_name -> wwapi.v1.NodeField + 9, // 16: wwapi.v1.NodeInfo.kernelArgs:type_name -> wwapi.v1.NodeField + 9, // 17: wwapi.v1.NodeInfo.systemOverlay:type_name -> wwapi.v1.NodeField + 9, // 18: wwapi.v1.NodeInfo.runtimeOverlay:type_name -> wwapi.v1.NodeField + 9, // 19: wwapi.v1.NodeInfo.ipxe:type_name -> wwapi.v1.NodeField + 9, // 20: wwapi.v1.NodeInfo.init:type_name -> wwapi.v1.NodeField + 9, // 21: wwapi.v1.NodeInfo.root:type_name -> wwapi.v1.NodeField + 9, // 22: wwapi.v1.NodeInfo.assetKey:type_name -> wwapi.v1.NodeField + 9, // 23: wwapi.v1.NodeInfo.ipmiIpaddr:type_name -> wwapi.v1.NodeField + 9, // 24: wwapi.v1.NodeInfo.ipmiNetmask:type_name -> wwapi.v1.NodeField + 9, // 25: wwapi.v1.NodeInfo.ipmiPort:type_name -> wwapi.v1.NodeField + 9, // 26: wwapi.v1.NodeInfo.ipmiGateway:type_name -> wwapi.v1.NodeField + 9, // 27: wwapi.v1.NodeInfo.ipmiUserName:type_name -> wwapi.v1.NodeField + 9, // 28: wwapi.v1.NodeInfo.ipmiInterface:type_name -> wwapi.v1.NodeField + 9, // 29: wwapi.v1.NodeInfo.ipmiPassword:type_name -> wwapi.v1.NodeField + 20, // 30: wwapi.v1.NodeInfo.NetDevs:type_name -> wwapi.v1.NodeInfo.NetDevsEntry + 21, // 31: wwapi.v1.NodeInfo.Tags:type_name -> wwapi.v1.NodeInfo.TagsEntry + 22, // 32: wwapi.v1.NodeInfo.Keys:type_name -> wwapi.v1.NodeInfo.KeysEntry + 11, // 33: wwapi.v1.NodeListResponse.nodes:type_name -> wwapi.v1.NodeInfo + 16, // 34: wwapi.v1.NodeStatusResponse.nodeStatus:type_name -> wwapi.v1.NodeStatus + 9, // 35: wwapi.v1.NetDev.TagsEntry.value:type_name -> wwapi.v1.NodeField + 10, // 36: wwapi.v1.NodeInfo.NetDevsEntry.value:type_name -> wwapi.v1.NetDev + 9, // 37: wwapi.v1.NodeInfo.TagsEntry.value:type_name -> wwapi.v1.NodeField + 9, // 38: wwapi.v1.NodeInfo.KeysEntry.value:type_name -> wwapi.v1.NodeField + 0, // 39: wwapi.v1.WWApi.ContainerBuild:input_type -> wwapi.v1.ContainerBuildParameter + 1, // 40: wwapi.v1.WWApi.ContainerDelete:input_type -> wwapi.v1.ContainerDeleteParameter + 2, // 41: wwapi.v1.WWApi.ContainerImport:input_type -> wwapi.v1.ContainerImportParameter + 23, // 42: wwapi.v1.WWApi.ContainerList:input_type -> google.protobuf.Empty + 5, // 43: wwapi.v1.WWApi.ContainerShow:input_type -> wwapi.v1.ContainerShowParameter + 13, // 44: wwapi.v1.WWApi.NodeAdd:input_type -> wwapi.v1.NodeAddParameter + 14, // 45: wwapi.v1.WWApi.NodeDelete:input_type -> wwapi.v1.NodeDeleteParameter + 8, // 46: wwapi.v1.WWApi.NodeList:input_type -> wwapi.v1.NodeNames + 15, // 47: wwapi.v1.WWApi.NodeSet:input_type -> wwapi.v1.NodeSetParameter + 8, // 48: wwapi.v1.WWApi.NodeStatus:input_type -> wwapi.v1.NodeNames + 23, // 49: wwapi.v1.WWApi.Version:input_type -> google.protobuf.Empty + 4, // 50: wwapi.v1.WWApi.ContainerBuild:output_type -> wwapi.v1.ContainerListResponse + 23, // 51: wwapi.v1.WWApi.ContainerDelete:output_type -> google.protobuf.Empty + 4, // 52: wwapi.v1.WWApi.ContainerImport:output_type -> wwapi.v1.ContainerListResponse + 4, // 53: wwapi.v1.WWApi.ContainerList:output_type -> wwapi.v1.ContainerListResponse + 6, // 54: wwapi.v1.WWApi.ContainerShow:output_type -> wwapi.v1.ContainerShowResponse + 12, // 55: wwapi.v1.WWApi.NodeAdd:output_type -> wwapi.v1.NodeListResponse + 23, // 56: wwapi.v1.WWApi.NodeDelete:output_type -> google.protobuf.Empty + 12, // 57: wwapi.v1.WWApi.NodeList:output_type -> wwapi.v1.NodeListResponse + 12, // 58: wwapi.v1.WWApi.NodeSet:output_type -> wwapi.v1.NodeListResponse + 17, // 59: wwapi.v1.WWApi.NodeStatus:output_type -> wwapi.v1.NodeStatusResponse + 18, // 60: wwapi.v1.WWApi.Version:output_type -> wwapi.v1.VersionResponse + 50, // [50:61] is the sub-list for method output_type + 39, // [39:50] is the sub-list for method input_type + 39, // [39:39] is the sub-list for extension type_name + 39, // [39:39] is the sub-list for extension extendee + 0, // [0:39] is the sub-list for field type_name +} + +func init() { file_routes_proto_init() } +func file_routes_proto_init() { + if File_routes_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_routes_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContainerBuildParameter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContainerDeleteParameter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContainerImportParameter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContainerInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContainerListResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContainerShowParameter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContainerShowResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContainerSyncUserParameter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeNames); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeField); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetDev); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeListResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeAddParameter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeDeleteParameter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeSetParameter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeStatus); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeStatusResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VersionResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_routes_proto_rawDesc, + NumEnums: 0, + NumMessages: 23, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_routes_proto_goTypes, + DependencyIndexes: file_routes_proto_depIdxs, + MessageInfos: file_routes_proto_msgTypes, + }.Build() + File_routes_proto = out.File + file_routes_proto_rawDesc = nil + file_routes_proto_goTypes = nil + file_routes_proto_depIdxs = nil +} diff --git a/internal/pkg/api/routes/wwapiv1/routes.pb.gw.go b/internal/pkg/api/routes/wwapiv1/routes.pb.gw.go new file mode 100644 index 00000000..167091fb --- /dev/null +++ b/internal/pkg/api/routes/wwapiv1/routes.pb.gw.go @@ -0,0 +1,978 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: routes.proto + +/* +Package wwapiv1 is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package wwapiv1 + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/emptypb" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +func request_WWApi_ContainerBuild_0(ctx context.Context, marshaler runtime.Marshaler, client WWApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ContainerBuildParameter + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ContainerBuild(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WWApi_ContainerBuild_0(ctx context.Context, marshaler runtime.Marshaler, server WWApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ContainerBuildParameter + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ContainerBuild(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_WWApi_ContainerDelete_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_WWApi_ContainerDelete_0(ctx context.Context, marshaler runtime.Marshaler, client WWApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ContainerDeleteParameter + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WWApi_ContainerDelete_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ContainerDelete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WWApi_ContainerDelete_0(ctx context.Context, marshaler runtime.Marshaler, server WWApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ContainerDeleteParameter + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WWApi_ContainerDelete_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ContainerDelete(ctx, &protoReq) + return msg, metadata, err + +} + +func request_WWApi_ContainerImport_0(ctx context.Context, marshaler runtime.Marshaler, client WWApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ContainerImportParameter + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ContainerImport(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WWApi_ContainerImport_0(ctx context.Context, marshaler runtime.Marshaler, server WWApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ContainerImportParameter + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ContainerImport(ctx, &protoReq) + return msg, metadata, err + +} + +func request_WWApi_ContainerList_0(ctx context.Context, marshaler runtime.Marshaler, client WWApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq emptypb.Empty + var metadata runtime.ServerMetadata + + msg, err := client.ContainerList(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WWApi_ContainerList_0(ctx context.Context, marshaler runtime.Marshaler, server WWApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq emptypb.Empty + var metadata runtime.ServerMetadata + + msg, err := server.ContainerList(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_WWApi_ContainerShow_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_WWApi_ContainerShow_0(ctx context.Context, marshaler runtime.Marshaler, client WWApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ContainerShowParameter + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WWApi_ContainerShow_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ContainerShow(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WWApi_ContainerShow_0(ctx context.Context, marshaler runtime.Marshaler, server WWApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ContainerShowParameter + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WWApi_ContainerShow_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ContainerShow(ctx, &protoReq) + return msg, metadata, err + +} + +func request_WWApi_NodeAdd_0(ctx context.Context, marshaler runtime.Marshaler, client WWApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq NodeAddParameter + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.NodeAdd(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WWApi_NodeAdd_0(ctx context.Context, marshaler runtime.Marshaler, server WWApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq NodeAddParameter + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.NodeAdd(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_WWApi_NodeDelete_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_WWApi_NodeDelete_0(ctx context.Context, marshaler runtime.Marshaler, client WWApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq NodeDeleteParameter + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WWApi_NodeDelete_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.NodeDelete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WWApi_NodeDelete_0(ctx context.Context, marshaler runtime.Marshaler, server WWApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq NodeDeleteParameter + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WWApi_NodeDelete_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.NodeDelete(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_WWApi_NodeList_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_WWApi_NodeList_0(ctx context.Context, marshaler runtime.Marshaler, client WWApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq NodeNames + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WWApi_NodeList_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.NodeList(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WWApi_NodeList_0(ctx context.Context, marshaler runtime.Marshaler, server WWApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq NodeNames + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WWApi_NodeList_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.NodeList(ctx, &protoReq) + return msg, metadata, err + +} + +func request_WWApi_NodeSet_0(ctx context.Context, marshaler runtime.Marshaler, client WWApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq NodeSetParameter + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.NodeSet(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WWApi_NodeSet_0(ctx context.Context, marshaler runtime.Marshaler, server WWApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq NodeSetParameter + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.NodeSet(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_WWApi_NodeStatus_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_WWApi_NodeStatus_0(ctx context.Context, marshaler runtime.Marshaler, client WWApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq NodeNames + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WWApi_NodeStatus_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.NodeStatus(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WWApi_NodeStatus_0(ctx context.Context, marshaler runtime.Marshaler, server WWApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq NodeNames + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WWApi_NodeStatus_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.NodeStatus(ctx, &protoReq) + return msg, metadata, err + +} + +func request_WWApi_Version_0(ctx context.Context, marshaler runtime.Marshaler, client WWApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq emptypb.Empty + var metadata runtime.ServerMetadata + + msg, err := client.Version(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WWApi_Version_0(ctx context.Context, marshaler runtime.Marshaler, server WWApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq emptypb.Empty + var metadata runtime.ServerMetadata + + msg, err := server.Version(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterWWApiHandlerServer registers the http handlers for service WWApi to "mux". +// UnaryRPC :call WWApiServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterWWApiHandlerFromEndpoint instead. +func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, server WWApiServer) error { + + mux.Handle("POST", pattern_WWApi_ContainerBuild_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerBuild", runtime.WithHTTPPathPattern("/v1/containerbuild")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WWApi_ContainerBuild_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_ContainerBuild_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_WWApi_ContainerDelete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerDelete", runtime.WithHTTPPathPattern("/v1/container")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WWApi_ContainerDelete_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_ContainerDelete_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_WWApi_ContainerImport_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerImport", runtime.WithHTTPPathPattern("/v1/container")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WWApi_ContainerImport_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_ContainerImport_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_WWApi_ContainerList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerList", runtime.WithHTTPPathPattern("/v1/container")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WWApi_ContainerList_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_ContainerList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_WWApi_ContainerShow_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerShow", runtime.WithHTTPPathPattern("/v1/containershow")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WWApi_ContainerShow_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_ContainerShow_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_WWApi_NodeAdd_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeAdd", runtime.WithHTTPPathPattern("/v1/node")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WWApi_NodeAdd_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_NodeAdd_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_WWApi_NodeDelete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeDelete", runtime.WithHTTPPathPattern("/v1/node")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WWApi_NodeDelete_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_NodeDelete_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_WWApi_NodeList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeList", runtime.WithHTTPPathPattern("/v1/node")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WWApi_NodeList_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_NodeList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_WWApi_NodeSet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeSet", runtime.WithHTTPPathPattern("/v1/nodeset")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WWApi_NodeSet_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_NodeSet_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_WWApi_NodeStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeStatus", runtime.WithHTTPPathPattern("/v1/nodestatus")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WWApi_NodeStatus_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_NodeStatus_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_WWApi_Version_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/Version", runtime.WithHTTPPathPattern("/version")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WWApi_Version_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_Version_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterWWApiHandlerFromEndpoint is same as RegisterWWApiHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterWWApiHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterWWApiHandler(ctx, mux, conn) +} + +// RegisterWWApiHandler registers the http handlers for service WWApi to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterWWApiHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterWWApiHandlerClient(ctx, mux, NewWWApiClient(conn)) +} + +// RegisterWWApiHandlerClient registers the http handlers for service WWApi +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "WWApiClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "WWApiClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "WWApiClient" to call the correct interceptors. +func RegisterWWApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, client WWApiClient) error { + + mux.Handle("POST", pattern_WWApi_ContainerBuild_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerBuild", runtime.WithHTTPPathPattern("/v1/containerbuild")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WWApi_ContainerBuild_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_ContainerBuild_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_WWApi_ContainerDelete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerDelete", runtime.WithHTTPPathPattern("/v1/container")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WWApi_ContainerDelete_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_ContainerDelete_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_WWApi_ContainerImport_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerImport", runtime.WithHTTPPathPattern("/v1/container")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WWApi_ContainerImport_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_ContainerImport_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_WWApi_ContainerList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerList", runtime.WithHTTPPathPattern("/v1/container")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WWApi_ContainerList_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_ContainerList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_WWApi_ContainerShow_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerShow", runtime.WithHTTPPathPattern("/v1/containershow")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WWApi_ContainerShow_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_ContainerShow_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_WWApi_NodeAdd_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeAdd", runtime.WithHTTPPathPattern("/v1/node")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WWApi_NodeAdd_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_NodeAdd_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_WWApi_NodeDelete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeDelete", runtime.WithHTTPPathPattern("/v1/node")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WWApi_NodeDelete_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_NodeDelete_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_WWApi_NodeList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeList", runtime.WithHTTPPathPattern("/v1/node")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WWApi_NodeList_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_NodeList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_WWApi_NodeSet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeSet", runtime.WithHTTPPathPattern("/v1/nodeset")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WWApi_NodeSet_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_NodeSet_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_WWApi_NodeStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeStatus", runtime.WithHTTPPathPattern("/v1/nodestatus")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WWApi_NodeStatus_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_NodeStatus_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_WWApi_Version_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/Version", runtime.WithHTTPPathPattern("/version")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WWApi_Version_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_Version_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_WWApi_ContainerBuild_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "containerbuild"}, "")) + + pattern_WWApi_ContainerDelete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "container"}, "")) + + pattern_WWApi_ContainerImport_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "container"}, "")) + + pattern_WWApi_ContainerList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "container"}, "")) + + pattern_WWApi_ContainerShow_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "containershow"}, "")) + + pattern_WWApi_NodeAdd_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "node"}, "")) + + pattern_WWApi_NodeDelete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "node"}, "")) + + pattern_WWApi_NodeList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "node"}, "")) + + pattern_WWApi_NodeSet_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "nodeset"}, "")) + + pattern_WWApi_NodeStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "nodestatus"}, "")) + + pattern_WWApi_Version_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"version"}, "")) +) + +var ( + forward_WWApi_ContainerBuild_0 = runtime.ForwardResponseMessage + + forward_WWApi_ContainerDelete_0 = runtime.ForwardResponseMessage + + forward_WWApi_ContainerImport_0 = runtime.ForwardResponseMessage + + forward_WWApi_ContainerList_0 = runtime.ForwardResponseMessage + + forward_WWApi_ContainerShow_0 = runtime.ForwardResponseMessage + + forward_WWApi_NodeAdd_0 = runtime.ForwardResponseMessage + + forward_WWApi_NodeDelete_0 = runtime.ForwardResponseMessage + + forward_WWApi_NodeList_0 = runtime.ForwardResponseMessage + + forward_WWApi_NodeSet_0 = runtime.ForwardResponseMessage + + forward_WWApi_NodeStatus_0 = runtime.ForwardResponseMessage + + forward_WWApi_Version_0 = runtime.ForwardResponseMessage +) diff --git a/internal/pkg/api/routes/wwapiv1/routes_grpc.pb.go b/internal/pkg/api/routes/wwapiv1/routes_grpc.pb.go new file mode 100644 index 00000000..4c5096b5 --- /dev/null +++ b/internal/pkg/api/routes/wwapiv1/routes_grpc.pb.go @@ -0,0 +1,494 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.21.1 +// source: routes.proto + +package wwapiv1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// WWApiClient is the client API for WWApi service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type WWApiClient interface { + // ContainerBuild builds zero or more containers. + ContainerBuild(ctx context.Context, in *ContainerBuildParameter, opts ...grpc.CallOption) (*ContainerListResponse, error) + // ContainerDelete removes one or more container from Warewulf management. + ContainerDelete(ctx context.Context, in *ContainerDeleteParameter, opts ...grpc.CallOption) (*emptypb.Empty, error) + // ContainerImport imports a container to Warewulf. + ContainerImport(ctx context.Context, in *ContainerImportParameter, opts ...grpc.CallOption) (*ContainerListResponse, error) + // ContainerList lists ContainerInfo for each container. + ContainerList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ContainerListResponse, error) + // ContainerShow lists ContainerShow for each container. + ContainerShow(ctx context.Context, in *ContainerShowParameter, opts ...grpc.CallOption) (*ContainerShowResponse, error) + // NodeAdd adds one or more nodes for management by Warewulf and returns + // the added nodes. Node fields may be shimmed in per profiles. + NodeAdd(ctx context.Context, in *NodeAddParameter, opts ...grpc.CallOption) (*NodeListResponse, error) + // NodeDelete removes one or more nodes from Warewulf management. + NodeDelete(ctx context.Context, in *NodeDeleteParameter, opts ...grpc.CallOption) (*emptypb.Empty, error) + // NodeList lists some or all nodes managed by Warewulf. + NodeList(ctx context.Context, in *NodeNames, opts ...grpc.CallOption) (*NodeListResponse, error) + // NodeSet updates node fields for one or more nodes. + NodeSet(ctx context.Context, in *NodeSetParameter, opts ...grpc.CallOption) (*NodeListResponse, error) + // NodeStatus returns the imaging state for nodes. + // This requires warewulfd. + NodeStatus(ctx context.Context, in *NodeNames, opts ...grpc.CallOption) (*NodeStatusResponse, error) + // Version returns the wwapi version, the api prefix, and the Warewulf + // version. This is also useful for testing if the service is up. + Version(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*VersionResponse, error) +} + +type wWApiClient struct { + cc grpc.ClientConnInterface +} + +func NewWWApiClient(cc grpc.ClientConnInterface) WWApiClient { + return &wWApiClient{cc} +} + +func (c *wWApiClient) ContainerBuild(ctx context.Context, in *ContainerBuildParameter, opts ...grpc.CallOption) (*ContainerListResponse, error) { + out := new(ContainerListResponse) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/ContainerBuild", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *wWApiClient) ContainerDelete(ctx context.Context, in *ContainerDeleteParameter, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/ContainerDelete", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *wWApiClient) ContainerImport(ctx context.Context, in *ContainerImportParameter, opts ...grpc.CallOption) (*ContainerListResponse, error) { + out := new(ContainerListResponse) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/ContainerImport", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *wWApiClient) ContainerList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ContainerListResponse, error) { + out := new(ContainerListResponse) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/ContainerList", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *wWApiClient) ContainerShow(ctx context.Context, in *ContainerShowParameter, opts ...grpc.CallOption) (*ContainerShowResponse, error) { + out := new(ContainerShowResponse) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/ContainerShow", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *wWApiClient) NodeAdd(ctx context.Context, in *NodeAddParameter, opts ...grpc.CallOption) (*NodeListResponse, error) { + out := new(NodeListResponse) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/NodeAdd", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *wWApiClient) NodeDelete(ctx context.Context, in *NodeDeleteParameter, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/NodeDelete", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *wWApiClient) NodeList(ctx context.Context, in *NodeNames, opts ...grpc.CallOption) (*NodeListResponse, error) { + out := new(NodeListResponse) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/NodeList", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *wWApiClient) NodeSet(ctx context.Context, in *NodeSetParameter, opts ...grpc.CallOption) (*NodeListResponse, error) { + out := new(NodeListResponse) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/NodeSet", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *wWApiClient) NodeStatus(ctx context.Context, in *NodeNames, opts ...grpc.CallOption) (*NodeStatusResponse, error) { + out := new(NodeStatusResponse) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/NodeStatus", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *wWApiClient) Version(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*VersionResponse, error) { + out := new(VersionResponse) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/Version", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// WWApiServer is the server API for WWApi service. +// All implementations must embed UnimplementedWWApiServer +// for forward compatibility +type WWApiServer interface { + // ContainerBuild builds zero or more containers. + ContainerBuild(context.Context, *ContainerBuildParameter) (*ContainerListResponse, error) + // ContainerDelete removes one or more container from Warewulf management. + ContainerDelete(context.Context, *ContainerDeleteParameter) (*emptypb.Empty, error) + // ContainerImport imports a container to Warewulf. + ContainerImport(context.Context, *ContainerImportParameter) (*ContainerListResponse, error) + // ContainerList lists ContainerInfo for each container. + ContainerList(context.Context, *emptypb.Empty) (*ContainerListResponse, error) + // ContainerShow lists ContainerShow for each container. + ContainerShow(context.Context, *ContainerShowParameter) (*ContainerShowResponse, error) + // NodeAdd adds one or more nodes for management by Warewulf and returns + // the added nodes. Node fields may be shimmed in per profiles. + NodeAdd(context.Context, *NodeAddParameter) (*NodeListResponse, error) + // NodeDelete removes one or more nodes from Warewulf management. + NodeDelete(context.Context, *NodeDeleteParameter) (*emptypb.Empty, error) + // NodeList lists some or all nodes managed by Warewulf. + NodeList(context.Context, *NodeNames) (*NodeListResponse, error) + // NodeSet updates node fields for one or more nodes. + NodeSet(context.Context, *NodeSetParameter) (*NodeListResponse, error) + // NodeStatus returns the imaging state for nodes. + // This requires warewulfd. + NodeStatus(context.Context, *NodeNames) (*NodeStatusResponse, error) + // Version returns the wwapi version, the api prefix, and the Warewulf + // version. This is also useful for testing if the service is up. + Version(context.Context, *emptypb.Empty) (*VersionResponse, error) + mustEmbedUnimplementedWWApiServer() +} + +// UnimplementedWWApiServer must be embedded to have forward compatible implementations. +type UnimplementedWWApiServer struct { +} + +func (UnimplementedWWApiServer) ContainerBuild(context.Context, *ContainerBuildParameter) (*ContainerListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ContainerBuild not implemented") +} +func (UnimplementedWWApiServer) ContainerDelete(context.Context, *ContainerDeleteParameter) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method ContainerDelete not implemented") +} +func (UnimplementedWWApiServer) ContainerImport(context.Context, *ContainerImportParameter) (*ContainerListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ContainerImport not implemented") +} +func (UnimplementedWWApiServer) ContainerList(context.Context, *emptypb.Empty) (*ContainerListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ContainerList not implemented") +} +func (UnimplementedWWApiServer) ContainerShow(context.Context, *ContainerShowParameter) (*ContainerShowResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ContainerShow not implemented") +} +func (UnimplementedWWApiServer) NodeAdd(context.Context, *NodeAddParameter) (*NodeListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NodeAdd not implemented") +} +func (UnimplementedWWApiServer) NodeDelete(context.Context, *NodeDeleteParameter) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method NodeDelete not implemented") +} +func (UnimplementedWWApiServer) NodeList(context.Context, *NodeNames) (*NodeListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NodeList not implemented") +} +func (UnimplementedWWApiServer) NodeSet(context.Context, *NodeSetParameter) (*NodeListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NodeSet not implemented") +} +func (UnimplementedWWApiServer) NodeStatus(context.Context, *NodeNames) (*NodeStatusResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NodeStatus not implemented") +} +func (UnimplementedWWApiServer) Version(context.Context, *emptypb.Empty) (*VersionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Version not implemented") +} +func (UnimplementedWWApiServer) mustEmbedUnimplementedWWApiServer() {} + +// UnsafeWWApiServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to WWApiServer will +// result in compilation errors. +type UnsafeWWApiServer interface { + mustEmbedUnimplementedWWApiServer() +} + +func RegisterWWApiServer(s grpc.ServiceRegistrar, srv WWApiServer) { + s.RegisterService(&WWApi_ServiceDesc, srv) +} + +func _WWApi_ContainerBuild_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ContainerBuildParameter) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WWApiServer).ContainerBuild(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/wwapi.v1.WWApi/ContainerBuild", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WWApiServer).ContainerBuild(ctx, req.(*ContainerBuildParameter)) + } + return interceptor(ctx, in, info, handler) +} + +func _WWApi_ContainerDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ContainerDeleteParameter) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WWApiServer).ContainerDelete(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/wwapi.v1.WWApi/ContainerDelete", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WWApiServer).ContainerDelete(ctx, req.(*ContainerDeleteParameter)) + } + return interceptor(ctx, in, info, handler) +} + +func _WWApi_ContainerImport_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ContainerImportParameter) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WWApiServer).ContainerImport(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/wwapi.v1.WWApi/ContainerImport", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WWApiServer).ContainerImport(ctx, req.(*ContainerImportParameter)) + } + return interceptor(ctx, in, info, handler) +} + +func _WWApi_ContainerList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WWApiServer).ContainerList(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/wwapi.v1.WWApi/ContainerList", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WWApiServer).ContainerList(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _WWApi_ContainerShow_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ContainerShowParameter) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WWApiServer).ContainerShow(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/wwapi.v1.WWApi/ContainerShow", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WWApiServer).ContainerShow(ctx, req.(*ContainerShowParameter)) + } + return interceptor(ctx, in, info, handler) +} + +func _WWApi_NodeAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NodeAddParameter) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WWApiServer).NodeAdd(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/wwapi.v1.WWApi/NodeAdd", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WWApiServer).NodeAdd(ctx, req.(*NodeAddParameter)) + } + return interceptor(ctx, in, info, handler) +} + +func _WWApi_NodeDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NodeDeleteParameter) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WWApiServer).NodeDelete(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/wwapi.v1.WWApi/NodeDelete", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WWApiServer).NodeDelete(ctx, req.(*NodeDeleteParameter)) + } + return interceptor(ctx, in, info, handler) +} + +func _WWApi_NodeList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NodeNames) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WWApiServer).NodeList(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/wwapi.v1.WWApi/NodeList", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WWApiServer).NodeList(ctx, req.(*NodeNames)) + } + return interceptor(ctx, in, info, handler) +} + +func _WWApi_NodeSet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NodeSetParameter) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WWApiServer).NodeSet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/wwapi.v1.WWApi/NodeSet", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WWApiServer).NodeSet(ctx, req.(*NodeSetParameter)) + } + return interceptor(ctx, in, info, handler) +} + +func _WWApi_NodeStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NodeNames) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WWApiServer).NodeStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/wwapi.v1.WWApi/NodeStatus", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WWApiServer).NodeStatus(ctx, req.(*NodeNames)) + } + return interceptor(ctx, in, info, handler) +} + +func _WWApi_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WWApiServer).Version(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/wwapi.v1.WWApi/Version", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WWApiServer).Version(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +// WWApi_ServiceDesc is the grpc.ServiceDesc for WWApi service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var WWApi_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "wwapi.v1.WWApi", + HandlerType: (*WWApiServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ContainerBuild", + Handler: _WWApi_ContainerBuild_Handler, + }, + { + MethodName: "ContainerDelete", + Handler: _WWApi_ContainerDelete_Handler, + }, + { + MethodName: "ContainerImport", + Handler: _WWApi_ContainerImport_Handler, + }, + { + MethodName: "ContainerList", + Handler: _WWApi_ContainerList_Handler, + }, + { + MethodName: "ContainerShow", + Handler: _WWApi_ContainerShow_Handler, + }, + { + MethodName: "NodeAdd", + Handler: _WWApi_NodeAdd_Handler, + }, + { + MethodName: "NodeDelete", + Handler: _WWApi_NodeDelete_Handler, + }, + { + MethodName: "NodeList", + Handler: _WWApi_NodeList_Handler, + }, + { + MethodName: "NodeSet", + Handler: _WWApi_NodeSet_Handler, + }, + { + MethodName: "NodeStatus", + Handler: _WWApi_NodeStatus_Handler, + }, + { + MethodName: "Version", + Handler: _WWApi_Version_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "routes.proto", +} diff --git a/internal/pkg/api/util/util.go b/internal/pkg/api/util/util.go new file mode 100644 index 00000000..273b5594 --- /dev/null +++ b/internal/pkg/api/util/util.go @@ -0,0 +1,21 @@ +package util + +import ( + "github.com/manifoldco/promptui" +) + +// ConfirmationPrompt prompt is a blocking confirmation prompt. +// Returns true on y or yes user input. +func ConfirmationPrompt(label string) (yes bool) { + + prompt := promptui.Prompt{ + Label: label, + IsConfirm: true, + } + + result, _ := prompt.Run() + if result == "y" || result == "yes" { + yes = true + } + return +} \ No newline at end of file diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 6dd00bb1..c9e00a09 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -24,8 +24,8 @@ func init() { } } -func New() (nodeYaml, error) { - var ret nodeYaml +func New() (NodeYaml, error) { + var ret NodeYaml wwlog.Printf(wwlog.VERBOSE, "Opening node configuration file: %s\n", ConfigFile) data, err := ioutil.ReadFile(ConfigFile) @@ -49,7 +49,7 @@ Get all the nodes of a configuration. This function also merges the nodes with the given profiles and set the default values for every node */ -func (config *nodeYaml) FindAllNodes() ([]NodeInfo, error) { +func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) { var ret []NodeInfo wwconfig, err := warewulfconf.New() if err != nil { @@ -180,7 +180,14 @@ func (config *nodeYaml) FindAllNodes() ([]NodeInfo, error) { } n.NetDevs[devname].Tags[keyname].Set(key) } - + n.NetDevs[devname].Tags = make(map[string]*Entry) + for keyname, key := range netdev.Tags { + if _, ok := n.Tags[keyname]; !ok { + var keyVar Entry + n.NetDevs[devname].Tags[keyname] = &keyVar + } + n.NetDevs[devname].Tags[keyname].Set(key) + } } // Merge Keys into Tags for backwards compatibility @@ -302,7 +309,7 @@ func (config *nodeYaml) FindAllNodes() ([]NodeInfo, error) { return ret, nil } -func (config *nodeYaml) FindAllProfiles() ([]NodeInfo, error) { +func (config *NodeYaml) FindAllProfiles() ([]NodeInfo, error) { var ret []NodeInfo for name, profile := range config.NodeProfiles { @@ -434,7 +441,7 @@ func (config *nodeYaml) FindAllProfiles() ([]NodeInfo, error) { return ret, nil } -func (config *nodeYaml) FindDiscoverableNode() (NodeInfo, string, error) { +func (config *NodeYaml) FindDiscoverableNode() (NodeInfo, string, error) { var ret NodeInfo nodes, _ := config.FindAllNodes() diff --git a/internal/pkg/node/datastructure.go b/internal/pkg/node/datastructure.go index 62408a86..4e818b8f 100644 --- a/internal/pkg/node/datastructure.go +++ b/internal/pkg/node/datastructure.go @@ -9,7 +9,7 @@ import ( * YAML data representations ******/ -type nodeYaml struct { +type NodeYaml struct { WWInternal int `yaml:"WW_INTERNAL"` NodeProfiles map[string]*NodeConf Nodes map[string]*NodeConf diff --git a/internal/pkg/node/modifiers.go b/internal/pkg/node/modifiers.go index 5cccd193..7e95c193 100644 --- a/internal/pkg/node/modifiers.go +++ b/internal/pkg/node/modifiers.go @@ -15,7 +15,7 @@ import ( * ****/ -func (config *nodeYaml) AddNode(nodeID string) (NodeInfo, error) { +func (config *NodeYaml) AddNode(nodeID string) (NodeInfo, error) { var node NodeConf var n NodeInfo @@ -37,7 +37,7 @@ func (config *nodeYaml) AddNode(nodeID string) (NodeInfo, error) { return n, nil } -func (config *nodeYaml) DelNode(nodeID string) error { +func (config *NodeYaml) DelNode(nodeID string) error { if _, ok := config.Nodes[nodeID]; !ok { return errors.New("Nodename does not exist: " + nodeID) @@ -49,7 +49,7 @@ func (config *nodeYaml) DelNode(nodeID string) error { return nil } -func (config *nodeYaml) NodeUpdate(node NodeInfo) error { +func (config *NodeYaml) NodeUpdate(node NodeInfo) error { nodeID := node.Id.Get() if _, ok := config.Nodes[nodeID]; !ok { @@ -126,7 +126,7 @@ func (config *nodeYaml) NodeUpdate(node NodeInfo) error { * ****/ -func (config *nodeYaml) AddProfile(profileID string) (NodeInfo, error) { +func (config *NodeYaml) AddProfile(profileID string) (NodeInfo, error) { var node NodeConf var n NodeInfo @@ -143,7 +143,7 @@ func (config *nodeYaml) AddProfile(profileID string) (NodeInfo, error) { return n, nil } -func (config *nodeYaml) DelProfile(profileID string) error { +func (config *NodeYaml) DelProfile(profileID string) error { if _, ok := config.NodeProfiles[profileID]; !ok { return errors.New("Profile does not exist: " + profileID) @@ -158,7 +158,7 @@ func (config *nodeYaml) DelProfile(profileID string) error { /* Update the the config for the given profile so that it can unmarshalled. */ -func (config *nodeYaml) ProfileUpdate(profile NodeInfo) error { +func (config *NodeYaml) ProfileUpdate(profile NodeInfo) error { profileID := profile.Id.Get() if _, ok := config.NodeProfiles[profileID]; !ok { @@ -228,7 +228,7 @@ func (config *nodeYaml) ProfileUpdate(profile NodeInfo) error { * ****/ -func (config *nodeYaml) Persist() error { +func (config *NodeYaml) Persist() error { out, err := yaml.Marshal(config) if err != nil { diff --git a/internal/pkg/node/node_test.go b/internal/pkg/node/node_test.go index 3a3943e5..988b4144 100644 --- a/internal/pkg/node/node_test.go +++ b/internal/pkg/node/node_test.go @@ -25,7 +25,7 @@ nodes: hwaddr: 08:00:27:39:46:70 ipaddr: 10.0.8.150 ` - var nodeYaml nodeYaml + var nodeYaml NodeYaml err := yaml.Unmarshal([]byte(nodeConfig), &nodeYaml) assert.NoError(t, err) diff --git a/internal/pkg/node/util.go b/internal/pkg/node/util.go index 4b8bb9b3..c641376c 100644 --- a/internal/pkg/node/util.go +++ b/internal/pkg/node/util.go @@ -6,7 +6,7 @@ import ( "strings" ) -func (config *nodeYaml) FindByHwaddr(hwa string) (NodeInfo, error) { +func (config *NodeYaml) FindByHwaddr(hwa string) (NodeInfo, error) { if _, err := net.ParseMAC(hwa); err != nil { return NodeInfo{}, errors.New("invalid hardware address: " + hwa) } @@ -26,7 +26,7 @@ func (config *nodeYaml) FindByHwaddr(hwa string) (NodeInfo, error) { return ret, errors.New("No nodes found with HW Addr: " + hwa) } -func (config *nodeYaml) FindByIpaddr(ipaddr string) (NodeInfo, error) { +func (config *NodeYaml) FindByIpaddr(ipaddr string) (NodeInfo, error) { if net.ParseIP(ipaddr) == nil { return NodeInfo{}, errors.New("invalid IP:" + ipaddr) } diff --git a/internal/pkg/node/util_test.go b/internal/pkg/node/util_test.go index c6d82623..fd8c8323 100644 --- a/internal/pkg/node/util_test.go +++ b/internal/pkg/node/util_test.go @@ -6,7 +6,7 @@ import ( "gopkg.in/yaml.v2" ) -func NewTestNode() (nodeYaml, error) { +func NewTestNode() (NodeYaml, error) { var data = ` nodeprofiles: default: @@ -39,7 +39,7 @@ nodes: default: false ipaddr: fd1a:2b3c:4d5e:06f0:1234:5678:90ab:cdef ` - var ret nodeYaml + var ret NodeYaml err := yaml.Unmarshal([]byte(data), &ret) if err != nil { return ret, err @@ -59,7 +59,7 @@ func Test_nodeYaml_FindByHwaddr(t *testing.T) { tests := []struct { name string //fields fields - config nodeYaml + config NodeYaml args args want string wantErr bool @@ -95,7 +95,7 @@ func Test_nodeYaml_FindByIpaddr(t *testing.T) { } tests := []struct { name string - config nodeYaml + config NodeYaml args args want string wantErr bool diff --git a/internal/pkg/version/version.go b/internal/pkg/version/version.go index 97dfd290..f77050f4 100644 --- a/internal/pkg/version/version.go +++ b/internal/pkg/version/version.go @@ -3,9 +3,24 @@ package version import ( "fmt" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" "github.com/hpcng/warewulf/internal/pkg/buildconfig" ) +/* +Return the version of wwctl +*/ func GetVersion() string { return fmt.Sprintf("%s-%s", buildconfig.VERSION(), buildconfig.RELEASE()) } + +/* +Returns the version of the api via grpc +*/ +func Version() (versionResponse *wwapiv1.VersionResponse) { + versionResponse = &wwapiv1.VersionResponse{} + versionResponse.ApiPrefix = "rc1" + versionResponse.ApiVersion = "1" + versionResponse.WarewulfVersion = GetVersion() + return +} diff --git a/tools.go b/tools.go new file mode 100644 index 00000000..519c21df --- /dev/null +++ b/tools.go @@ -0,0 +1,10 @@ +// +build tools + +package tools + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway" + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2" + _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" + _ "google.golang.org/protobuf/cmd/protoc-gen-go" +)