From 27563db856a05c0cecb9f14c0b09f0cfe1b13f15 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Tue, 23 May 2023 16:48:13 +0200 Subject: [PATCH 01/75] added genders examples --- CHANGELOG.md | 1 + etc/examples/genders.ww | 10 ++++++++++ userdocs/contents/example-templates.rst | 25 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100755 etc/examples/genders.ww create mode 100644 userdocs/contents/example-templates.rst diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c68becc..70449bbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added experimental dnsmasq support. - Check for formal correct IP and MAC addresses for command line options and when reading in the configurations +- Added template to create genders database ## [4.4.0] 2023-01-18 diff --git a/etc/examples/genders.ww b/etc/examples/genders.ww new file mode 100755 index 00000000..6866c108 --- /dev/null +++ b/etc/examples/genders.ww @@ -0,0 +1,10 @@ +# This file is autogenerated by warewulf +# Host: {{.BuildHost}} +# Time: {{.BuildTime}} +# Source: {{.BuildSource}} +{{- range $node := $.AllNodes}} {{/* for each node */}} +{{ $node.Id.Get }} {{ range $i,$profile := $node.Profiles.GetSlice }} +{{- if $i }},{{end}} +{{- $profile }}{{ end }} +{{- range $key,$value := $node.Tags }},{{$value.Get}}{{ end }} +{{- end }} diff --git a/userdocs/contents/example-templates.rst b/userdocs/contents/example-templates.rst new file mode 100644 index 00000000..b04f176b --- /dev/null +++ b/userdocs/contents/example-templates.rst @@ -0,0 +1,25 @@ +================ +Useful templates +================ + +The examples directory contains some useful examples for day to day cluster admninstration. + +Genders +======= + +The file `genders.ww` can be placed as `/etc/genders.ww` which will create a genders database which containts all nodes with their profile as key. Also for every user defined 'key' the 'tah' will be added. + +.. Note: + +A arbitrary tag with a key can be added to a node with + +.. code-block:: bash + wwctl node set --tagadd key=value n01 + wwctl node set --tagadd key2='foo=baar' n01 + +will result in genders file with following line + + +.. code-block:: bash + n01: value,foo=baar + From d81dcdf65b9088c8157fabcb4d21040b65cc965b Mon Sep 17 00:00:00 2001 From: Yoshiaki Senda Date: Fri, 9 Jun 2023 00:39:16 +0900 Subject: [PATCH 02/75] add genconfig line * without `genconfig`, warewulfd doesn not read `/etc/warewulf/warewuld.conf`, and read `/usr/local/etc/warewulf/warewulf.conf` instead. * added `genconfig` line to make our build configuration identical to rpm package one This fix #847 --- userdocs/quickstart/el8.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/userdocs/quickstart/el8.rst b/userdocs/quickstart/el8.rst index acd0a848..8f1d770d 100644 --- a/userdocs/quickstart/el8.rst +++ b/userdocs/quickstart/el8.rst @@ -13,6 +13,22 @@ Install Warewulf and dependencies git clone https://github.com/hpcng/warewulf.git cd warewulf + make genconfig \ + PREFIX=/usr \ + BINDIR=/usr/bin \ + SYSCONFDIR=/etc \ + DATADIR=/usr/share \ + LOCALSTATEDIR=/var/lib \ + SHAREDSTATEDIR=/var/lib \ + MANDIR=/usr/share/man \ + INFODIR=/usr/share/info \ + DOCDIR=/usr/share/doc \ + SRVDIR=/var/lib \ + TFTPDIR=/var/lib/tftpboot \ + SYSTEMDDIR=/usr/lib/systemd/system \ + BASHCOMPDIR=/etc/bash_completion.d/ \ + FIREWALLDDIR=/usr/lib/firewalld/services \ + WWCLIENTDIR=/warewulf make all sudo make install From bc614c6eb319f91561e7d611d463b29a2a37e599 Mon Sep 17 00:00:00 2001 From: Yoshiaki Senda Date: Fri, 9 Jun 2023 00:41:50 +0900 Subject: [PATCH 03/75] Update CHANGELOG.md + Fix build configuration on Quickstart guide #847 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71dc96e8..a0dbae63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `--parents` option to `overlay import` subcommand to create necessary parent folder. #608 - Fix kernelargs are not printing properly in node list output. #828 +- Fix build configuration on Quickstart guide #847 ### Changed From c640b37ca03a18e8b476e5620e8fb5f6cfd9ac50 Mon Sep 17 00:00:00 2001 From: Yoshiaki Senda Date: Mon, 12 Jun 2023 22:28:20 +0900 Subject: [PATCH 04/75] Add EL9 quickstart guide to index.rst --- userdocs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/userdocs/index.rst b/userdocs/index.rst index a6f342d1..b4a1b5c1 100644 --- a/userdocs/index.rst +++ b/userdocs/index.rst @@ -32,6 +32,7 @@ Welcome to the Warewulf User Guide! EL7 (CentOS and RHEL) EL8 (Rocky Linux and RHEL) + EL9 (Rocky Linux and RHEL) openSUSE Leap and SLES 15 .. toctree:: From 104a05fc6c404aa46e4c14a9816fbfdecef5219f Mon Sep 17 00:00:00 2001 From: Yoshiaki Senda Date: Mon, 12 Jun 2023 22:30:29 +0900 Subject: [PATCH 05/75] Update CHANGELOG.md Add EL9 quickstart guide to index.rst --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdbf9943..2e78a8cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 parent folder. #608 - Fix kernelargs are not printing properly in node list output. #828 - Add Quickstart guide for EL9 +- Add EL9 Quickstart guide to index.rst ### Changed From 87fd3a55620d41408daa8e10c41356c20abf3bca Mon Sep 17 00:00:00 2001 From: Yoshiaki Senda Date: Thu, 15 Jun 2023 14:33:28 +0900 Subject: [PATCH 06/75] Add development-environment-vagrant Add development-environment-vagrant to index.rst --- userdocs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/userdocs/index.rst b/userdocs/index.rst index b4a1b5c1..7b651a90 100644 --- a/userdocs/index.rst +++ b/userdocs/index.rst @@ -42,6 +42,7 @@ Welcome to the Warewulf User Guide! Contributing Debugging Documentation + Development Environment (Vagrant) Development Environment (KVM) Development Environment (VirtualBox) Glossary From d9fe4b5e6346adddb861b9bc6950dc8501e75054 Mon Sep 17 00:00:00 2001 From: Yoshiaki Senda Date: Thu, 15 Jun 2023 15:44:57 +0900 Subject: [PATCH 07/75] Add New documentation for development environment (Vagrant) --- CHANGELOG.md | 1 + .../development-environment-vagrant.rst | 272 ++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 userdocs/contributing/development-environment-vagrant.rst diff --git a/CHANGELOG.md b/CHANGELOG.md index 592b1137..06c5cb06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - New documentation for the hostlist syntax. #611 +- New documentation for development environment (Vagrant) ### Fixed diff --git a/userdocs/contributing/development-environment-vagrant.rst b/userdocs/contributing/development-environment-vagrant.rst new file mode 100644 index 00000000..ca1b77c4 --- /dev/null +++ b/userdocs/contributing/development-environment-vagrant.rst @@ -0,0 +1,272 @@ +============================= +Development Environment (Vagrant) +============================= + +Create Rocky Linux 9 virtual machine for Warewulf testbed using Vagrant + +Host system requirements +===================== + +#. CPU supports H/W virtualization. +#. KVM kernel module available and loaded. + + +CPU H/W Virtualization support +-------------------------------- + +.. code-block:: bash + + lscpu | grep Virtualization + Virtualization: AMD-V + Virtualization type: full + + + +KVM kernel module +--------------------- + +.. code-block:: bash + + lsmode | grep kvm + ccp 118784 1 kvm_amd + kvm 1105920 1 kvm_amd + irqbypass 16384 1 kvm + + +Setup development environment on Rocky Linux 9 +============================================================== + +Install QEMU, libvirt +----------------------- + +.. code-block:: bash + + # Install packages + sudo dnf install -y libvirt qemu-kvm \ + libguestfs virtio-win guestfs-tools libguestfs-inspect-icons virt-win-reg \ + virt-install virt-top + + # Enable and start libvirtd + sudo systemctl enable --now libvirtd + + # Add user to libvirt group + $ sudo usermod -aG libvirt rocky + +Install Cockpit +----------------- + +.. code-block:: bash + + # Install packages + sudo dnf install -y cockpit cockpit-machines + + # Enable and start cockpit (http://localhost:9090) + sudo systemctl enable --now cockpit.socket + +Install Vagrant, vagrant-libvirt plug-in and vagrant-reload plug-in +--------------------------------------------------------------------- + +.. code-block:: bash + + sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo + sudo dnf install -y vagrant + + sudo dnf group install -y "Development tools" + sudo dnf config-manager --set-enabled crb + sudo dnf install -y libvirt-devel + + vagrant plugin install vagrant-libvirt + vagrant plugin install vagrant-reload + + +Vagrant box and Vagrantfile for Warewulf sandbox +=================================================== + +Create Rocky Linux 9.2 vagrant box +------------------------------------ + +.. code-block:: bash + + cat << 'EOF' > box-metadata.json + { + "name" : "rockylinux/9", + "description" : "Rocky Linux 9 2.0.0", + "versions" : [ + { + "version" : "2.0.0-20230513.0", + "providers" : [ + { + "name" : "libvirt", + "url" : "https://dl.rockylinux.org/pub/rocky/9.2/images/x86_64/Rocky-9-Vagrant-Libvirt-9.2-20230513.0.x86_64.box" + } + ] + } + ] + } + EOF + + $ vagrant box add box-metadata.json + +Vagrantfile +------------ + +.. code-block:: bash + + mkdir -p ~/warewulf-sandbox + cd ~/warewulf-sandbox + + cat << 'EOF' > Vagrantfile + Vagrant.configure("2") do |config| + number_of_node = ENV["NODES"] || 2 + branch = ENV["BRANCH"] || "v4.4.0" + + config.vm.define :head do |head| + head.vm.box = "rockylinux/9" + head.vm.box_version = "2.0.0-20230513.0" + head.vm.hostname = "warewulf" + + head.vm.network "private_network", + ip: "192.168.200.254", + netmask: "255.255.255.0", + libvirt__network_name: "pxe", + libvirt__dhcp_enabled: false + + head.vm.synced_folder ".", "/vagrant", type: "nfs", nfs_version: 4, nfs_udp: false + + head.vm.provider :libvirt do |libvirt| + libvirt.cpu_mode = "host-passthrough" + libvirt.memory = '8192' + libvirt.cpus = '2' + libvirt.machine_virtual_size = 40 + end + + head.vm.provision "shell", inline: <<-SHELL + dnf install -y cloud-utils-growpart + growpart /dev/vda 5 + xfs_growfs /dev/vda5 + SHELL + + head.vm.provision "shell", inline: <<-SHELL + dnf groupinstall -y "Development Tools" + dnf install -y epel-release + dnf config-manager --set-enabled crb + dnf install -y golang tftp-server dhcp-server nfs-utils gpgme-devel libassuan-devel + + cd /tmp + git clone https://github.com/hpcng/warewulf.git + cd warewulf + git checkout v4.4.0 + make genconfig \ + PREFIX=/usr \ + BINDIR=/usr/bin \ + SYSCONFDIR=/etc \ + DATADIR=/usr/share \ + LOCALSTATEDIR=/var/lib \ + SHAREDSTATEDIR=/var/lib \ + MANDIR=/usr/share/man \ + INFODIR=/usr/share/info \ + DOCDIR=/usr/share/doc \ + SRVDIR=/var/lib \ + TFTPDIR=/var/lib/tftpboot \ + SYSTEMDDIR=/usr/lib/systemd/system \ + BASHCOMPDIR=/etc/bash_completion.d/ \ + FIREWALLDDIR=/usr/lib/firewalld/services \ + WWCLIENTDIR=/warewulf + make all + make install + + systemctl disable --now firewalld + + sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config + SHELL + + head.vm.provision "reload" + + head.vm.provision "shell", inline: <<-SHELL + cat << 'CONF' | sudo tee /etc/warewulf/warewulf.conf + WW_INTERNAL: 43 + ipaddr: 192.168.200.254 + netmask: 255.255.255.0 + network: 192.168.200.0 + warewulf: + port: 9873 + secure: false + update interval: 60 + autobuild overlays: true + host overlay: true + syslog: false + dhcp: + enabled: true + range start: 192.168.200.50 + range end: 192.168.200.99 + systemd name: dhcpd + tftp: + enabled: true + systemd name: tftp + nfs: + enabled: true + export paths: + - path: /home + export options: rw,sync + mount options: defaults + mount: true + - path: /opt + export options: ro,sync,no_root_squash + mount options: defaults + mount: false + systemd name: nfs-server + CONF + + sed -i 's@ExecStart=/usr/bin/wwctl server start@ExecStart=/usr/bin/wwctl server start -d -v@' /usr/lib/systemd/system/warewulfd.service + systemctl enable --now warewulfd + + wwctl configure --all + + wwctl container import docker://ghcr.io/hpcng/warewulf-rockylinux:9 rocky-9 + wwctl profile set --yes --container rocky-9 "default" + wwctl profile set --yes --netdev eth1 --netmask 255.255.255.0 --gateway 192.168.200.254 "default" + + wwctl node add n0001.cluster -I 192.168.200.101 --discoverable true + wwctl node add n0002.cluster -I 192.168.200.102 --discoverable true + SHELL + end + + (1..number_of_node).each do |i| + config.vm.define :"n000#{i}", autostart: false do |node| + node.vm.hostname = "n000#{i}" + node.vm.network "private_network", + libvirt__network_name: "pxe" + + node.vm.provider :libvirt do |compute| + compute.cpu_mode = 'host-passthrough' + compute.memory = '8192' + compute.cpus = '2' + boot_network = {'network' => 'pxe'} + compute.boot boot_network + end + end + end + end + EOF + +Spin up head node +=================== + +.. code-block:: bash + + vagrant up + + +Spin up compute nodes +======================= + + + +.. code-block:: bash + + vagrant up n0001 + + # Wait until n0001 becomes ready + + vagrant up n0002 + From 960bdd6cbcb6838f4733df7d34bde2e821594fb6 Mon Sep 17 00:00:00 2001 From: Yoshiaki Senda Date: Thu, 15 Jun 2023 15:51:25 +0900 Subject: [PATCH 08/75] fix typo --- userdocs/contributing/development-environment-vagrant.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/userdocs/contributing/development-environment-vagrant.rst b/userdocs/contributing/development-environment-vagrant.rst index ca1b77c4..51a79da3 100644 --- a/userdocs/contributing/development-environment-vagrant.rst +++ b/userdocs/contributing/development-environment-vagrant.rst @@ -50,7 +50,7 @@ Install QEMU, libvirt sudo systemctl enable --now libvirtd # Add user to libvirt group - $ sudo usermod -aG libvirt rocky + sudo usermod -aG libvirt rocky Install Cockpit ----------------- @@ -105,7 +105,7 @@ Create Rocky Linux 9.2 vagrant box } EOF - $ vagrant box add box-metadata.json + vagrant box add box-metadata.json Vagrantfile ------------ From cf5658fed95cd4c283b3c8b661f54da7005d17e4 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Thu, 15 Jun 2023 08:54:17 +0200 Subject: [PATCH 09/75] fixed targets for doc build Signed-off-by: Christian Goll --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 1abd6869..f5c1dacc 100644 --- a/Makefile +++ b/Makefile @@ -216,12 +216,12 @@ init: cp -r tftpboot/* $(WWTFTPDIR)/ipxe/ restorecon -r $(WWTFTPDIR) -wwctl: $(WWCTL_DEPS) +wwctl: config vendor $(WWCTL_DEPS) @echo Building "$@" @cd cmd/wwctl; GOOS=linux go build -mod vendor -tags "$(WW_GO_BUILD_TAGS)" \ -o ../../wwctl -wwclient: $(WWCLIENT_DEPS) +wwclient: config vendor $(WWCLIENT_DEPS) @echo Building "$@" @cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags "-extldflags -static" \ -o ../../wwclient @@ -246,6 +246,7 @@ dist: vendor config rm -rf .dist reference: wwctl + mkdir -p userdocs/reference ./wwctl --emptyconf genconfig reference userdocs/reference/ latexpdf: reference From e5dcffca0fb4bceb39c0938fcf3c78ad8683c2bd Mon Sep 17 00:00:00 2001 From: Yoshiaki Senda Date: Thu, 15 Jun 2023 16:25:40 +0900 Subject: [PATCH 10/75] fix indentation error --- .../development-environment-vagrant.rst | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/userdocs/contributing/development-environment-vagrant.rst b/userdocs/contributing/development-environment-vagrant.rst index 51a79da3..3b7ad169 100644 --- a/userdocs/contributing/development-environment-vagrant.rst +++ b/userdocs/contributing/development-environment-vagrant.rst @@ -184,38 +184,38 @@ Vagrantfile head.vm.provision "shell", inline: <<-SHELL cat << 'CONF' | sudo tee /etc/warewulf/warewulf.conf - WW_INTERNAL: 43 - ipaddr: 192.168.200.254 - netmask: 255.255.255.0 - network: 192.168.200.0 - warewulf: - port: 9873 - secure: false - update interval: 60 - autobuild overlays: true - host overlay: true - syslog: false - dhcp: - enabled: true - range start: 192.168.200.50 - range end: 192.168.200.99 - systemd name: dhcpd - tftp: - enabled: true - systemd name: tftp - nfs: - enabled: true - export paths: - - path: /home - export options: rw,sync - mount options: defaults - mount: true - - path: /opt - export options: ro,sync,no_root_squash - mount options: defaults - mount: false - systemd name: nfs-server - CONF + WW_INTERNAL: 43 + ipaddr: 192.168.200.254 + netmask: 255.255.255.0 + network: 192.168.200.0 + warewulf: + port: 9873 + secure: false + update interval: 60 + autobuild overlays: true + host overlay: true + syslog: false + dhcp: + enabled: true + range start: 192.168.200.50 + range end: 192.168.200.99 + systemd name: dhcpd + tftp: + enabled: true + systemd name: tftp + nfs: + enabled: true + export paths: + - path: /home + export options: rw,sync + mount options: defaults + mount: true + - path: /opt + export options: ro,sync,no_root_squash + mount options: defaults + mount: false + systemd name: nfs-server + CONF sed -i 's@ExecStart=/usr/bin/wwctl server start@ExecStart=/usr/bin/wwctl server start -d -v@' /usr/lib/systemd/system/warewulfd.service systemctl enable --now warewulfd From 835d953c95fa2efa57f92390ab95aa23bf9f8870 Mon Sep 17 00:00:00 2001 From: Yoshiaki Senda Date: Thu, 15 Jun 2023 17:39:57 +0900 Subject: [PATCH 11/75] fix indentation --- .../development-environment-vagrant.rst | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/userdocs/contributing/development-environment-vagrant.rst b/userdocs/contributing/development-environment-vagrant.rst index 3b7ad169..85aae023 100644 --- a/userdocs/contributing/development-environment-vagrant.rst +++ b/userdocs/contributing/development-environment-vagrant.rst @@ -189,32 +189,32 @@ Vagrantfile netmask: 255.255.255.0 network: 192.168.200.0 warewulf: - port: 9873 - secure: false - update interval: 60 - autobuild overlays: true - host overlay: true - syslog: false + port: 9873 + secure: false + update interval: 60 + autobuild overlays: true + host overlay: true + syslog: false dhcp: - enabled: true - range start: 192.168.200.50 - range end: 192.168.200.99 - systemd name: dhcpd + enabled: true + range start: 192.168.200.50 + range end: 192.168.200.99 + systemd name: dhcpd tftp: - enabled: true - systemd name: tftp + enabled: true + systemd name: tftp nfs: - enabled: true - export paths: - - path: /home - export options: rw,sync - mount options: defaults - mount: true - - path: /opt - export options: ro,sync,no_root_squash - mount options: defaults - mount: false - systemd name: nfs-server + enabled: true + export paths: + - path: /home + export options: rw,sync + mount options: defaults + mount: true + - path: /opt + export options: ro,sync,no_root_squash + mount options: defaults + mount: false + systemd name: nfs-server CONF sed -i 's@ExecStart=/usr/bin/wwctl server start@ExecStart=/usr/bin/wwctl server start -d -v@' /usr/lib/systemd/system/warewulfd.service From 9e07c99df9116555033f701d6f603e6814fb3abb Mon Sep 17 00:00:00 2001 From: Yoshiaki Senda Date: Thu, 15 Jun 2023 18:34:04 +0900 Subject: [PATCH 12/75] reflect feedback --- userdocs/contributing/development-environment-vagrant.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/userdocs/contributing/development-environment-vagrant.rst b/userdocs/contributing/development-environment-vagrant.rst index 85aae023..152014b8 100644 --- a/userdocs/contributing/development-environment-vagrant.rst +++ b/userdocs/contributing/development-environment-vagrant.rst @@ -14,6 +14,8 @@ Host system requirements CPU H/W Virtualization support -------------------------------- +Check CPU virtualization capability using following command. If your system has Intel CPU, you will see Intel VT here, and if your system has AMD CPU, you will see AMD-V here. + .. code-block:: bash lscpu | grep Virtualization @@ -52,7 +54,7 @@ Install QEMU, libvirt # Add user to libvirt group sudo usermod -aG libvirt rocky -Install Cockpit +Install Cockpit (Optional) ----------------- .. code-block:: bash From 960062969e23d4bebb30b269c0a48f6819b5b2d6 Mon Sep 17 00:00:00 2001 From: Yoshiaki Senda Date: Thu, 15 Jun 2023 18:36:43 +0900 Subject: [PATCH 13/75] minor fix --- userdocs/contributing/development-environment-vagrant.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/userdocs/contributing/development-environment-vagrant.rst b/userdocs/contributing/development-environment-vagrant.rst index 152014b8..7f7f8a21 100644 --- a/userdocs/contributing/development-environment-vagrant.rst +++ b/userdocs/contributing/development-environment-vagrant.rst @@ -14,7 +14,7 @@ Host system requirements CPU H/W Virtualization support -------------------------------- -Check CPU virtualization capability using following command. If your system has Intel CPU, you will see Intel VT here, and if your system has AMD CPU, you will see AMD-V here. +Check CPU virtualization capability using following command. If your system has Intel CPU, you will see `Intel VT` here, and if your system has AMD CPU, you will see `AMD-V` here. .. code-block:: bash From 2f82e5c964c9db873c76276cc6b1dfccc6427e9a Mon Sep 17 00:00:00 2001 From: Yoshiaki Senda Date: Thu, 15 Jun 2023 18:37:40 +0900 Subject: [PATCH 14/75] fix inline code block --- userdocs/contributing/development-environment-vagrant.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/userdocs/contributing/development-environment-vagrant.rst b/userdocs/contributing/development-environment-vagrant.rst index 7f7f8a21..bff87329 100644 --- a/userdocs/contributing/development-environment-vagrant.rst +++ b/userdocs/contributing/development-environment-vagrant.rst @@ -14,7 +14,7 @@ Host system requirements CPU H/W Virtualization support -------------------------------- -Check CPU virtualization capability using following command. If your system has Intel CPU, you will see `Intel VT` here, and if your system has AMD CPU, you will see `AMD-V` here. +Check CPU virtualization capability using following command. If your system has Intel CPU, you will see :code:`Intel VT` here, and if your system has AMD CPU, you will see :code:`AMD-V` here. .. code-block:: bash From f1e1e8d90ab46a2bb4de9772f53ed9cfa60c35d5 Mon Sep 17 00:00:00 2001 From: James Cogo <127156142+jamescogo@users.noreply.github.com> Date: Thu, 22 Jun 2023 16:47:19 -0700 Subject: [PATCH 15/75] Update el9.rst No match for argument: gpgpme-devel Error: Unable to find a match: gpgpme-devel -Changed gpgpme-devel ==> gpgme-devel --- userdocs/quickstart/el9.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/userdocs/quickstart/el9.rst b/userdocs/quickstart/el9.rst index 02f1915f..575dc2b3 100644 --- a/userdocs/quickstart/el9.rst +++ b/userdocs/quickstart/el9.rst @@ -10,7 +10,7 @@ Install Warewulf and dependencies sudo dnf groupinstall "Development Tools" sudo dnf install epel-release sudo dnf config-manager --set-enabled crb - sudo dnf install golang tftp-server dhcp-server nfs-utils gpgpme-devel libassuan-devel + sudo dnf install golang tftp-server dhcp-server nfs-utils gpgme-devel libassuan-devel git clone https://github.com/hpcng/warewulf.git cd warewulf From ba3939345c72a93327754d3656737fb10cc52e52 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Mon, 26 Jun 2023 15:59:45 +0200 Subject: [PATCH 16/75] Fix lsmod wrong spelling --- userdocs/contributing/development-environment-vagrant.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/userdocs/contributing/development-environment-vagrant.rst b/userdocs/contributing/development-environment-vagrant.rst index bff87329..8b3f0ba4 100644 --- a/userdocs/contributing/development-environment-vagrant.rst +++ b/userdocs/contributing/development-environment-vagrant.rst @@ -29,7 +29,7 @@ KVM kernel module .. code-block:: bash - lsmode | grep kvm + lsmod | grep kvm ccp 118784 1 kvm_amd kvm 1105920 1 kvm_amd irqbypass 16384 1 kvm From 2cf928d6b7d134dc78f64aa1b49d36148808a91d Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Tue, 27 Jun 2023 14:08:57 +0200 Subject: [PATCH 17/75] reboot also without systemd Signed-off-by: Christian Goll --- CHANGELOG.md | 2 ++ overlays/wwinit/init | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06c5cb06..81c84b6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 when reading in the configurations - Write log messages to stderr rather than stdout. #768 +- realy reboot also without systemd + ## [4.4.0] 2023-01-18 ### Added diff --git a/overlays/wwinit/init b/overlays/wwinit/init index a6999991..af754f94 100755 --- a/overlays/wwinit/init +++ b/overlays/wwinit/init @@ -11,7 +11,7 @@ if test -f "/warewulf/config"; then else echo "ERROR: Warewulf configuration file not found... rebooting in 1 minute" sleep 60 - /sbin/reboot + echo b > /proc/sysrq-trigger || /sbin/reboot fi echo "Warewulf v4 is now booting: $WWHOSTNAME" @@ -53,4 +53,4 @@ fi echo echo "There was a problem with the provisioning process, rebooting in 1 minute..." sleep 60 -/sbin/reboot \ No newline at end of file +echo b > /proc/sysrq-trigger || /sbin/reboot From 9934f2a1b179bd0466f7d3cc55d844eeda502ecb Mon Sep 17 00:00:00 2001 From: Arnaud Lecomte Date: Wed, 28 Jun 2023 11:01:10 +0200 Subject: [PATCH 18/75] Addind information related to node status checkup during provisiong process --- userdocs/contents/provisioning.rst | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/userdocs/contents/provisioning.rst b/userdocs/contents/provisioning.rst index 8c69f620..5a451669 100644 --- a/userdocs/contents/provisioning.rst +++ b/userdocs/contents/provisioning.rst @@ -70,3 +70,30 @@ occur: #. Container: #. The container now boots exactly as any operating system would expect + + +Node status +======================== +During the whole provisioning process of your nodes, you can check their status +through the following command : +.. code-block:: console + + # wwctl node status + NODENAME STAGE SENT LASTSEEN (s) + ================================================================================ + c001 RUNTIME_OVERLAY generic.img.gz 16 + + +For each node, there is 4 different stages : +* **IPXE** +* **KERNEL** +* **SYSTEM_OVERLAY** +* **RUNTIME_OVERLAY** + +Those stages are directly linked to the The Provisioning Process section and are provided +by the wwclient + +Thanks to ``wwctl node status`` command, you can also check your communication between +the warewulf client and server. +Depending on your warewulf version, you should see a reset of the last seen counter every 1 minute due to the +warewulf runtime overlay update. \ No newline at end of file From 22df58eab474fa947fd7378a6a3a9b7d4fd034be Mon Sep 17 00:00:00 2001 From: John Hanks Date: Tue, 25 Jul 2023 18:53:14 -0500 Subject: [PATCH 19/75] More aggressive cleaning based on contents of .gitignore. --- Makefile | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index f5c1dacc..2bfb9f10 100644 --- a/Makefile +++ b/Makefile @@ -281,17 +281,36 @@ wwapird: ## Build the rest api server (revese proxy to the grpc api server). go build -o ./wwapird internal/app/api/wwapird/wwapird.go contclean: + rm -f $(WAREWULF)-$(VERSION).tar.gz + rm -f bash_completion + rm -f config + rm -f config_defaults + rm -f Defaults.mk + rm -f etc/wwapi{c,d,rd}.conf + rm -f etc/wwapi{c,d,rd}.config + rm -f include/systemd/warewulfd.service + rm -f internal/pkg/buildconfig/setconfigs.go + rm -f internal/pkg/config/buildconfig.go + rm -f man_page + rm -f print_defaults + rm -f update_configuration + rm -f usr/share/man/man1/ + rm -f warewulf.spec + rm -f warewulf-*.tar.gz + rm -f wwapic + rm -f wwapid + rm -f wwapird rm -f wwclient rm -f wwctl - rm -rf .dist - rm -f $(WAREWULF)-$(VERSION).tar.gz - rm -rf man_pages - rm -f warewulf.spec - rm -f config - rm -f Defaults.mk rm -rf $(TOOLS_DIR) - rm -f update_configuration - rm -f etc/wwapi{c,d,rd}.conf + rm -rf bash_completion.d + rm -rf /config + rm -rf .dist/ + rm -rf _dist/ + rm -rf etc/bash_completion.d/ + rm -rf man_pages + rm -rf userdocs/_* + rm -rf userdocs/reference/* clean: contclean rm -rf vendor From 6a53b9aa19011113840c51efaa27795a58d16d60 Mon Sep 17 00:00:00 2001 From: John Hanks Date: Tue, 25 Jul 2023 18:54:06 -0500 Subject: [PATCH 20/75] Replace deprecated io.ioutil functions with new os versions. --- internal/app/wwctl/overlay/imprt/main_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/app/wwctl/overlay/imprt/main_test.go b/internal/app/wwctl/overlay/imprt/main_test.go index e74c84b5..bbed5a63 100644 --- a/internal/app/wwctl/overlay/imprt/main_test.go +++ b/internal/app/wwctl/overlay/imprt/main_test.go @@ -2,7 +2,6 @@ package imprt import ( "fmt" - "io/ioutil" "os" "testing" @@ -13,7 +12,7 @@ import ( ) func Test_List(t *testing.T) { - tmpdir, err := ioutil.TempDir(os.TempDir(), "warewulf") + tmpdir, err := os.MkdirTemp(os.TempDir(), "warewulf") if err != nil { t.Errorf("Could not create temp folder: %v", err) t.FailNow() @@ -34,7 +33,7 @@ func Test_List(t *testing.T) { t.FailNow() } - file, err := ioutil.TempFile(tmpdir, "file") + file, err := os.CreateTemp(tmpdir, "file") if err != nil { t.Errorf("Could not create tempfile") t.FailNow() From 082eddc0f70914e77dbbf06cd4418ff8de7f5a0e Mon Sep 17 00:00:00 2001 From: griznog Date: Wed, 26 Jul 2023 07:22:43 -0500 Subject: [PATCH 21/75] Update CHANGELOG. --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06c5cb06..3df7afb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New documentation for development environment (Vagrant) ### Fixed - +- More aggressive `make clean`. +- Replace deprecated `io.utils` functions with new `os` functions. - The correct header is now displayed when `-al` flags are specified to overlay list. - Added a missing `.ww` extension to the `70-ww4-netname.rules` template in the From e05438fdc03df88346bcaf9cf7fd1a615c4c433a Mon Sep 17 00:00:00 2001 From: griznog Date: Wed, 26 Jul 2023 11:46:13 -0500 Subject: [PATCH 22/75] Use warewulf.conf from build instead of installed warewulf.conf. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2bfb9f10..53316639 100644 --- a/Makefile +++ b/Makefile @@ -203,7 +203,7 @@ files: all 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) - ./wwctl genconfig completions > $(DESTDIR)$(BASHCOMPDIR)/wwctl + ./wwctl --warewulfconf etc/warewulf.conf genconfig completions > $(DESTDIR)$(BASHCOMPDIR)/wwctl cp man_pages/*.1* $(DESTDIR)$(MANDIR)/man1/ cp man_pages/*.5* $(DESTDIR)$(MANDIR)/man5/ install -m 0644 staticfiles/README-ipxe.md $(DESTDIR)$(WWDATADIR)/ipxe From 13c426de1c39b24bd4b3a202f67328783d89fa1a Mon Sep 17 00:00:00 2001 From: griznog Date: Thu, 27 Jul 2023 07:27:20 -0500 Subject: [PATCH 23/75] Add an argument to the runtime/system overlay related functions to carry the context to getOverlayFile() for building the img name. --- internal/pkg/overlay/config.go | 24 ++++++++++++++++++++++-- internal/pkg/overlay/overlay.go | 25 +++++++++++++++++-------- internal/pkg/warewulfd/provision.go | 8 +++++++- internal/pkg/warewulfd/util.go | 5 +++-- 4 files changed, 49 insertions(+), 13 deletions(-) diff --git a/internal/pkg/overlay/config.go b/internal/pkg/overlay/config.go index d2a9e924..8cc09d95 100644 --- a/internal/pkg/overlay/config.go +++ b/internal/pkg/overlay/config.go @@ -5,6 +5,7 @@ import ( "path" "strings" + "github.com/hpcng/warewulf/internal/pkg/wwlog" warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" ) @@ -30,7 +31,26 @@ func OverlaySourceDir(overlayName string) string { /* Returns the overlay name of the image for a given node */ -func OverlayImage(nodeName string, overlayName []string) string { +func OverlayImage(nodeName string, overlayName []string, img_context ...string) string { + var name string + var context string + + /* Check optional context argument. If missing, default to legacy. */ + if len(img_context) == 0 { + context = "legacy" + } else { + context = img_context[0] + } + conf := warewulfconf.Get() - return path.Join(conf.Paths.WWProvisiondir, "overlays/", nodeName, strings.Join(overlayName, "-")+".img") + + switch context { + case "legacy": + name = strings.Join(overlayName, "-")+".img" + default: + wwlog.Warn("Context %s passed to OverlayImage(), using %s to build image name.", context, "__" + strings.ToUpper(context) + "__") + name = "__" + strings.ToUpper(context) + "__.img" + } + + return path.Join(conf.Paths.WWProvisiondir, "overlays/", nodeName, name) } diff --git a/internal/pkg/overlay/overlay.go b/internal/pkg/overlay/overlay.go index 0d441cc7..ec505c28 100644 --- a/internal/pkg/overlay/overlay.go +++ b/internal/pkg/overlay/overlay.go @@ -47,13 +47,13 @@ func BuildAllOverlays(nodes []node.NodeInfo) error { sysOverlays := n.SystemOverlay.GetSlice() wwlog.Info("Building system overlays for %s: [%s]", n.Id.Get(), strings.Join(sysOverlays, ", ")) - err := BuildOverlay(n, sysOverlays) + err := BuildOverlay(n, sysOverlays, "system") if err != nil { return errors.Wrapf(err, "could not build system overlays %v for node %s", sysOverlays, n.Id.Get()) } runOverlays := n.RuntimeOverlay.GetSlice() wwlog.Info("Building runtime overlays for %s: [%s]", n.Id.Get(), strings.Join(runOverlays, ", ")) - err = BuildOverlay(n, runOverlays) + err = BuildOverlay(n, runOverlays, "runtime") if err != nil { return errors.Wrapf(err, "could not build runtime overlays %v for node %s", runOverlays, n.Id.Get()) } @@ -66,11 +66,12 @@ func BuildAllOverlays(nodes []node.NodeInfo) error { func BuildSpecificOverlays(nodes []node.NodeInfo, overlayNames []string) error { for _, n := range nodes { - wwlog.Info("Building overlay for %s: %v", n.Id.Get(), overlayNames) - err := BuildOverlay(n, overlayNames) - if err != nil { - return errors.Wrapf(err, "could not build overlay for node %s: %v", n.Id.Get(), overlayNames) + for _, overlayName := range overlayNames { + err := BuildOverlay(n, []string{overlayName}) + if err != nil { + return errors.Wrapf(err, "could not build overlay %s for node %s", overlayName, n.Id.Get()) + } } } @@ -139,10 +140,18 @@ func OverlayInit(overlayName string) error { /* Build the given overlays for a node and create a Image for them */ -func BuildOverlay(nodeInfo node.NodeInfo, overlayNames []string) error { +func BuildOverlay(nodeInfo node.NodeInfo, overlayNames []string, img_context ...string) error { + var context string + /* Check optional context argument. If missing, default to legacy. */ + if len(img_context) == 0 { + context = "legacy" + } else { + context = img_context[0] + } + // create the dir where the overlay images will reside name := fmt.Sprintf("overlay %s/%v", nodeInfo.Id.Get(), overlayNames) - overlayImage := OverlayImage(nodeInfo.Id.Get(), overlayNames) + overlayImage := OverlayImage(nodeInfo.Id.Get(), overlayNames, context) overlayImageDir := path.Dir(overlayImage) err := os.MkdirAll(overlayImageDir, 0755) diff --git a/internal/pkg/warewulfd/provision.go b/internal/pkg/warewulfd/provision.go index 60457208..d83fd70d 100644 --- a/internal/pkg/warewulfd/provision.go +++ b/internal/pkg/warewulfd/provision.go @@ -59,6 +59,8 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { status_stage := status_stages[rinfo.stage] var stage_overlays []string var stage_file string = "" + var img_context string = "legacy" /* Default to old image name behavior */ + // TODO: when module version is upgraded to go1.18, should be 'any' type var tmpl_data interface{} @@ -128,6 +130,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { } else if rinfo.stage == "system" { if len(node.SystemOverlay.GetSlice()) != 0 { stage_overlays = node.SystemOverlay.GetSlice() + img_context = rinfo.stage } else { wwlog.Warn("No system overlay set for node %s", node.Id.Get()) } @@ -135,8 +138,10 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { } else if rinfo.stage == "runtime" { if rinfo.overlay != "" { stage_overlays = []string{rinfo.overlay} + img_context = "legacy" } else if len(node.RuntimeOverlay.GetSlice()) != 0 { stage_overlays = node.RuntimeOverlay.GetSlice() + img_context = rinfo.stage } else { wwlog.Warn("No runtime overlay set for node %s", node.Id.Get()) } @@ -147,7 +152,8 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { stage_file, err = getOverlayFile( node.Id.Get(), stage_overlays, - conf.Warewulf.AutobuildOverlays) + conf.Warewulf.AutobuildOverlays, + img_context ) if err != nil { w.WriteHeader(http.StatusInternalServerError) diff --git a/internal/pkg/warewulfd/util.go b/internal/pkg/warewulfd/util.go index 6b2275fe..4e497c6f 100644 --- a/internal/pkg/warewulfd/util.go +++ b/internal/pkg/warewulfd/util.go @@ -44,9 +44,10 @@ func sendFile( func getOverlayFile( nodeId string, stage_overlays []string, - autobuild bool ) (stage_file string, err error) { + autobuild bool, + img_context string) (stage_file string, err error) { - stage_file = overlay.OverlayImage(nodeId, stage_overlays) + stage_file = overlay.OverlayImage(nodeId, stage_overlays, img_context) err = nil build := !util.IsFile(stage_file) From 92f94a6630ce3abeaa9af0a3b4f02ed27af41cbe Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Tue, 8 Aug 2023 09:55:55 -0600 Subject: [PATCH 24/75] Fix duplicate entries in rpm %files The inclusion of %{_sysconfdir}/warewulf/* and %{_sysconfdir}/warewulf/nodes.conf caused nodes.conf to be listed twice, generating a warning. The inclusion of %{srvdir}/warewulf, which is redundant with %{_sharedstatedir}/warewulf, caused all files in %{_sharedstatedir}/warewulf/overlays to be listed twice, generating a warning. Signed-off-by: Jonathon Anderson --- warewulf.spec.in | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/warewulf.spec.in b/warewulf.spec.in index fd0c32c0..e0f06208 100644 --- a/warewulf.spec.in +++ b/warewulf.spec.in @@ -116,14 +116,16 @@ getent group %{wwgroup} >/dev/null || groupadd -r %{wwgroup} %files %defattr(-, root, %{wwgroup}) %dir %{_sysconfdir}/warewulf -%config(noreplace) %{_sysconfdir}/warewulf/* -%config(noreplace) %attr(0640,-,-) %{_sysconfdir}/warewulf/nodes.conf +%config(noreplace) %{_sysconfdir}/warewulf/warewulf.conf +%config(noreplace) %{_sysconfdir}/warewulf/wwapi*.conf +%config(noreplace) %{_sysconfdir}/warewulf/examples +%config(noreplace) %{_sysconfdir}/warewulf/ipxe +%attr(0640,-,-) %{_sysconfdir}/warewulf/nodes.conf %{_sysconfdir}/bash_completion.d/wwctl %dir %{_sharedstatedir}/warewulf %{_sharedstatedir}/warewulf/chroots %{_sharedstatedir}/warewulf/overlays -%{srvdir}/warewulf %attr(-, root, root) %{_bindir}/wwctl %attr(-, root, root) %{_bindir}/wwapi* From 7b2f278f94719ed8d3143623a0a97482d4e264f7 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Fri, 26 May 2023 17:23:41 -0600 Subject: [PATCH 25/75] Refactor and fix bugs in syncuids Closes #840 * SyncUids can now return applicable errors even during showOnly, so updated ContainerImport to ignore errors during showOnly. * Fixed handling of file gids during SyncUids Signed-off-by: Jonathon Anderson --- CHANGELOG.md | 1 + internal/pkg/api/container/container.go | 9 +- internal/pkg/container/syncuids.go | 581 +++++++++++++++--------- internal/pkg/container/syncuids_test.go | 283 ++++++++++++ 4 files changed, 660 insertions(+), 214 deletions(-) create mode 100644 internal/pkg/container/syncuids_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 3df7afb8..5596f867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix build configuration on Quickstart guide #847 - Add Quickstart guide for EL9 - Add EL9 Quickstart guide to index.rst +- Container file gids are now updated properly during syncuser. #840 ### Changed diff --git a/internal/pkg/api/container/container.go b/internal/pkg/api/container/container.go index 5ae4347a..6c427664 100644 --- a/internal/pkg/api/container/container.go +++ b/internal/pkg/api/container/container.go @@ -203,11 +203,14 @@ func ContainerImport(cip *wwapiv1.ContainerImportParameter) (containerName strin return } - err = container.SyncUids(cip.Name, !cip.SyncUser) - if err != nil && !cip.SyncUser { + SyncUserShowOnly := !cip.SyncUser + err = container.SyncUids(cip.Name, SyncUserShowOnly) + if err != nil { err = fmt.Errorf("error in user sync, fix error and run 'syncuser' manually: %s", err) wwlog.Error(err.Error()) - return + if cip.SyncUser { + return + } } wwlog.Info("Building container: %s", cip.Name) diff --git a/internal/pkg/container/syncuids.go b/internal/pkg/container/syncuids.go index 62cd8b27..a4bac584 100644 --- a/internal/pkg/container/syncuids.go +++ b/internal/pkg/container/syncuids.go @@ -16,243 +16,402 @@ import ( "github.com/pkg/errors" ) -type completeUserInfo struct { - Name string - UidHost int `access:"r,w"` - GidHost int `access:"r,w"` - UidCont int `access:"r,w"` - GidCont int `access:"r,w"` - FileListUid []string `access:"r,w"` - FileListGid []string `access:"r,w"` -} -type simpleUserInfo struct { - name string - uid int - gid int -} +const passwdPath = "/etc/passwd" +const groupPath = "/etc/group" -/* -sync the uids,gids from the host to the container -*/ + +// SyncUids updates the /etc/passwd and /etc/group files in the +// container identified by containerName by installing the equivalent +// files from the host and appending names only in the +// container. Files in the container are updated to match the new +// numeric id assignments. +// +// If showOnly is true, the container is not actually updated, but +// relevant log entries and output are generated. +// +// Any I/O errors are returned unmodified. +// +// A conflict arises if the container has an entry with the same id as +// an entry in the host and the host does not have an entry with the +// same name. In this case, an error is returned. func SyncUids(containerName string, showOnly bool) error { - var userDb []completeUserInfo - passwdName := "/etc/passwd" - groupName := "/etc/group" + containerPath := RootFsDir(containerName) + containerPasswdPath := path.Join(containerPath, passwdPath) + containerGroupPath := path.Join(containerPath, groupPath) - // populate db with users from the host - hostUsers, err := createPasswdMap(passwdName) - if err != nil { - wwlog.Error("Could not open "+passwdName) - return err - } - for _, hostUser := range hostUsers { - userDb = append(userDb, completeUserInfo{Name: hostUser.name, - UidHost: hostUser.uid, GidHost: hostUser.gid, UidCont: -1, GidCont: -1}) - } + passwdSync := make(syncDB) + if err := passwdSync.readFromHost(passwdPath); err != nil { return err } + if err := passwdSync.readFromContainer(containerPasswdPath); err != nil { return err } + if err := passwdSync.checkConflicts(); err != nil { return err } + if err := passwdSync.findUserFiles(containerPath); err != nil { return err } - // merge container users into db and track users that are only in the - // container - fullPath := RootFsDir(containerName) - containerUsers, err := createPasswdMap(path.Join(fullPath, passwdName)) - if err != nil { - wwlog.Error("Could not open "+path.Join(fullPath, passwdName)) - return err - } - var userOnlyCont []string - for _, containerUser := range containerUsers { - foundUser := false - for idxHost, user := range userDb { - if containerUser.name == user.Name { - foundUser = true - (&userDb[idxHost]).UidCont = containerUser.uid - (&userDb[idxHost]).GidCont = containerUser.gid - } - } - if !foundUser { - userDb = append(userDb, completeUserInfo{Name: containerUser.name, - UidHost: -1, GidHost: -1, UidCont: containerUser.uid, GidCont: containerUser.gid}) - wwlog.Warn("user: %s:%v:%v not present on host", containerUser.name, containerUser.uid, containerUser.gid) - userOnlyCont = append(userOnlyCont, containerUser.name) - } - } + groupSync := make(syncDB) + if err := groupSync.readFromHost(groupPath); err != nil { return err } + if err := groupSync.readFromContainer(containerGroupPath); err != nil { return err } + if err := groupSync.checkConflicts(); err != nil { return err } + if err := groupSync.findGroupFiles(containerPath); err != nil { return err } - // detect users in the host and container with conflicting uids - for _, containerUser := range userDb { - if (containerUser.UidCont == -1 || containerUser.UidHost != -1) { - // containerUser is either not actually in the - // container or is also in the host - continue - } - for _, hostUser := range userDb { - if hostUser.UidHost == containerUser.UidCont { - wwlog.Warn("uid(%v) collision for host: %s and container: %s", - containerUser.UidCont, hostUser.Name, containerUser.Name) - return errors.New(fmt.Sprintf("user %s only present in container has same uid(%v) as user %s on host,\n"+ - "add this user to /etc/passwd on host", containerUser.Name, containerUser.UidCont, hostUser.Name)) - } - } - } + passwdSync.log("passwd") + groupSync.log("group") if showOnly { - wwlog.Info("uid/gid not synced, run \nwwctl container syncuser --write %s\nto synchronize uid/gids.", containerName) - return nil - } - // create list of files which need changed ownerships in order to - // change them later what avoid uid/gid collisions - for idx, user := range userDb { - if (user.UidHost != user.UidCont && user.UidHost != -1) || - (user.GidHost != user.GidCont && user.GidHost != -1 && user.UidHost != -1) { - wwlog.Verbose(fmt.Sprintf("host %s:%v:%v <-> container %s:%v:%v", - user.Name, user.UidHost, user.GidHost, user.Name, user.UidCont, user.GidCont)) - err = filepath.Walk(fullPath, func(filePath string, info fs.FileInfo, err error) error { - // root is always good, if we fail to get UID/GID of a file - var uid, gid int - if stat, ok := info.Sys().(*syscall.Stat_t); ok { - uid = int(stat.Uid) - gid = int(stat.Gid) - } - if uid == user.UidCont { - (&userDb[idx]).FileListUid = append((&userDb[idx]).FileListUid, filePath) - } - if gid == user.GidCont { - (&userDb[idx]).FileListGid = append((&userDb[idx]).FileListGid, filePath) - } - return nil - }) - if err != nil { - return err - } + if passwdSync.needsSync() || groupSync.needsSync() { + wwlog.Info("uid/gid not synced: run `wwctl container syncuser --write %s`", containerName) + } else { + wwlog.Info("uid/gid already synced") } - } - // change uids and gid of file - for _, user := range userDb { - if len(user.FileListUid) != 0 { - //fmt.Printf("uidList(%s): %v\n", user.Name, user.FileListUid) - for _, file := range user.FileListUid { - fsInfo, err := os.Stat(file) - if err != nil { - return err - } - var gid int - if stat, ok := fsInfo.Sys().(*syscall.Stat_t); ok { - gid = int(stat.Gid) - } - wwlog.Debug("%s chown(%v,%v)", file, user.UidHost, gid) - err = os.Chown(file, user.UidHost, gid) - if err != nil { - return err - } - } - } - if len(user.FileListGid) != 0 { - //fmt.Printf("gidList(%s): %v\n", user.Name, user.FileListGid) - for _, file := range user.FileListGid { - fsInfo, err := os.Stat(file) - if err != nil { - return err - } - var uid int - if stat, ok := fsInfo.Sys().(*syscall.Stat_t); ok { - uid = int(stat.Uid) - } - wwlog.Debug("%s chown(%v,%v)", file, user.UidHost, uid) - // only chown files and dirs - if fsInfo.IsDir() && fsInfo.Mode().IsRegular() { - err = os.Chown(file, uid, user.GidHost) - if err != nil { - return err - } - } - } + } else { + if err := passwdSync.chownUserFiles(); err != nil { return err } + if err := groupSync.chownGroupFiles(); err != nil { return err } + if err := passwdSync.update(containerPasswdPath, passwdPath); err != nil { return err } + if err := groupSync.update(containerGroupPath, groupPath); err != nil { return err } + wwlog.Info("uid/gid synced for container %s", containerName) + } + + return nil +} + + +// A syncDB maps user or group names to syncInfo instances, which +// correlate IDs between host and container and track affected +// files. This can be used for either /etc/passwd or /etc/group IDs. +type syncDB map[string]syncInfo + + +// checkConflicts inspects a syncDB map for irreconcilable +// conflicts. A conflict arises if the container has an entry with the +// same id as an entry in the host and the host does not have an entry +// with the same name. +func (db syncDB) checkConflicts() error { + for nameInContainer, containerIds := range db { + if (!containerIds.inContainer() || containerIds.inHost()) { + continue } - } - // get the entries for the passwd/group file before copy over - passwdEntries, err := getEntires(path.Join(fullPath, passwdName), userOnlyCont) - if err != nil { - return err - } - // implicitly assuming that users/groups which only exists on the host have the same name - groupEntries, err := getEntires(path.Join(fullPath, groupName), userOnlyCont) - if err != nil { - return err - } - if err = os.Remove(path.Join(fullPath, passwdName)); err != nil { - return err - } - if err = os.Remove(path.Join(fullPath, groupName)); err != nil { - return err - } - if err = util.CopyFile(passwdName, path.Join(fullPath, passwdName)); err != nil { - return err - } - if err = util.CopyFile(groupName, path.Join(fullPath, groupName)); err != nil { - return err - } - if err = util.AppendLines(path.Join(fullPath, passwdName), passwdEntries); err != nil { - return err - } - if err = util.AppendLines(path.Join(fullPath, groupName), groupEntries); err != nil { - return err + for nameInHost, hostIds := range db { + if hostIds.HostID == containerIds.ContainerID { + errorMsg := fmt.Sprintf("id(%v) collision: host(%s) container(%s)", containerIds.ContainerID, nameInHost, nameInContainer) + wwlog.Error(errorMsg) + wwlog.Error("add %s to host to resolve conflict", nameInContainer) + return errors.New(errorMsg) + } + } } return nil - } -/* -creates simple user db []simpleUserInfo for a /etc/{passwd|group} file -*/ -func createPasswdMap(fileName string) ([]simpleUserInfo, error) { - var nameDb []simpleUserInfo - file, err := os.Open(fileName) - if err != nil { - return nil, err - } - defer file.Close() - fileScanner := bufio.NewScanner(file) - for fileScanner.Scan() { - line := fileScanner.Text() - entries := strings.Split(line, ":") - name := entries[0] - uid, err := strconv.Atoi(entries[2]) - if err != nil { - wwlog.Warn("could not parse uid(%s) for %s", entries[2], name) - } - gid, err := strconv.Atoi(entries[3]) - if err != nil { - wwlog.Warn("could not parse gid(%s) for %s", entries[2], name) - } - if name != "" { - nameDb = append(nameDb, simpleUserInfo{name: name, uid: uid, gid: gid}) - } +// log generates debug and verbose logs inspecting a syncDB map. +// +// The provided prefix is prepended to log entries to provide context +// for the given syncDB map. (e.g., to differentiate between a user or +// group map) +func (db syncDB) log(prefix string) { + var onlyContainer, onlyHost, match, differ []string + for name, ids := range db { + wwlog.Debug("%s: %s: host(%v) container(%v) files: %v", prefix, name, ids.HostID, ids.ContainerID, ids.ContainerFiles) + if ids.onlyContainer() { onlyContainer = append(onlyContainer, name) } + if ids.onlyHost() { onlyHost = append(onlyHost, name) } + if ids.match() { match = append(match, name) } + if ids.differ() { differ = append(differ, name) } } - wwlog.Debug(fmt.Sprintf("created uid/gid map with %v entries from %s", len(nameDb), fileName)) - return nameDb, nil + + wwlog.Verbose("%s: container only: %v", prefix, onlyContainer) + wwlog.Verbose("%s: host only: %v", prefix, onlyHost) + wwlog.Verbose("%s: match: %v", prefix, match) + wwlog.Verbose("%s: differ: %v", prefix, differ) } -/* -Creates a slice with the entries of of passwd for the given slice of user names -*/ -func getEntires(fileName string, names []string) ([]string, error) { - file, err := os.Open(fileName) - if err != nil { - return nil, err + +// read reads fileName (an /etc/passwd or /etc/group file) into a +// syncDB map. Since the name and numerical id for both files are +// stored at fields 0 and 2, the same function can read both files. +// +// fromContainer identifies whether the given fileName includes +// entries from the host or the container. +func (db syncDB) read(fileName string, fromContainer bool) error { + wwlog.Debug("read %s (container: %t)", fileName, fromContainer) + if file, err := os.Open(fileName); err != nil { + return err + } else { + defer file.Close() + fileScanner := bufio.NewScanner(file) + for fileScanner.Scan() { + line := fileScanner.Text() + fields := strings.Split(line, ":") + + name := fields[0] + if name == "" { + continue + } + + id, err := strconv.Atoi(fields[2]) + if err != nil { + wwlog.Warn("Ignoring line %s (parse error)", line) + continue + } + + entry, ok := db[name] + if !ok { + entry = syncInfo{HostID: -1, ContainerID: -1} + } + + if fromContainer { + if entry.ContainerID == -1 { + entry.ContainerID = id + } else if entry.ContainerID != id { + wwlog.Warn("Ignoring container id(%v) for %s from %s after existing value %v", id, name, fileName, entry.ContainerID) + continue + } + } else { + if entry.HostID == -1 { + entry.HostID = id + } else if entry.HostID != id { + wwlog.Warn("Ignoring host id(%v) for %s from %s after existing value %v", id, name, fileName, entry.HostID) + continue + } + } + + db[name] = entry + } + return nil } +} + + +// readFromHost reads fileName into a syncDB map. +// +// Equivalent to read(fileName, false) +func (db syncDB) readFromHost(fileName string) error { return db.read(fileName, false) } + + +// readFromContainer reads fileName into a syncDB map. +// +// Equivalent to read(fileName, true) +func (db syncDB) readFromContainer(fileName string) error { return db.read(fileName, true) } + + +// findFiles finds files under containerPath that are owned by each +// tracked container ID. +// +// If byGid is true, files with a matching gid are +// recorded. Otherwise, files with a matching uid are recorded. +func (db syncDB) findFiles(containerPath string, byGid bool) error { + for name, ids := range db { + if err := ids.findFiles(containerPath, byGid); err != nil { return err } + if len(ids.ContainerFiles) > 0 { + wwlog.Debug("files for %s (%v -> %v, gid: %v): %v", name, ids.ContainerID, ids.HostID, byGid, ids.ContainerFiles) + } + db[name] = ids + } + return nil +} + + +// findUserFiles is equivalent to findFiles(containerPath, false) +func (db syncDB) findUserFiles(containerPath string) error { return db.findFiles(containerPath, false) } + + +// findGroupFiles is equivalent to findFiles(containerPath, true) +func (db syncDB) findGroupFiles(containerPath string) error { return db.findFiles(containerPath, true) } + + +// chownFiles updates found files (see findFiles) to reflect ownership +// using host ids. +// +// If byGid is true, file gids are updated. Otherwise, file uids are +// updated. +func (db syncDB) chownFiles(byGid bool) error { + for _, ids := range db { + if err := ids.chownFiles(byGid); err != nil { return err } + } + return nil +} + + +// chownUserFiles is equivalent to chownFiles(false) +func (db syncDB) chownUserFiles() error { return db.chownFiles(false) } + + +// chownUserFiles is equivalent to chownFiles(true) +func (db syncDB) chownGroupFiles() error { return db.chownFiles(true) } + + +// getOnlyContainerLines returns the lines from fileName (either an +// /etc/passwd or /etc/group file) for names that occur only in the +// container. +// +// These lines are added to the respective file from the host when +// updating the container. +func (db syncDB) getOnlyContainerLines(fileName string) ([]string, error) { + file, err := os.Open(fileName) + if err != nil { return nil, err } defer file.Close() - var list []string fileScanner := bufio.NewScanner(file) + + var lines []string for fileScanner.Scan() { line := fileScanner.Text() - entries := strings.Split(line, ":") - for _, name := range names { - if entries[0] == name { - list = append(list, line) + fields := strings.Split(line, ":") + for name, ids := range db { + if fields[0] == name { + if ids.onlyContainer() { + lines = append(lines, line) + } + break } } } - wwlog.Debug("file: %s, list: %v", fileName, list) - return list, nil + wwlog.Debug("file: %s, entries only in container: %v", fileName, lines) + return lines, nil +} + + +// update replaces containerPath with hostPath and adds lines from +// getOnlyContainerLines to the end of containerPath. This is used to +// replace /etc/passwd (or /etc/group) in the container with the +// equivalent file from the host while retaining names unique to the +// container. +func (db syncDB) update(containerPath string, hostPath string) error { + wwlog.Debug("update %s from %s)", containerPath, hostPath) + if lines, err := db.getOnlyContainerLines(containerPath); err != nil { + return err + } else { + if err := os.Remove(containerPath); err != nil { return err } + if err := util.CopyFile(hostPath, containerPath); err != nil { return err } + if err := util.AppendLines(containerPath, lines); err != nil { return err } + return nil + } +} + + +// needsSync returns true if the syncDB map indicates that ids between +// the container and host are out-of-sync. +func (db syncDB) needsSync() bool { + for name, ids := range db { + if ids.onlyHost() { + wwlog.Debug("sync required: %s only in host", name) + return true + } + if ids.differ() { + wwlog.Debug("sync required: %s is %v in host and %v in container", name, ids.HostID, ids.ContainerID) + return true + } + if len(ids.ContainerFiles) > 0 { + wwlog.Debug("sync required: %v files to update for %s", len(ids.ContainerFiles), name) + } + } + return false +} + + +// sycncInfo correlates the numerical id of a name on the host +// (HostID) and the container (ContainerID), along with the files in +// the container that are owned by that name. This allows affected +// files to be updated when the HostID is applied to the container. +type syncInfo struct { + HostID int `access:"r,w"` + ContainerID int `access:"r,w"` + ContainerFiles []string `access:"r,w"` +} + + +// inHost returns true if info has a record of an id for this name in +// the host. +func (info *syncInfo) inHost() bool { + return info.HostID != -1 +} + + +// inContainer returns true if info has a record of an id for this +// name in the container. +func (info *syncInfo) inContainer() bool { + return info.ContainerID != -1 +} + + +// onlyHost returns true if info has a record of an id for this name +// in the host and not in the container. +func (info *syncInfo) onlyHost() bool { + return info.inHost() && !info.inContainer() +} + + +// onlyHost returns true if info has a record of an id for this name +// in the container and not in the host. +func (info *syncInfo) onlyContainer() bool { + return info.inContainer() && !info.inHost() +} + + +// match returns true if info represents a name that exists with the +// same numerical id in both the host and the container. +func (info *syncInfo) match() bool { + return info.inContainer() && info.inHost() && info.HostID == info.ContainerID +} + + +// differ returns true if info represents a name that exists in both +// the host and the container but with different numerical ids. +func (info *syncInfo) differ() bool { + return info.inContainer() && info.inHost() && info.HostID != info.ContainerID +} + + +// findFiles walks containerPath to find files owned by the name +// represented by info and records them in info.ContainerFiles. +// +// If byGid is true, the file's gid is checked; otherwise, the file's +// uid is checked. +func (info *syncInfo) findFiles(containerPath string, byGid bool) error { + var containerFiles []string + if (info.inHost() && !info.match()) { + if err := filepath.Walk(containerPath, func(filePath string, fileInfo fs.FileInfo, err error) error { + if stat, ok := fileInfo.Sys().(*syscall.Stat_t); ok { + var id int + if byGid { + id = int(stat.Gid) + } else { + id = int(stat.Uid) + } + if id == info.ContainerID { + containerFiles = append(containerFiles, filePath) + } + } + return nil + }); err != nil { + return err + } + } + info.ContainerFiles = containerFiles + return nil +} + + +// chownFiles updates the files recorded in info.ContainerFiles to use +// the numerical IDs from the host. +// +// If byGid is true, the file's gid is updated; otherwise, the file's +// uid is updated. +func (info *syncInfo) chownFiles(byGid bool) error { + for _, file := range info.ContainerFiles { + if fileInfo, err := os.Stat(file); err != nil { + return err + } else { + if fileInfo.IsDir() || fileInfo.Mode().IsRegular() { + var newUid, newGid int + if byGid { + newUid = -1 + newGid = info.HostID + } else { + newUid = info.HostID + newGid = -1 + } + if err := os.Chown(file, newUid, newGid); err != nil { return err } + } + } + } + return nil } diff --git a/internal/pkg/container/syncuids_test.go b/internal/pkg/container/syncuids_test.go new file mode 100644 index 00000000..d4cf3f8d --- /dev/null +++ b/internal/pkg/container/syncuids_test.go @@ -0,0 +1,283 @@ +package container + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + + +func writeTempFile(t *testing.T, input string) (string) { + tempFile, createTempError := os.CreateTemp("", "syncuids-*") + assert.NoError(t, createTempError) + _, writeError := tempFile.Write([]byte(input)) + assert.NoError(t, writeError) + assert.NoError(t, tempFile.Sync()) + return tempFile.Name() +} + + +func makeSyncDB(t *testing.T, hostInput string, containerInput string) (syncDB) { + hostFileName := writeTempFile(t, hostInput) + defer os.Remove(hostFileName) + containerFileName := writeTempFile(t, containerInput) + defer os.Remove(containerFileName) + db := make(syncDB) + var err error + err = db.readFromHost(hostFileName) + assert.NoError(t, err) + err = db.readFromContainer(containerFileName) + assert.NoError(t, err) + return db +} + + +func Test_readFromHost_single(t *testing.T) { + hostFileName := writeTempFile(t, `testuser:x:1001:1001::/home/testuser:/bin/bash`) + defer os.Remove(hostFileName) + + db := make(syncDB) + err := db.readFromHost(hostFileName) + assert.NoError(t, err) + + assert.Len(t, db, 1) + assert.Equal(t, 1001, db["testuser"].HostID) + assert.Equal(t, -1, db["testuser"].ContainerID) +} + + +func Test_readFromHost_multiple(t *testing.T) { + hostFileName := writeTempFile(t, ` +testuser1:x:1001:1001::/home/testuser:/bin/bash +testuser2:x:1002:1002::/home/testuser:/bin/bash +`) + defer os.Remove(hostFileName) + + db := make(syncDB) + err := db.readFromHost(hostFileName) + assert.NoError(t, err) + + assert.Len(t, db, 2) + assert.Equal(t, 1001, db["testuser1"].HostID) + assert.Equal(t, -1, db["testuser1"].ContainerID) + assert.Equal(t, 1002, db["testuser2"].HostID) + assert.Equal(t, -1, db["testuser2"].ContainerID) +} + + +func Test_readFromContainer_single(t *testing.T) { + containerFileName := writeTempFile(t, `testuser:x:1001:1001::/home/testuser:/bin/bash`) + defer os.Remove(containerFileName) + + db := make(syncDB) + err := db.readFromContainer(containerFileName) + assert.NoError(t, err) + + assert.Len(t, db, 1) + assert.Equal(t, 1001, db["testuser"].ContainerID) + assert.Equal(t, -1, db["testuser"].HostID) +} + + +func Test_readFromContainer_multiple(t *testing.T) { + containerFileName := writeTempFile(t, ` +testuser1:x:1001:1001::/home/testuser:/bin/bash +testuser2:x:1002:1002::/home/testuser:/bin/bash +`) + defer os.Remove(containerFileName) + + db := make(syncDB) + err := db.readFromContainer(containerFileName) + assert.NoError(t, err) + assert.Len(t, db, 2) + assert.Equal(t, 1001, db["testuser1"].ContainerID) + assert.Equal(t, -1, db["testuser1"].HostID) + assert.Equal(t, 1002, db["testuser2"].ContainerID) + assert.Equal(t, -1, db["testuser2"].HostID) +} + + +func Test_readFromBoth_multiple(t *testing.T) { + containerFileName := writeTempFile(t, ` +testuser1:x:1001:1001::/home/testuser:/bin/bash +testuser2:x:1002:1002::/home/testuser:/bin/bash +`) + defer os.Remove(containerFileName) + + + hostFileName := writeTempFile(t, ` +testuser1:x:2001:2001::/home/testuser:/bin/bash +testuser3:x:2003:2003::/home/testuser:/bin/bash +`) + defer os.Remove(hostFileName) + + db := make(syncDB) + var err error + err = db.readFromContainer(containerFileName) + assert.NoError(t, err) + err = db.readFromHost(hostFileName) + assert.NoError(t, err) + assert.Len(t, db, 3) + assert.Equal(t, 1001, db["testuser1"].ContainerID) + assert.Equal(t, 2001, db["testuser1"].HostID) + assert.Equal(t, 1002, db["testuser2"].ContainerID) + assert.Equal(t, -1, db["testuser2"].HostID) + assert.Equal(t, -1, db["testuser3"].ContainerID) + assert.Equal(t, 2003, db["testuser3"].HostID) +} + + +func Test_checkConflicts_empty(t *testing.T) { + db := makeSyncDB(t, "", "") + assert.NoError(t, db.checkConflicts()) +} + + +func Test_checkConflicts_single(t *testing.T) { + db := makeSyncDB(t, "", "testuser:x:1001:1001::/home/testuser:/bin/bash") + assert.NoError(t, db.checkConflicts()) +} + + +func Test_checkConflicts_match(t *testing.T) { + db := makeSyncDB(t, + "testuser:x:1001:1001::/home/testuser:/bin/bash", + "testuser:x:1001:1001::/home/testuser:/bin/bash") + assert.NoError(t, db.checkConflicts()) +} + + +func Test_checkConflicts_conflict(t *testing.T) { + db := makeSyncDB(t, + "testuser2:x:1001:1001::/home/testuser:/bin/bash", + "testuser1:x:1001:1001::/home/testuser:/bin/bash") + assert.Error(t, db.checkConflicts()) +} + + +func Test_getOnlyContainerLines(t *testing.T) { + containerFileName := writeTempFile(t, ` +testuser1:x:1001:1001::/home/testuser:/bin/bash +testuser2:x:1002:1002::/home/testuser:/bin/bash +`) + defer os.Remove(containerFileName) + + + hostFileName := writeTempFile(t, ` +testuser1:x:2001:2001::/home/testuser:/bin/bash +testuser3:x:2003:2003::/home/testuser:/bin/bash +`) + defer os.Remove(hostFileName) + + db := make(syncDB) + var err error + err = db.readFromContainer(containerFileName) + assert.NoError(t, err) + err = db.readFromHost(hostFileName) + assert.NoError(t, err) + + lines, err := db.getOnlyContainerLines(containerFileName) + assert.NoError(t, err) + + assert.Len(t, lines, 1) + assert.Equal(t, lines[0], "testuser2:x:1002:1002::/home/testuser:/bin/bash") +} + + +func Test_needsSync_empty(t *testing.T) { + db := makeSyncDB(t, "", "") + assert.False(t, db.needsSync()) +} + + +func Test_needsSync_containerOnly(t *testing.T) { + db := makeSyncDB(t, "", ` +testuser1:x:1001:1001::/home/testuser:/bin/bash +testuser2:x:1002:1002::/home/testuser:/bin/bash`) + + assert.False(t, db.needsSync()) +} + + +func Test_needsSync_hostOnly(t *testing.T) { + db := makeSyncDB(t, ` +testuser1:x:1001:1001::/home/testuser:/bin/bash +testuser2:x:1002:1002::/home/testuser:/bin/bash`, "") + + assert.True(t, db.needsSync()) +} + + +func Test_needsSync_match(t *testing.T) { + db := makeSyncDB(t, + "testuser:x:1001:1001::/home/testuser:/bin/bash", + "testuser:x:1001:1001::/home/testuser:/bin/bash") + + assert.False(t, db.needsSync()) +} + + +func Test_needsSync_differ(t *testing.T) { + db := makeSyncDB(t, + ` +testuser1:x:2001:2001::/home/testuser:/bin/bash +testuser3:x:2003:2003::/home/testuser:/bin/bash`, + ` +testuser1:x:1001:1001::/home/testuser:/bin/bash +testuser2:x:1002:1002::/home/testuser:/bin/bash`) + + assert.True(t, db.needsSync()) +} + + +func Test_onlyHost(t *testing.T) { + db := makeSyncDB(t, "testuser1:x:2001:2001::/home/testuser:/bin/bash", "") + entry := db["testuser1"] + assert.True(t, entry.inHost()) + assert.False(t, entry.inContainer()) + assert.True(t, entry.onlyHost()) + assert.False(t, entry.onlyContainer()) + assert.False(t, entry.match()) + assert.False(t, entry.differ()) +} + + +func Test_onlyContainer(t *testing.T) { + db := makeSyncDB(t, "", "testuser1:x:2001:2001::/home/testuser:/bin/bash") + entry := db["testuser1"] + assert.False(t, entry.inHost()) + assert.True(t, entry.inContainer()) + assert.False(t, entry.onlyHost()) + assert.True(t, entry.onlyContainer()) + assert.False(t, entry.match()) + assert.False(t, entry.differ()) +} + + +func Test_match(t *testing.T) { + db := makeSyncDB(t, + "testuser1:x:2001:2001::/home/testuser:/bin/bash", + "testuser1:x:2001:2001::/home/testuser:/bin/bash") + entry := db["testuser1"] + assert.True(t, entry.inHost()) + assert.True(t, entry.inContainer()) + assert.False(t, entry.onlyHost()) + assert.False(t, entry.onlyContainer()) + assert.True(t, entry.match()) + assert.False(t, entry.differ()) +} + + +func Test_differ(t *testing.T) { + db := makeSyncDB(t, + "testuser1:x:1001:1001::/home/testuser:/bin/bash", + "testuser1:x:2001:2001::/home/testuser:/bin/bash") + entry := db["testuser1"] + assert.True(t, entry.inHost()) + assert.True(t, entry.inContainer()) + assert.False(t, entry.onlyHost()) + assert.False(t, entry.onlyContainer()) + assert.False(t, entry.match()) + assert.True(t, entry.differ()) +} From 7093d722a5d3176afde43cdcbc71a3ae794d3465 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Tue, 8 Aug 2023 19:16:24 -0600 Subject: [PATCH 26/75] Fix formatting of LICENSE.md Signed-off-by: Jonathon Anderson --- LICENSE.md | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index fa199c60..31dfc198 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,13 +1,36 @@ Copyright (c) 2001-2020, Gregory M. Kurtzer. All rights reserved. -Copyright (c) 2003-2011, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy). All rights reserved.. All rights reserved. + +Copyright (c) 2003-2011, The Regents of the University of California, +through Lawrence Berkeley National Laboratory (subject to receipt of +any required approvals from the U.S. Dept. of Energy). All rights +reserved.. All rights reserved. + Copyright (c) 2020, Ctrl IQ, Inc. All rights reserved. -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the + distribution. -3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From abd3e21aa016823afb672400fbf69ffae52a796e Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Tue, 8 Aug 2023 19:31:02 -0600 Subject: [PATCH 27/75] Fix a typo in container delete confirmation prompt Signed-off-by: Jonathon Anderson --- internal/app/wwctl/container/delete/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/app/wwctl/container/delete/main.go b/internal/app/wwctl/container/delete/main.go index 250b5ac2..576ccef7 100644 --- a/internal/app/wwctl/container/delete/main.go +++ b/internal/app/wwctl/container/delete/main.go @@ -14,7 +14,7 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) { ContainerNames: args, } if !SetYes { - yes := util.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to container %s", args)) + yes := util.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to delete container %s", args)) if !yes { return } From 209453389459ca5ed9c914ff079480dd2dea36a2 Mon Sep 17 00:00:00 2001 From: Matthew Hink Date: Wed, 14 Jun 2023 15:17:52 -0700 Subject: [PATCH 28/75] Build fixes for API - Updated the linter from 1.50 to 1.53.2 because the previous version was falling over (likely OOM). - Updated protoc build command for newer version of protobuf. The make command below should work now: ```shell make clean setup proto all build wwapid wwapic wwapird ; echo $? ``` Signed-off-by: MatthewHink --- CHANGELOG.md | 1 + Makefile | 5 +- internal/pkg/api/routes/wwapiv1/routes.pb.go | 4 +- .../pkg/api/routes/wwapiv1/routes.pb.gw.go | 266 ++++++++++-------- .../pkg/api/routes/wwapiv1/routes_grpc.pb.go | 65 +++-- 5 files changed, 201 insertions(+), 140 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5596f867..df7cede1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add Quickstart guide for EL9 - Add EL9 Quickstart guide to index.rst - Container file gids are now updated properly during syncuser. #840 +- Fix build for API. ### Changed diff --git a/Makefile b/Makefile index 53316639..2cb29c9c 100644 --- a/Makefile +++ b/Makefile @@ -86,7 +86,7 @@ CONFIG := $(shell pwd) GO_TOOLS_BIN := $(addprefix $(TOOLS_BIN)/, $(notdir $(GO_TOOLS))) GO_TOOLS_VENDOR := $(addprefix vendor/, $(GO_TOOLS)) GOLANGCI_LINT := $(TOOLS_BIN)/golangci-lint -GOLANGCI_LINT_VERSION := v1.50.0 +GOLANGCI_LINT_VERSION := v1.53.2 # helper functions godeps=$(shell go list -deps -f '{{if not .Standard}}{{ $$dep := . }}{{range .GoFiles}}{{$$dep.Dir}}/{{.}} {{end}}{{end}}' $(1) | sed "s%${PWD}/%%g") @@ -263,8 +263,7 @@ latexpdf: reference ## sudo ldconfig # refresh shared library cache. ## To setup protoc-gen-grpc-gateway, see https://github.com/grpc-ecosystem/grpc-gateway proto: - rm -rf internal/pkg/api/routes/wwapiv1/ - protoc -I internal/pkg/api/routes/v1 -I=. \ + protoc -I /usr/include -I internal/pkg/api/routes/v1 -I=. \ --grpc-gateway_out=. \ --grpc-gateway_opt logtostderr=true \ --go_out=. \ diff --git a/internal/pkg/api/routes/wwapiv1/routes.pb.go b/internal/pkg/api/routes/wwapiv1/routes.pb.go index 3f87f8a6..e3dbce97 100644 --- a/internal/pkg/api/routes/wwapiv1/routes.pb.go +++ b/internal/pkg/api/routes/wwapiv1/routes.pb.go @@ -3,8 +3,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.0 -// protoc v3.9.2 +// protoc-gen-go v1.28.1 +// protoc v3.21.1 // source: routes.proto package wwapiv1 diff --git a/internal/pkg/api/routes/wwapiv1/routes.pb.gw.go b/internal/pkg/api/routes/wwapiv1/routes.pb.gw.go index b7591bea..1165a2e3 100644 --- a/internal/pkg/api/routes/wwapiv1/routes.pb.gw.go +++ b/internal/pkg/api/routes/wwapiv1/routes.pb.gw.go @@ -396,20 +396,22 @@ func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerBuild", runtime.WithHTTPPathPattern("/v1/containerbuild")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_WWApi_ContainerBuild_0(annotatedContext, 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) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_ContainerBuild_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_ContainerBuild_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -419,20 +421,22 @@ func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerDelete", runtime.WithHTTPPathPattern("/v1/container")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_WWApi_ContainerDelete_0(annotatedContext, 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) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_ContainerDelete_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_ContainerDelete_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -442,20 +446,22 @@ func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerImport", runtime.WithHTTPPathPattern("/v1/container")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_WWApi_ContainerImport_0(annotatedContext, 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) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_ContainerImport_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_ContainerImport_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -465,20 +471,22 @@ func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerList", runtime.WithHTTPPathPattern("/v1/container")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_WWApi_ContainerList_0(annotatedContext, 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) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_ContainerList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_ContainerList_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -488,20 +496,22 @@ func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerShow", runtime.WithHTTPPathPattern("/v1/containershow")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_WWApi_ContainerShow_0(annotatedContext, 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) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_ContainerShow_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_ContainerShow_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -511,20 +521,22 @@ func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeAdd", runtime.WithHTTPPathPattern("/v1/node")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_WWApi_NodeAdd_0(annotatedContext, 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) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_NodeAdd_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_NodeAdd_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -534,20 +546,22 @@ func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeDelete", runtime.WithHTTPPathPattern("/v1/node")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_WWApi_NodeDelete_0(annotatedContext, 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) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_NodeDelete_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_NodeDelete_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -557,20 +571,22 @@ func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeList", runtime.WithHTTPPathPattern("/v1/node")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_WWApi_NodeList_0(annotatedContext, 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) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_NodeList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_NodeList_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -580,20 +596,22 @@ func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeSet", runtime.WithHTTPPathPattern("/v1/nodeset")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_WWApi_NodeSet_0(annotatedContext, 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) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_NodeSet_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_NodeSet_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -603,20 +621,22 @@ func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeStatus", runtime.WithHTTPPathPattern("/v1/nodestatus")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_WWApi_NodeStatus_0(annotatedContext, 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) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_NodeStatus_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_NodeStatus_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -626,20 +646,22 @@ func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/Version", runtime.WithHTTPPathPattern("/version")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_WWApi_Version_0(annotatedContext, 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) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_Version_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_Version_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -649,7 +671,7 @@ func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv // 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...) + conn, err := grpc.DialContext(ctx, endpoint, opts...) if err != nil { return err } @@ -688,19 +710,21 @@ func RegisterWWApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerBuild", runtime.WithHTTPPathPattern("/v1/containerbuild")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) + resp, md, err := request_WWApi_ContainerBuild_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_ContainerBuild_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_ContainerBuild_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -708,19 +732,21 @@ func RegisterWWApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerDelete", runtime.WithHTTPPathPattern("/v1/container")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) + resp, md, err := request_WWApi_ContainerDelete_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_ContainerDelete_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_ContainerDelete_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -728,19 +754,21 @@ func RegisterWWApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerImport", runtime.WithHTTPPathPattern("/v1/container")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) + resp, md, err := request_WWApi_ContainerImport_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_ContainerImport_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_ContainerImport_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -748,19 +776,21 @@ func RegisterWWApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerList", runtime.WithHTTPPathPattern("/v1/container")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) + resp, md, err := request_WWApi_ContainerList_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_ContainerList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_ContainerList_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -768,19 +798,21 @@ func RegisterWWApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerShow", runtime.WithHTTPPathPattern("/v1/containershow")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) + resp, md, err := request_WWApi_ContainerShow_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_ContainerShow_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_ContainerShow_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -788,19 +820,21 @@ func RegisterWWApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeAdd", runtime.WithHTTPPathPattern("/v1/node")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) + resp, md, err := request_WWApi_NodeAdd_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_NodeAdd_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_NodeAdd_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -808,19 +842,21 @@ func RegisterWWApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeDelete", runtime.WithHTTPPathPattern("/v1/node")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) + resp, md, err := request_WWApi_NodeDelete_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_NodeDelete_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_NodeDelete_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -828,19 +864,21 @@ func RegisterWWApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeList", runtime.WithHTTPPathPattern("/v1/node")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) + resp, md, err := request_WWApi_NodeList_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_NodeList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_NodeList_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -848,19 +886,21 @@ func RegisterWWApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeSet", runtime.WithHTTPPathPattern("/v1/nodeset")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) + resp, md, err := request_WWApi_NodeSet_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_NodeSet_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_NodeSet_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -868,19 +908,21 @@ func RegisterWWApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/NodeStatus", runtime.WithHTTPPathPattern("/v1/nodestatus")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) + resp, md, err := request_WWApi_NodeStatus_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_NodeStatus_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_NodeStatus_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -888,19 +930,21 @@ func RegisterWWApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/Version", runtime.WithHTTPPathPattern("/version")) + var err error + var annotatedContext context.Context + annotatedContext, 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(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) + resp, md, err := request_WWApi_Version_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_WWApi_Version_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WWApi_Version_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) diff --git a/internal/pkg/api/routes/wwapiv1/routes_grpc.pb.go b/internal/pkg/api/routes/wwapiv1/routes_grpc.pb.go index d31185f6..65a3bb33 100644 --- a/internal/pkg/api/routes/wwapiv1/routes_grpc.pb.go +++ b/internal/pkg/api/routes/wwapiv1/routes_grpc.pb.go @@ -1,7 +1,10 @@ +// Routes for the wwapi (WareWulf API). +// TODO: Try protoc-gen-doc for generating documentation. + // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v3.9.2 +// - protoc-gen-go-grpc v1.3.0 +// - protoc v3.21.1 // source: routes.proto package wwapiv1 @@ -19,6 +22,20 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 +const ( + WWApi_ContainerBuild_FullMethodName = "/wwapi.v1.WWApi/ContainerBuild" + WWApi_ContainerDelete_FullMethodName = "/wwapi.v1.WWApi/ContainerDelete" + WWApi_ContainerImport_FullMethodName = "/wwapi.v1.WWApi/ContainerImport" + WWApi_ContainerList_FullMethodName = "/wwapi.v1.WWApi/ContainerList" + WWApi_ContainerShow_FullMethodName = "/wwapi.v1.WWApi/ContainerShow" + WWApi_NodeAdd_FullMethodName = "/wwapi.v1.WWApi/NodeAdd" + WWApi_NodeDelete_FullMethodName = "/wwapi.v1.WWApi/NodeDelete" + WWApi_NodeList_FullMethodName = "/wwapi.v1.WWApi/NodeList" + WWApi_NodeSet_FullMethodName = "/wwapi.v1.WWApi/NodeSet" + WWApi_NodeStatus_FullMethodName = "/wwapi.v1.WWApi/NodeStatus" + WWApi_Version_FullMethodName = "/wwapi.v1.WWApi/Version" +) + // 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. @@ -60,7 +77,7 @@ func NewWWApiClient(cc grpc.ClientConnInterface) WWApiClient { 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...) + err := c.cc.Invoke(ctx, WWApi_ContainerBuild_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -69,7 +86,7 @@ func (c *wWApiClient) ContainerBuild(ctx context.Context, in *ContainerBuildPara func (c *wWApiClient) ContainerDelete(ctx context.Context, in *ContainerDeleteParameter, opts ...grpc.CallOption) (*empty.Empty, error) { out := new(empty.Empty) - err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/ContainerDelete", in, out, opts...) + err := c.cc.Invoke(ctx, WWApi_ContainerDelete_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -78,7 +95,7 @@ func (c *wWApiClient) ContainerDelete(ctx context.Context, in *ContainerDeletePa 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...) + err := c.cc.Invoke(ctx, WWApi_ContainerImport_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -87,7 +104,7 @@ func (c *wWApiClient) ContainerImport(ctx context.Context, in *ContainerImportPa func (c *wWApiClient) ContainerList(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*ContainerListResponse, error) { out := new(ContainerListResponse) - err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/ContainerList", in, out, opts...) + err := c.cc.Invoke(ctx, WWApi_ContainerList_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -96,7 +113,7 @@ func (c *wWApiClient) ContainerList(ctx context.Context, in *empty.Empty, opts . 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...) + err := c.cc.Invoke(ctx, WWApi_ContainerShow_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -105,7 +122,7 @@ func (c *wWApiClient) ContainerShow(ctx context.Context, in *ContainerShowParame 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...) + err := c.cc.Invoke(ctx, WWApi_NodeAdd_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -114,7 +131,7 @@ func (c *wWApiClient) NodeAdd(ctx context.Context, in *NodeAddParameter, opts .. func (c *wWApiClient) NodeDelete(ctx context.Context, in *NodeDeleteParameter, opts ...grpc.CallOption) (*empty.Empty, error) { out := new(empty.Empty) - err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/NodeDelete", in, out, opts...) + err := c.cc.Invoke(ctx, WWApi_NodeDelete_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -123,7 +140,7 @@ func (c *wWApiClient) NodeDelete(ctx context.Context, in *NodeDeleteParameter, o 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...) + err := c.cc.Invoke(ctx, WWApi_NodeList_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -132,7 +149,7 @@ func (c *wWApiClient) NodeList(ctx context.Context, in *NodeNames, opts ...grpc. 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...) + err := c.cc.Invoke(ctx, WWApi_NodeSet_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -141,7 +158,7 @@ func (c *wWApiClient) NodeSet(ctx context.Context, in *NodeSetParameter, opts .. 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...) + err := c.cc.Invoke(ctx, WWApi_NodeStatus_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -150,7 +167,7 @@ func (c *wWApiClient) NodeStatus(ctx context.Context, in *NodeNames, opts ...grp func (c *wWApiClient) Version(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*VersionResponse, error) { out := new(VersionResponse) - err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/Version", in, out, opts...) + err := c.cc.Invoke(ctx, WWApi_Version_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -249,7 +266,7 @@ func _WWApi_ContainerBuild_Handler(srv interface{}, ctx context.Context, dec fun } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/wwapi.v1.WWApi/ContainerBuild", + FullMethod: WWApi_ContainerBuild_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).ContainerBuild(ctx, req.(*ContainerBuildParameter)) @@ -267,7 +284,7 @@ func _WWApi_ContainerDelete_Handler(srv interface{}, ctx context.Context, dec fu } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/wwapi.v1.WWApi/ContainerDelete", + FullMethod: WWApi_ContainerDelete_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).ContainerDelete(ctx, req.(*ContainerDeleteParameter)) @@ -285,7 +302,7 @@ func _WWApi_ContainerImport_Handler(srv interface{}, ctx context.Context, dec fu } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/wwapi.v1.WWApi/ContainerImport", + FullMethod: WWApi_ContainerImport_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).ContainerImport(ctx, req.(*ContainerImportParameter)) @@ -303,7 +320,7 @@ func _WWApi_ContainerList_Handler(srv interface{}, ctx context.Context, dec func } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/wwapi.v1.WWApi/ContainerList", + FullMethod: WWApi_ContainerList_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).ContainerList(ctx, req.(*empty.Empty)) @@ -321,7 +338,7 @@ func _WWApi_ContainerShow_Handler(srv interface{}, ctx context.Context, dec func } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/wwapi.v1.WWApi/ContainerShow", + FullMethod: WWApi_ContainerShow_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).ContainerShow(ctx, req.(*ContainerShowParameter)) @@ -339,7 +356,7 @@ func _WWApi_NodeAdd_Handler(srv interface{}, ctx context.Context, dec func(inter } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/wwapi.v1.WWApi/NodeAdd", + FullMethod: WWApi_NodeAdd_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).NodeAdd(ctx, req.(*NodeAddParameter)) @@ -357,7 +374,7 @@ func _WWApi_NodeDelete_Handler(srv interface{}, ctx context.Context, dec func(in } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/wwapi.v1.WWApi/NodeDelete", + FullMethod: WWApi_NodeDelete_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).NodeDelete(ctx, req.(*NodeDeleteParameter)) @@ -375,7 +392,7 @@ func _WWApi_NodeList_Handler(srv interface{}, ctx context.Context, dec func(inte } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/wwapi.v1.WWApi/NodeList", + FullMethod: WWApi_NodeList_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).NodeList(ctx, req.(*NodeNames)) @@ -393,7 +410,7 @@ func _WWApi_NodeSet_Handler(srv interface{}, ctx context.Context, dec func(inter } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/wwapi.v1.WWApi/NodeSet", + FullMethod: WWApi_NodeSet_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).NodeSet(ctx, req.(*NodeSetParameter)) @@ -411,7 +428,7 @@ func _WWApi_NodeStatus_Handler(srv interface{}, ctx context.Context, dec func(in } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/wwapi.v1.WWApi/NodeStatus", + FullMethod: WWApi_NodeStatus_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).NodeStatus(ctx, req.(*NodeNames)) @@ -429,7 +446,7 @@ func _WWApi_Version_Handler(srv interface{}, ctx context.Context, dec func(inter } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/wwapi.v1.WWApi/Version", + FullMethod: WWApi_Version_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).Version(ctx, req.(*empty.Empty)) From 97248797f5437eca3cbd67ea4ede0e68acc79c8e Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Wed, 9 Aug 2023 11:50:10 -0600 Subject: [PATCH 29/75] Restore %config(noreplace) to nodes.conf The previous PR #888 erroneously removed the %config(noreplace) macro from the nodes.conf file. This PR restores it. Signed-off-by: Jonathon Anderson --- warewulf.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/warewulf.spec.in b/warewulf.spec.in index e0f06208..f0208f83 100644 --- a/warewulf.spec.in +++ b/warewulf.spec.in @@ -120,7 +120,7 @@ getent group %{wwgroup} >/dev/null || groupadd -r %{wwgroup} %config(noreplace) %{_sysconfdir}/warewulf/wwapi*.conf %config(noreplace) %{_sysconfdir}/warewulf/examples %config(noreplace) %{_sysconfdir}/warewulf/ipxe -%attr(0640,-,-) %{_sysconfdir}/warewulf/nodes.conf +%config(noreplace) %attr(0640,-,-) %{_sysconfdir}/warewulf/nodes.conf %{_sysconfdir}/bash_completion.d/wwctl %dir %{_sharedstatedir}/warewulf From 94e80b17d7514c631e289a2323c11627c63a4511 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 01:21:33 -0600 Subject: [PATCH 30/75] Split Makefile variables into a separate file Signed-off-by: Jonathon Anderson --- Makefile | 100 +-------------------------------------------------- Variables.mk | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 99 deletions(-) create mode 100644 Variables.mk diff --git a/Makefile b/Makefile index 2cb29c9c..ea64242a 100644 --- a/Makefile +++ b/Makefile @@ -1,104 +1,6 @@ .PHONY: all clean contclean --include Defaults.mk - -# Linux distro (try and set to /etc/os-release ID) -OS_REL := $(shell sed -n "s/^ID\s*=\s*['"\""]\(.*\)['"\""]/\1/p" /etc/os-release) -OS ?= $(OS_REL) - -# List of variables to save and replace in files -VARLIST := OS - -# Project Information -VARLIST += WAREWULF VERSION RELEASE -WAREWULF ?= warewulf -VERSION ?= 4.4.0 -GIT_TAG := $(shell test -e .git && git log -1 --format="%h") - -ifdef GIT_TAG - ifdef $(filter $(OS),ubuntu debian) - RELEASE ?= 1.git_$(subst -,+,$(GIT_TAG)) - else - RELEASE ?= 1.git_$(subst -,_,$(GIT_TAG)) - endif -else - RELEASE ?= 1 -endif - -# Use LSB-compliant paths if OS is known -ifneq ($(OS),) - USE_LSB_PATHS := true -endif - -# Always default to GNU autotools default paths if PREFIX has been redefined -ifdef PREFIX - USE_LSB_PATHS := false -endif - -# System directory paths -VARLIST += PREFIX BINDIR SYSCONFDIR SRVDIR DATADIR MANDIR DOCDIR LOCALSTATEDIR -PREFIX ?= /usr/local -BINDIR ?= $(PREFIX)/bin -SYSCONFDIR ?= $(PREFIX)/etc -DATADIR ?= $(PREFIX)/share -MANDIR ?= $(DATADIR)/man -DOCDIR ?= $(DATADIR)/doc - -ifeq ($(USE_LSB_PATHS),true) - SRVDIR ?= /srv - LOCALSTATEDIR ?= /var/local -else - SRVDIR ?= $(PREFIX)/srv - LOCALSTATEDIR ?= $(PREFIX)/var -endif - -# OS-Specific Service Locations -VARLIST += TFTPDIR FIREWALLDDIR SYSTEMDDIR BASHCOMPDIR -SYSTEMDDIR ?= /usr/lib/systemd/system -BASHCOMPDIR ?= /etc/bash_completion.d -FIREWALLDDIR ?= /usr/lib/firewalld/services -ifeq ($(OS),suse) - TFTPDIR ?= /srv/tftpboot -endif -ifeq ($(OS),ubuntu) - TFTPDIR ?= /srv/tftp -endif -# Default to Red Hat / Rocky Linux -TFTPDIR ?= /var/lib/tftpboot - -# Warewulf directory paths -VARLIST += WWCLIENTDIR WWCONFIGDIR WWPROVISIONDIR WWOVERLAYDIR WWCHROOTDIR WWTFTPDIR WWDOCDIR WWDATADIR -WWCONFIGDIR := $(SYSCONFDIR)/$(WAREWULF) -WWPROVISIONDIR := $(SRVDIR)/$(WAREWULF) -WWOVERLAYDIR := $(LOCALSTATEDIR)/$(WAREWULF)/overlays -WWCHROOTDIR := $(LOCALSTATEDIR)/$(WAREWULF)/chroots -WWTFTPDIR := $(TFTPDIR)/$(WAREWULF) -WWDOCDIR := $(DOCDIR)/$(WAREWULF) -WWDATADIR := $(DATADIR)/$(WAREWULF) -WWCLIENTDIR ?= /warewulf - -# auto installed tooling -TOOLS_DIR := .tools -TOOLS_BIN := $(TOOLS_DIR)/bin -CONFIG := $(shell pwd) - -# tools -GO_TOOLS_BIN := $(addprefix $(TOOLS_BIN)/, $(notdir $(GO_TOOLS))) -GO_TOOLS_VENDOR := $(addprefix vendor/, $(GO_TOOLS)) -GOLANGCI_LINT := $(TOOLS_BIN)/golangci-lint -GOLANGCI_LINT_VERSION := v1.53.2 - -# helper functions -godeps=$(shell go list -deps -f '{{if not .Standard}}{{ $$dep := . }}{{range .GoFiles}}{{$$dep.Dir}}/{{.}} {{end}}{{end}}' $(1) | sed "s%${PWD}/%%g") -WWCTL_DEPS:=$(call godeps,cmd/wwctl/main.go) -WWCLIENT_DEPS:=$(call godeps,cmd/wwclient/main.go) - -# use GOPROXY for older git clients and speed up downloads -GOPROXY ?= https://proxy.golang.org -export GOPROXY - -# built tags needed for wwbuild binary -WW_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree +include Variables.mk # Default target all: config vendor wwctl wwclient man_pages wwapid wwapic wwapird diff --git a/Variables.mk b/Variables.mk new file mode 100644 index 00000000..29ef1924 --- /dev/null +++ b/Variables.mk @@ -0,0 +1,99 @@ +-include Defaults.mk + +# Linux distro (try and set to /etc/os-release ID) +OS_REL := $(shell sed -n "s/^ID\s*=\s*['"\""]\(.*\)['"\""]/\1/p" /etc/os-release) +OS ?= $(OS_REL) + +# List of variables to save and replace in files +VARLIST := OS + +# Project Information +VARLIST += WAREWULF VERSION RELEASE +WAREWULF ?= warewulf +VERSION ?= 4.4.0 +GIT_TAG := $(shell test -e .git && git log -1 --format="%h") + +ifdef GIT_TAG + ifdef $(filter $(OS),ubuntu debian) + RELEASE ?= 1.git_$(subst -,+,$(GIT_TAG)) + else + RELEASE ?= 1.git_$(subst -,_,$(GIT_TAG)) + endif +else + RELEASE ?= 1 +endif + +# Use LSB-compliant paths if OS is known +ifneq ($(OS),) + USE_LSB_PATHS := true +endif + +# Always default to GNU autotools default paths if PREFIX has been redefined +ifdef PREFIX + USE_LSB_PATHS := false +endif + +# System directory paths +VARLIST += PREFIX BINDIR SYSCONFDIR SRVDIR DATADIR MANDIR DOCDIR LOCALSTATEDIR +PREFIX ?= /usr/local +BINDIR ?= $(PREFIX)/bin +SYSCONFDIR ?= $(PREFIX)/etc +DATADIR ?= $(PREFIX)/share +MANDIR ?= $(DATADIR)/man +DOCDIR ?= $(DATADIR)/doc + +ifeq ($(USE_LSB_PATHS),true) + SRVDIR ?= /srv + LOCALSTATEDIR ?= /var/local +else + SRVDIR ?= $(PREFIX)/srv + LOCALSTATEDIR ?= $(PREFIX)/var +endif + +# OS-Specific Service Locations +VARLIST += TFTPDIR FIREWALLDDIR SYSTEMDDIR BASHCOMPDIR +SYSTEMDDIR ?= /usr/lib/systemd/system +BASHCOMPDIR ?= /etc/bash_completion.d +FIREWALLDDIR ?= /usr/lib/firewalld/services +ifeq ($(OS),suse) + TFTPDIR ?= /srv/tftpboot +endif +ifeq ($(OS),ubuntu) + TFTPDIR ?= /srv/tftp +endif +# Default to Red Hat / Rocky Linux +TFTPDIR ?= /var/lib/tftpboot + +# Warewulf directory paths +VARLIST += WWCLIENTDIR WWCONFIGDIR WWPROVISIONDIR WWOVERLAYDIR WWCHROOTDIR WWTFTPDIR WWDOCDIR WWDATADIR +WWCONFIGDIR := $(SYSCONFDIR)/$(WAREWULF) +WWPROVISIONDIR := $(SRVDIR)/$(WAREWULF) +WWOVERLAYDIR := $(LOCALSTATEDIR)/$(WAREWULF)/overlays +WWCHROOTDIR := $(LOCALSTATEDIR)/$(WAREWULF)/chroots +WWTFTPDIR := $(TFTPDIR)/$(WAREWULF) +WWDOCDIR := $(DOCDIR)/$(WAREWULF) +WWDATADIR := $(DATADIR)/$(WAREWULF) +WWCLIENTDIR ?= /warewulf + +# auto installed tooling +TOOLS_DIR := .tools +TOOLS_BIN := $(TOOLS_DIR)/bin +CONFIG := $(shell pwd) + +# tools +GO_TOOLS_BIN := $(addprefix $(TOOLS_BIN)/, $(notdir $(GO_TOOLS))) +GO_TOOLS_VENDOR := $(addprefix vendor/, $(GO_TOOLS)) +GOLANGCI_LINT := $(TOOLS_BIN)/golangci-lint +GOLANGCI_LINT_VERSION := v1.53.2 + +# helper functions +godeps=$(shell go list -deps -f '{{if not .Standard}}{{ $$dep := . }}{{range .GoFiles}}{{$$dep.Dir}}/{{.}} {{end}}{{end}}' $(1) | sed "s%${PWD}/%%g") +WWCTL_DEPS:=$(call godeps,cmd/wwctl/main.go) +WWCLIENT_DEPS:=$(call godeps,cmd/wwclient/main.go) + +# use GOPROXY for older git clients and speed up downloads +GOPROXY ?= https://proxy.golang.org +export GOPROXY + +# built tags needed for wwbuild binary +WW_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree From a8a68e47adce41068ca34a50c1146a29d72700a1 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 01:33:02 -0600 Subject: [PATCH 31/75] Change Warewulf version to 4.5.x Future releases from the development branch will be 4.5.x. Additional 4.4.x versions would be tagged from the 4.4.x branch. Signed-off-by: Jonathon Anderson --- Variables.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Variables.mk b/Variables.mk index 29ef1924..43e28ff6 100644 --- a/Variables.mk +++ b/Variables.mk @@ -10,7 +10,7 @@ VARLIST := OS # Project Information VARLIST += WAREWULF VERSION RELEASE WAREWULF ?= warewulf -VERSION ?= 4.4.0 +VERSION ?= 4.5.x GIT_TAG := $(shell test -e .git && git log -1 --format="%h") ifdef GIT_TAG From 315b78bcce77def5b582bf99ee86e031e8307141 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 01:37:19 -0600 Subject: [PATCH 32/75] Remove echo and hidden commands from Makefile Signed-off-by: Jonathon Anderson --- Makefile | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index ea64242a..958e700a 100644 --- a/Makefile +++ b/Makefile @@ -13,11 +13,11 @@ setup_tools: $(GO_TOOLS_BIN) $(GOLANGCI_LINT) # install go tools into TOOLS_BIN $(GO_TOOLS_BIN): - @GOBIN="$(PWD)/$(TOOLS_BIN)" go install -mod=vendor $(GO_TOOLS) + GOBIN="$(PWD)/$(TOOLS_BIN)" go install -mod=vendor $(GO_TOOLS) # install golangci-lint into TOOLS_BIN $(GOLANGCI_LINT): - @curl -qq -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TOOLS_BIN) $(GOLANGCI_LINT_VERSION) + curl -qq -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TOOLS_BIN) $(GOLANGCI_LINT_VERSION) setup: vendor $(TOOLS_DIR) setup_tools @@ -28,7 +28,7 @@ ifndef OFFLINE_BUILD endif $(TOOLS_DIR): - @mkdir -p $@ + mkdir -p $@ # Pre-build steps for source, such as "go generate" config: @@ -46,8 +46,7 @@ genconfig: rm_config config # Lint lint: setup_tools - @echo Running golangci-lint... - @$(GOLANGCI_LINT) run --build-tags "$(WW_GO_BUILD_TAGS)" --skip-dirs internal/pkg/staticfiles ./... + $(GOLANGCI_LINT) run --build-tags "$(WW_GO_BUILD_TAGS)" --skip-dirs internal/pkg/staticfiles ./... vet: go vet ./... @@ -119,21 +118,18 @@ init: restorecon -r $(WWTFTPDIR) wwctl: config vendor $(WWCTL_DEPS) - @echo Building "$@" - @cd cmd/wwctl; GOOS=linux go build -mod vendor -tags "$(WW_GO_BUILD_TAGS)" \ + cd cmd/wwctl; GOOS=linux go build -mod vendor -tags "$(WW_GO_BUILD_TAGS)" \ -o ../../wwctl wwclient: config vendor $(WWCLIENT_DEPS) - @echo Building "$@" - @cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags "-extldflags -static" \ + cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags "-extldflags -static" \ -o ../../wwclient man_pages: wwctl - @install -d man_pages - @./wwctl --emptyconf genconfig man man_pages - @cp docs/man/man5/*.5 ./man_pages/ - @echo -n "Compressing manpage: " - @cd man_pages; for i in wwctl*1 *.5; do gzip --force $$i; echo -n "$$i "; done; echo + install -d man_pages + ./wwctl --emptyconf genconfig man man_pages + cp docs/man/man5/*.5 ./man_pages/ + cd man_pages; for i in wwctl*1 *.5; do gzip --force $$i; echo -n "$$i "; done; echo update_configuration: vendor cmd/update_configuration/update_configuration.go cd cmd/update_configuration && go build \ From 64fd43bb8694578ff668e510309c87d21693e7b7 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 03:52:24 -0600 Subject: [PATCH 33/75] Apply PHONY and remove extraneous comments Several of the targets in Makefile don't create a file of their name, and PHONY identifies these targets to make. Alongside, several comments in Makefile don't provide additional details beyond what is self-documented by the command or target name itself. Signed-off-by: Jonathon Anderson --- Makefile | 60 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 958e700a..8a71dddc 100644 --- a/Makefile +++ b/Makefile @@ -1,24 +1,21 @@ -.PHONY: all clean contclean - include Variables.mk -# Default target +.PHONY: all all: config vendor wwctl wwclient man_pages wwapid wwapic wwapird -# Validate source and build all packages +.PHONY: build build: lint test-it vet all -# set the go tools into the tools bin. +.PHONY: setup_tools setup_tools: $(GO_TOOLS_BIN) $(GOLANGCI_LINT) -# install go tools into TOOLS_BIN $(GO_TOOLS_BIN): GOBIN="$(PWD)/$(TOOLS_BIN)" go install -mod=vendor $(GO_TOOLS) -# install golangci-lint into TOOLS_BIN $(GOLANGCI_LINT): curl -qq -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TOOLS_BIN) $(GOLANGCI_LINT_VERSION) +.PHONY: setup setup: vendor $(TOOLS_DIR) setup_tools vendor: @@ -30,32 +27,33 @@ endif $(TOOLS_DIR): mkdir -p $@ -# Pre-build steps for source, such as "go generate" config: -# Store configuration for subsequent runs printf " $(foreach V,$(VARLIST),$V := $(strip $($V))\n)" > Defaults.mk - # Global variable search and replace for all *.in files find . -type f -name "*.in" -not -path "./vendor/*" \ -exec sh -c 'sed -ne "$(foreach V,$(VARLIST),s,@$V@,$(strip $($V)),g;)p" $${0} > $${0%.in}' {} \; touch config +.PHONY: rm_config rm_config: rm -f config +.PHONY: genconfig genconfig: rm_config config -# Lint +.PHONY: lint lint: setup_tools $(GOLANGCI_LINT) run --build-tags "$(WW_GO_BUILD_TAGS)" --skip-dirs internal/pkg/staticfiles ./... +.PHONY: vet vet: go vet ./... +.PHONY: test-it test-it: go test -v ./... -# Generate test coverage -test-cover: ## Run test coverage and generate html report +.PHONY: test-cover +test-cover: rm -fr coverage mkdir coverage go list -f '{{if gt (len .TestGoFiles) 0}}"go test -covermode count -coverprofile {{.Name}}.coverprofile -coverpkg ./... {{.ImportPath}}"{{end}}' ./... | xargs -I {} bash -c {} @@ -64,8 +62,10 @@ test-cover: ## Run test coverage and generate html report rm *.coverprofile go tool cover -html=coverage/cover.out -o=coverage/cover.html +.PHONY: debian debian: all +.PHONY: files files: all install -d -m 0755 $(DESTDIR)$(BINDIR) install -d -m 0755 $(DESTDIR)$(WWCHROOTDIR) @@ -112,6 +112,7 @@ files: all install -m 0644 staticfiles/x86_64.efi $(DESTDIR)$(WWDATADIR)/ipxe install -m 0644 staticfiles/x86_64.kpxe $(DESTDIR)$(WWDATADIR)/ipxe +.PHONY: init init: systemctl daemon-reload cp -r tftpboot/* $(WWTFTPDIR)/ipxe/ @@ -136,6 +137,7 @@ update_configuration: vendor cmd/update_configuration/update_configuration.go -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\ -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o ../../update_configuration +.PHONY: dist dist: vendor config rm -rf .dist/$(WAREWULF)-$(VERSION) $(WAREWULF)-$(VERSION).tar.gz mkdir -p .dist/$(WAREWULF)-$(VERSION) @@ -143,6 +145,7 @@ dist: vendor config cd .dist; tar -czf ../$(WAREWULF)-$(VERSION).tar.gz $(WAREWULF)-$(VERSION) rm -rf .dist +.PHONY: reference reference: wwctl mkdir -p userdocs/reference ./wwctl --emptyconf genconfig reference userdocs/reference/ @@ -150,16 +153,17 @@ reference: wwctl latexpdf: reference make -C userdocs latexpdf -## 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 +# 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 +.PHONY: proto proto: protoc -I /usr/include -I internal/pkg/api/routes/v1 -I=. \ --grpc-gateway_out=. \ @@ -168,15 +172,16 @@ proto: --go-grpc_out=. \ routes.proto -wwapid: ## Build the grpc api server. +wwapid: go build -o ./wwapid internal/app/api/wwapid/wwapid.go -wwapic: ## Build the sample wwapi client. +wwapic: go build -o ./wwapic internal/app/api/wwapic/wwapic.go -wwapird: ## Build the rest api server (revese proxy to the grpc api server). +wwapird: go build -o ./wwapird internal/app/api/wwapird/wwapird.go +.PHONY: contclean contclean: rm -f $(WAREWULF)-$(VERSION).tar.gz rm -f bash_completion @@ -209,9 +214,12 @@ contclean: rm -rf userdocs/_* rm -rf userdocs/reference/* +.PHONY: clean clean: contclean rm -rf vendor +.PHONY: install install: files +.PHONY: debinstall debinstall: files debfiles From c1d56885ac2606f2cf7f265260e25d22b1530365 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 11:28:49 -0600 Subject: [PATCH 34/75] Refactor Makefile config / Defaults.mk Makefile has included a Defaults.mk file to store variable configuration from one run for implicit inclusion in future runs, but the creation of this file has been automatic and opaque, and the state of it managed by a series of PHONY targets (e.g., config, genconfig, rm_config). This commit refactors config to name its files explicitly (so they can be more properly monitored by make) and makes the generation of Defaults.mk explicit so that its behavior is more obvious. Signed-off-by: Jonathon Anderson --- Dockerfile | 3 +-- Makefile | 25 ++++++++----------- Variables.mk | 3 +++ .../development-environment-vagrant.rst | 2 +- userdocs/quickstart/el8.rst | 2 +- userdocs/quickstart/el9.rst | 2 +- userdocs/quickstart/suse15.rst | 2 +- warewulf.spec.in | 5 ++-- 8 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Dockerfile b/Dockerfile index dfffc58e..616c294d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,8 +7,7 @@ RUN zypper -n install --no-recommends git go1.18 libgpgme-devel &&\ COPY . /warewulf-src RUN cd /warewulf-src &&\ - make contclean &&\ - make genconfig \ + make contclean Defaults.mk \ PREFIX=/usr \ BINDIRa=/usr/bin \ SYSCONFDIR=/etc \ diff --git a/Makefile b/Makefile index 8a71dddc..667a522f 100644 --- a/Makefile +++ b/Makefile @@ -27,18 +27,16 @@ endif $(TOOLS_DIR): mkdir -p $@ -config: - printf " $(foreach V,$(VARLIST),$V := $(strip $($V))\n)" > Defaults.mk - find . -type f -name "*.in" -not -path "./vendor/*" \ - -exec sh -c 'sed -ne "$(foreach V,$(VARLIST),s,@$V@,$(strip $($V)),g;)p" $${0} > $${0%.in}' {} \; - touch config +.PHONY: config +config: etc/wwapic.conf \ + etc/wwapid.conf \ + etc/wwapird.conf \ + include/systemd/warewulfd.service \ + internal/pkg/config/buildconfig.go \ + warewulf.spec -.PHONY: rm_config -rm_config: - rm -f config - -.PHONY: genconfig -genconfig: rm_config config +%: %.in + sed -ne "$(foreach V,$(VARLIST),s,@$V@,$(strip $($V)),g;)p" $@.in >$@ .PHONY: lint lint: setup_tools @@ -183,11 +181,10 @@ wwapird: .PHONY: contclean contclean: + rm -f Defaults.mk rm -f $(WAREWULF)-$(VERSION).tar.gz rm -f bash_completion - rm -f config - rm -f config_defaults - rm -f Defaults.mk + rm -f config_defaults rm -f etc/wwapi{c,d,rd}.conf rm -f etc/wwapi{c,d,rd}.config rm -f include/systemd/warewulfd.service diff --git a/Variables.mk b/Variables.mk index 43e28ff6..aa22945a 100644 --- a/Variables.mk +++ b/Variables.mk @@ -97,3 +97,6 @@ export GOPROXY # built tags needed for wwbuild binary WW_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree + +Defaults.mk: + printf " $(foreach V,$(VARLIST),$V := $(strip $($V))\n)" >Defaults.mk diff --git a/userdocs/contributing/development-environment-vagrant.rst b/userdocs/contributing/development-environment-vagrant.rst index 8b3f0ba4..e437f080 100644 --- a/userdocs/contributing/development-environment-vagrant.rst +++ b/userdocs/contributing/development-environment-vagrant.rst @@ -158,7 +158,7 @@ Vagrantfile git clone https://github.com/hpcng/warewulf.git cd warewulf git checkout v4.4.0 - make genconfig \ + make clean Defaults.mk \ PREFIX=/usr \ BINDIR=/usr/bin \ SYSCONFDIR=/etc \ diff --git a/userdocs/quickstart/el8.rst b/userdocs/quickstart/el8.rst index b2ab3772..4e2e0510 100644 --- a/userdocs/quickstart/el8.rst +++ b/userdocs/quickstart/el8.rst @@ -13,7 +13,7 @@ Install Warewulf and dependencies git clone https://github.com/hpcng/warewulf.git cd warewulf - make genconfig \ + make clean Defaults.mk \ PREFIX=/usr \ BINDIR=/usr/bin \ SYSCONFDIR=/etc \ diff --git a/userdocs/quickstart/el9.rst b/userdocs/quickstart/el9.rst index 575dc2b3..0ad66d77 100644 --- a/userdocs/quickstart/el9.rst +++ b/userdocs/quickstart/el9.rst @@ -14,7 +14,7 @@ Install Warewulf and dependencies git clone https://github.com/hpcng/warewulf.git cd warewulf - make genconfig \ + make clean Defaults.mk \ PREFIX=/usr \ BINDIR=/usr/bin \ SYSCONFDIR=/etc \ diff --git a/userdocs/quickstart/suse15.rst b/userdocs/quickstart/suse15.rst index 7c0c58e2..ad6f8786 100644 --- a/userdocs/quickstart/suse15.rst +++ b/userdocs/quickstart/suse15.rst @@ -16,7 +16,7 @@ Install Warewulf and dependencies git clone https://github.com/hpcng/warewulf.git cd warewulf - PREFIX=/usr SYSCONFDIR=/etc TFTPDIR=/srv/tftproot LOCALSTATEDIR=/var/lib make genconfig + PREFIX=/usr SYSCONFDIR=/etc TFTPDIR=/srv/tftproot LOCALSTATEDIR=/var/lib make clean Defaults.mk make all sudo make install diff --git a/warewulf.spec.in b/warewulf.spec.in index f0208f83..8a6b15bc 100644 --- a/warewulf.spec.in +++ b/warewulf.spec.in @@ -71,7 +71,7 @@ system for large clusters of bare metal and/or virtual systems. %build %{?with_offline:OFFLINE_BUILD=1} # Install to sharedstatedir by redirecting LOCALSTATEDIR -make genconfig \ +make Defaults.mk \ PREFIX=%{_prefix} \ BINDIR=%{_bindir} \ SYSCONFDIR=%{_sysconfdir} \ @@ -92,7 +92,8 @@ make %install export NO_BRP_STALE_LINK_ERROR=yes -make install DESTDIR=%{buildroot} +make install \ + DESTDIR=%{buildroot} %pre From 3ef88f8fb0ddc9d4f54b6590aa2754f1cf04fb25 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 11:30:24 -0600 Subject: [PATCH 35/75] Collapse files target into install Signed-off-by: Jonathon Anderson --- Makefile | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 667a522f..bcd62210 100644 --- a/Makefile +++ b/Makefile @@ -63,8 +63,8 @@ test-cover: .PHONY: debian debian: all -.PHONY: files -files: all +.PHONY: install +install: all install -d -m 0755 $(DESTDIR)$(BINDIR) install -d -m 0755 $(DESTDIR)$(WWCHROOTDIR) install -d -m 0755 $(DESTDIR)$(WWPROVISIONDIR) @@ -215,8 +215,5 @@ contclean: clean: contclean rm -rf vendor -.PHONY: install -install: files - .PHONY: debinstall debinstall: files debfiles From 88001ecfb0f40f9449831e3644766764d12b5381 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 11:30:45 -0600 Subject: [PATCH 36/75] Remove extraneous whitespace Signed-off-by: Jonathon Anderson --- Makefile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index bcd62210..a6571d37 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,8 @@ setup: vendor $(TOOLS_DIR) setup_tools vendor: ifndef OFFLINE_BUILD - go mod tidy -v - go mod vendor + go mod tidy -v + go mod vendor endif $(TOOLS_DIR): @@ -126,7 +126,7 @@ wwclient: config vendor $(WWCLIENT_DEPS) man_pages: wwctl install -d man_pages - ./wwctl --emptyconf genconfig man man_pages + ./wwctl --emptyconf genconfig man man_pages cp docs/man/man5/*.5 ./man_pages/ cd man_pages; for i in wwctl*1 *.5; do gzip --force $$i; echo -n "$$i "; done; echo @@ -162,7 +162,7 @@ latexpdf: reference # sudo ldconfig # refresh shared library cache. # To setup protoc-gen-grpc-gateway, see https://github.com/grpc-ecosystem/grpc-gateway .PHONY: proto -proto: +proto: protoc -I /usr/include -I internal/pkg/api/routes/v1 -I=. \ --grpc-gateway_out=. \ --grpc-gateway_opt logtostderr=true \ @@ -190,14 +190,14 @@ contclean: rm -f include/systemd/warewulfd.service rm -f internal/pkg/buildconfig/setconfigs.go rm -f internal/pkg/config/buildconfig.go - rm -f man_page - rm -f print_defaults + rm -f man_page + rm -f print_defaults rm -f update_configuration rm -f usr/share/man/man1/ rm -f warewulf.spec rm -f warewulf-*.tar.gz - rm -f wwapic - rm -f wwapid + rm -f wwapic + rm -f wwapid rm -f wwapird rm -f wwclient rm -f wwctl From f375ff32cb1798acc22f33a083b8efcc405ce64b Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 11:30:57 -0600 Subject: [PATCH 37/75] Remove unused debian targets from Makefile Signed-off-by: Jonathon Anderson --- Makefile | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Makefile b/Makefile index a6571d37..e22d21fe 100644 --- a/Makefile +++ b/Makefile @@ -60,9 +60,6 @@ test-cover: rm *.coverprofile go tool cover -html=coverage/cover.out -o=coverage/cover.html -.PHONY: debian -debian: all - .PHONY: install install: all install -d -m 0755 $(DESTDIR)$(BINDIR) @@ -214,6 +211,3 @@ contclean: .PHONY: clean clean: contclean rm -rf vendor - -.PHONY: debinstall -debinstall: files debfiles From 1fdd3742c345ac283cd6c6822b4601cd325b4d1b Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 06:56:45 -0600 Subject: [PATCH 38/75] Improve the generation of man pages Signed-off-by: Jonathon Anderson --- .gitignore | 5 ++--- Makefile | 18 ++++++++---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 7859c0c0..2510fccf 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ /wwclient /wwctl /bash_completion -/man_page /config_defaults /update_configuration /wwapic @@ -19,9 +18,9 @@ /print_defaults # other created files -/man_pages +/docs/man/man1/*.1.gz +/docs/man/man5/*.5.gz /bash_completion.d -/usr/share/man/man1/ /etc/bash_completion.d/ warewulf.spec internal/pkg/buildconfig/setconfigs.go diff --git a/Makefile b/Makefile index e22d21fe..d5eab3c0 100644 --- a/Makefile +++ b/Makefile @@ -100,8 +100,8 @@ install: all install -m 0644 include/systemd/warewulfd.service $(DESTDIR)$(SYSTEMDDIR) install -m 0644 LICENSE.md $(DESTDIR)$(WWDOCDIR) ./wwctl --warewulfconf etc/warewulf.conf genconfig completions > $(DESTDIR)$(BASHCOMPDIR)/wwctl - cp man_pages/*.1* $(DESTDIR)$(MANDIR)/man1/ - cp man_pages/*.5* $(DESTDIR)$(MANDIR)/man5/ + for f in docs/man/man1/*.1.gz; do install -m 0644 $$f $(DESTDIR)$(MANDIR)/man1/; done + for f in docs/man/man5/*.5.gz; do install -m 0644 $$f $(DESTDIR)$(MANDIR)/man5/; done install -m 0644 staticfiles/README-ipxe.md $(DESTDIR)$(WWDATADIR)/ipxe install -m 0644 staticfiles/arm64.efi $(DESTDIR)$(WWDATADIR)/ipxe install -m 0644 staticfiles/x86_64.efi $(DESTDIR)$(WWDATADIR)/ipxe @@ -121,11 +121,12 @@ wwclient: config vendor $(WWCLIENT_DEPS) cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags "-extldflags -static" \ -o ../../wwclient -man_pages: wwctl - install -d man_pages - ./wwctl --emptyconf genconfig man man_pages - cp docs/man/man5/*.5 ./man_pages/ - cd man_pages; for i in wwctl*1 *.5; do gzip --force $$i; echo -n "$$i "; done; echo +.PHONY: man_pages +man_pages: wwctl $(wildcard docs/man/man5/*.5) + mkdir -p docs/man/man1 + ./wwctl --emptyconf genconfig man docs/man/man1 + gzip --force docs/man/man1/*.1 + gzip --force --keep docs/man/man5/*.5 update_configuration: vendor cmd/update_configuration/update_configuration.go cd cmd/update_configuration && go build \ @@ -187,10 +188,8 @@ contclean: rm -f include/systemd/warewulfd.service rm -f internal/pkg/buildconfig/setconfigs.go rm -f internal/pkg/config/buildconfig.go - rm -f man_page rm -f print_defaults rm -f update_configuration - rm -f usr/share/man/man1/ rm -f warewulf.spec rm -f warewulf-*.tar.gz rm -f wwapic @@ -204,7 +203,6 @@ contclean: rm -rf .dist/ rm -rf _dist/ rm -rf etc/bash_completion.d/ - rm -rf man_pages rm -rf userdocs/_* rm -rf userdocs/reference/* From 7e31e45a0f57ecf7a47ac8eed07142a5f0c2f065 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 06:57:58 -0600 Subject: [PATCH 39/75] Ignore extracted dists When testing manual mock builds, built dists are extracted in the current directory. This commit ignores those extracted dists so they are not erroneously committed to the repository. Signed-off-by: Jonathon Anderson --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2510fccf..315c804c 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ Defaults.mk .dist/ userdocs/_* userdocs/reference/* +/warewulf-?.?.?/ From ec15f0fe362c3128722340b2ae6fbd7a215b2ee8 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 07:18:36 -0600 Subject: [PATCH 40/75] Generate defaults.conf explicitly in Makefile Signed-off-by: Jonathon Anderson --- .gitignore | 1 + Makefile | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 315c804c..51b19d86 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ Defaults.mk /etc/wwapid.conf /etc/wwapic.conf /etc/wwapird.conf +/etc/defaults.conf .dist/ userdocs/_* userdocs/reference/* diff --git a/Makefile b/Makefile index d5eab3c0..b4d6f86d 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ include Variables.mk .PHONY: all -all: config vendor wwctl wwclient man_pages wwapid wwapic wwapird +all: config vendor wwctl wwclient man_pages wwapid wwapic wwapird etc/defaults.conf .PHONY: build build: lint test-it vet all @@ -35,6 +35,9 @@ config: etc/wwapic.conf \ internal/pkg/config/buildconfig.go \ warewulf.spec +etc/defaults.conf: wwctl + ./wwctl --emptyconf genconfig defaults >etc/defaults.conf + %: %.in sed -ne "$(foreach V,$(VARLIST),s,@$V@,$(strip $($V)),g;)p" $@.in >$@ @@ -79,7 +82,7 @@ install: all test -f $(DESTDIR)$(WWCONFIGDIR)/wwapic.conf || install -m 644 etc/wwapic.conf $(DESTDIR)$(WWCONFIGDIR) test -f $(DESTDIR)$(WWCONFIGDIR)/wwapid.conf || install -m 644 etc/wwapid.conf $(DESTDIR)$(WWCONFIGDIR) test -f $(DESTDIR)$(WWCONFIGDIR)/wwapird.conf || install -m 644 etc/wwapird.conf $(DESTDIR)$(WWCONFIGDIR) - test -f $(DESTDIR)$(DATADIR)/warewulf/defaults.conf || ./wwctl --emptyconf genconfig defaults > $(DESTDIR)$(DATADIR)/warewulf/defaults.conf + test -f $(DESTDIR)$(DATADIR)/warewulf/defaults.conf || install -m 644 etc/defaults.conf $(DESTDIR)$(DATADIR)/warewulf/defaults.conf cp -r etc/examples $(DESTDIR)$(WWCONFIGDIR)/ cp -r etc/ipxe $(DESTDIR)$(WWCONFIGDIR)/ cp -r overlays/* $(DESTDIR)$(WWOVERLAYDIR)/ From bf3995d6f5bd1678b8476726e3e96e90f1bb7ae9 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 07:24:35 -0600 Subject: [PATCH 41/75] Clean up test targets in Makefile Most notably, replaces the test-it target with a more simply named test target. Signed-off-by: Jonathon Anderson --- .github/workflows/test.yml | 2 +- Makefile | 14 +++++++------- userdocs/contributing/debugging.rst | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bc829b3d..e582f30d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,4 +41,4 @@ jobs: run: make vet - name: test - run: make test-it + run: make test diff --git a/Makefile b/Makefile index b4d6f86d..d336a82c 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ include Variables.mk all: config vendor wwctl wwclient man_pages wwapid wwapic wwapird etc/defaults.conf .PHONY: build -build: lint test-it vet all +build: lint test vet all .PHONY: setup_tools setup_tools: $(GO_TOOLS_BIN) $(GOLANGCI_LINT) @@ -49,17 +49,17 @@ lint: setup_tools vet: go vet ./... -.PHONY: test-it -test-it: - go test -v ./... +.PHONY: test +test: + go test ./... .PHONY: test-cover test-cover: - rm -fr coverage + rm -rf coverage mkdir coverage go list -f '{{if gt (len .TestGoFiles) 0}}"go test -covermode count -coverprofile {{.Name}}.coverprofile -coverpkg ./... {{.ImportPath}}"{{end}}' ./... | xargs -I {} bash -c {} - echo "mode: count" > coverage/cover.out - grep -h -v "^mode:" *.coverprofile >> "coverage/cover.out" + echo "mode: count" >coverage/cover.out + grep -h -v "^mode:" *.coverprofile >>"coverage/cover.out" rm *.coverprofile go tool cover -html=coverage/cover.out -o=coverage/cover.html diff --git a/userdocs/contributing/debugging.rst b/userdocs/contributing/debugging.rst index cf1c256d..d32affe0 100644 --- a/userdocs/contributing/debugging.rst +++ b/userdocs/contributing/debugging.rst @@ -21,12 +21,12 @@ vet`` on the full codebase. Running the full test suite =========================== -The Warewulf ``Makefile`` includes a ``test-it`` target which runs the +The Warewulf ``Makefile`` includes a ``test`` target which runs the full test suite. .. code-block:: console - $ make test-it + $ make test Using delve From 081ead8c9b84b0ec52aad2dedc09c519912aed3f Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 11:04:52 -0600 Subject: [PATCH 42/75] Use zero-led octals for all install permissions Signed-off-by: Jonathon Anderson --- Makefile | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index d336a82c..09407ec5 100644 --- a/Makefile +++ b/Makefile @@ -77,23 +77,23 @@ install: all install -d -m 0755 $(DESTDIR)$(FIREWALLDDIR) install -d -m 0755 $(DESTDIR)$(SYSTEMDDIR) 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)$(WWCONFIGDIR)/wwapic.conf || install -m 644 etc/wwapic.conf $(DESTDIR)$(WWCONFIGDIR) - test -f $(DESTDIR)$(WWCONFIGDIR)/wwapid.conf || install -m 644 etc/wwapid.conf $(DESTDIR)$(WWCONFIGDIR) - test -f $(DESTDIR)$(WWCONFIGDIR)/wwapird.conf || install -m 644 etc/wwapird.conf $(DESTDIR)$(WWCONFIGDIR) - test -f $(DESTDIR)$(DATADIR)/warewulf/defaults.conf || install -m 644 etc/defaults.conf $(DESTDIR)$(DATADIR)/warewulf/defaults.conf + test -f $(DESTDIR)$(WWCONFIGDIR)/warewulf.conf || install -m 0644 etc/warewulf.conf $(DESTDIR)$(WWCONFIGDIR) + test -f $(DESTDIR)$(WWCONFIGDIR)/nodes.conf || install -m 0644 etc/nodes.conf $(DESTDIR)$(WWCONFIGDIR) + test -f $(DESTDIR)$(WWCONFIGDIR)/wwapic.conf || install -m 0644 etc/wwapic.conf $(DESTDIR)$(WWCONFIGDIR) + test -f $(DESTDIR)$(WWCONFIGDIR)/wwapid.conf || install -m 0644 etc/wwapid.conf $(DESTDIR)$(WWCONFIGDIR) + test -f $(DESTDIR)$(WWCONFIGDIR)/wwapird.conf || install -m 0644 etc/wwapird.conf $(DESTDIR)$(WWCONFIGDIR) + test -f $(DESTDIR)$(DATADIR)/warewulf/defaults.conf || install -m 0644 etc/defaults.conf $(DESTDIR)$(DATADIR)/warewulf/defaults.conf cp -r etc/examples $(DESTDIR)$(WWCONFIGDIR)/ cp -r etc/ipxe $(DESTDIR)$(WWCONFIGDIR)/ cp -r overlays/* $(DESTDIR)$(WWOVERLAYDIR)/ - chmod 755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/init + chmod 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/init find $(DESTDIR)$(WWOVERLAYDIR) -type f -name "*.in" -exec rm -f {} \; - chmod 755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/$(WWCLIENTDIR)/wwinit - chmod 600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/etc/ssh/ssh* - chmod 600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/etc/NetworkManager/system-connections/ww4-managed.ww - chmod 644 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/etc/ssh/ssh*.pub.ww - chmod 600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/warewulf/config.ww - chmod 750 $(DESTDIR)$(WWOVERLAYDIR)/host + chmod 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/$(WWCLIENTDIR)/wwinit + chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/etc/ssh/ssh* + chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/etc/NetworkManager/system-connections/ww4-managed.ww + chmod 0644 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/etc/ssh/ssh*.pub.ww + chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/warewulf/config.ww + chmod 0750 $(DESTDIR)$(WWOVERLAYDIR)/host install -m 0755 wwctl $(DESTDIR)$(BINDIR) install -m 0755 wwclient $(DESTDIR)$(WWOVERLAYDIR)/wwinit/$(WWCLIENTDIR)/wwclient install -m 0755 wwapic $(DESTDIR)$(BINDIR) From 1c3b566d43f2d2f7c72d3451d5e8635bfc764032 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 11:05:58 -0600 Subject: [PATCH 43/75] Test targets depend on config Signed-off-by: Jonathon Anderson --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 09407ec5..47a91a37 100644 --- a/Makefile +++ b/Makefile @@ -46,15 +46,15 @@ lint: setup_tools $(GOLANGCI_LINT) run --build-tags "$(WW_GO_BUILD_TAGS)" --skip-dirs internal/pkg/staticfiles ./... .PHONY: vet -vet: +vet: config go vet ./... .PHONY: test -test: +test: config go test ./... .PHONY: test-cover -test-cover: +test-cover: config rm -rf coverage mkdir coverage go list -f '{{if gt (len .TestGoFiles) 0}}"go test -covermode count -coverprofile {{.Name}}.coverprofile -coverpkg ./... {{.ImportPath}}"{{end}}' ./... | xargs -I {} bash -c {} From daab6075d7144856befd2c1fcad3c1eddcd474fa Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 08:02:24 -0600 Subject: [PATCH 44/75] Remove extraneous rm from clean Makefile target Signed-off-by: Jonathon Anderson --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 47a91a37..bc2b4ab0 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,6 @@ install: all cp -r etc/ipxe $(DESTDIR)$(WWCONFIGDIR)/ cp -r overlays/* $(DESTDIR)$(WWOVERLAYDIR)/ chmod 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/init - find $(DESTDIR)$(WWOVERLAYDIR) -type f -name "*.in" -exec rm -f {} \; chmod 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/$(WWCLIENTDIR)/wwinit chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/etc/ssh/ssh* chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/etc/NetworkManager/system-connections/ww4-managed.ww From 0b9702a7d340b6165ffbbf40fe036ecc48a8f790 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 11:08:56 -0600 Subject: [PATCH 45/75] Separate out bash completions in Makefile Signed-off-by: Jonathon Anderson --- Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index bc2b4ab0..d80b75b4 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ include Variables.mk .PHONY: all -all: config vendor wwctl wwclient man_pages wwapid wwapic wwapird etc/defaults.conf +all: config vendor wwctl wwclient man_pages wwapid wwapic wwapird etc/defaults.conf etc/bash_completion.d/wwctl .PHONY: build build: lint test vet all @@ -101,7 +101,7 @@ install: all 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) - ./wwctl --warewulfconf etc/warewulf.conf genconfig completions > $(DESTDIR)$(BASHCOMPDIR)/wwctl + install -m 0644 etc/bash_completion.d/wwctl $(DESTDIR)$(BASHCOMPDIR)/wwctl for f in docs/man/man1/*.1.gz; do install -m 0644 $$f $(DESTDIR)$(MANDIR)/man1/; done for f in docs/man/man5/*.5.gz; do install -m 0644 $$f $(DESTDIR)$(MANDIR)/man5/; done install -m 0644 staticfiles/README-ipxe.md $(DESTDIR)$(WWDATADIR)/ipxe @@ -109,6 +109,10 @@ install: all install -m 0644 staticfiles/x86_64.efi $(DESTDIR)$(WWDATADIR)/ipxe install -m 0644 staticfiles/x86_64.kpxe $(DESTDIR)$(WWDATADIR)/ipxe +etc/bash_completion.d/wwctl: wwctl + mkdir -p etc/bash_completion.d/ + ./wwctl --warewulfconf etc/warewulf.conf genconfig completions >etc/bash_completion.d/wwctl + .PHONY: init init: systemctl daemon-reload From 5ff899293e2022d239e073cca81bec63cb14a4f3 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 11:11:11 -0600 Subject: [PATCH 46/75] Manage examples, ipxe, and overlays with install Signed-off-by: Jonathon Anderson --- Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index d80b75b4..81212ff2 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,7 @@ install: all install -d -m 0755 $(DESTDIR)$(WWCHROOTDIR) install -d -m 0755 $(DESTDIR)$(WWPROVISIONDIR) install -d -m 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/$(WWCLIENTDIR) + install -d -m 0755 $(DESTDIR)$(WWCONFIGDIR)/examples install -d -m 0755 $(DESTDIR)$(WWCONFIGDIR)/ipxe install -d -m 0755 $(DESTDIR)$(BASHCOMPDIR) install -d -m 0755 $(DESTDIR)$(MANDIR)/man1 @@ -83,9 +84,9 @@ install: all test -f $(DESTDIR)$(WWCONFIGDIR)/wwapid.conf || install -m 0644 etc/wwapid.conf $(DESTDIR)$(WWCONFIGDIR) test -f $(DESTDIR)$(WWCONFIGDIR)/wwapird.conf || install -m 0644 etc/wwapird.conf $(DESTDIR)$(WWCONFIGDIR) test -f $(DESTDIR)$(DATADIR)/warewulf/defaults.conf || install -m 0644 etc/defaults.conf $(DESTDIR)$(DATADIR)/warewulf/defaults.conf - cp -r etc/examples $(DESTDIR)$(WWCONFIGDIR)/ - cp -r etc/ipxe $(DESTDIR)$(WWCONFIGDIR)/ - cp -r overlays/* $(DESTDIR)$(WWOVERLAYDIR)/ + for f in etc/examples/*.ww; do install -m 0644 $$f $(DESTDIR)$(WWCONFIGDIR)/examples/; done + for f in etc/ipxe/*.ipxe; do install -m 0644 $$f $(DESTDIR)$(WWCONFIGDIR)/ipxe/; done + (cd overlays && find * -type f -exec install -D -m 0644 {} $(DESTDIR)$(WWOVERLAYDIR)/{} \;) chmod 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/init chmod 0755 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/$(WWCLIENTDIR)/wwinit chmod 0600 $(DESTDIR)$(WWOVERLAYDIR)/wwinit/etc/ssh/ssh* From 0561c3009ac0be047ba2a33d8110d2e648d29ce6 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 09:11:53 -0600 Subject: [PATCH 47/75] Improve specification of command builds Signed-off-by: Jonathon Anderson --- Makefile | 35 ++++++++++++++++------------------- Variables.mk | 2 -- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index 81212ff2..6ed02448 100644 --- a/Makefile +++ b/Makefile @@ -120,13 +120,11 @@ init: cp -r tftpboot/* $(WWTFTPDIR)/ipxe/ restorecon -r $(WWTFTPDIR) -wwctl: config vendor $(WWCTL_DEPS) - cd cmd/wwctl; GOOS=linux go build -mod vendor -tags "$(WW_GO_BUILD_TAGS)" \ - -o ../../wwctl +wwctl: config vendor $(call godeps,cmd/wwctl/main.go) + GOOS=linux go build -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o wwctl cmd/wwctl/main.go -wwclient: config vendor $(WWCLIENT_DEPS) - cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags "-extldflags -static" \ - -o ../../wwclient +wwclient: config vendor $(call godeps,cmd/wwclient/main.go) + CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags "-extldflags -static" -o wwclient cmd/wwclient/main.go .PHONY: man_pages man_pages: wwctl $(wildcard docs/man/man5/*.5) @@ -135,10 +133,18 @@ man_pages: wwctl $(wildcard docs/man/man5/*.5) gzip --force docs/man/man1/*.1 gzip --force --keep docs/man/man5/*.5 -update_configuration: vendor cmd/update_configuration/update_configuration.go - cd cmd/update_configuration && go build \ - -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\ - -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o ../../update_configuration +update_configuration: vendor $(call godeps,cmd/update_configuration/update_configuration.go) + go build -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'" \ + -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o update_configuration cmd/update_configuration/update_configuration.go + +wwapid: $(call godeps,internal/app/api/wwapid/wwapid.go) + go build -o ./wwapid internal/app/api/wwapid/wwapid.go + +wwapic: $(call godeps,internal/app/api/wwapic/wwapic.go) + go build -o ./wwapic internal/app/api/wwapic/wwapic.go + +wwapird: $(call godeps,internal/app/api/wwapird/wwapird.go) + go build -o ./wwapird internal/app/api/wwapird/wwapird.go .PHONY: dist dist: vendor config @@ -175,15 +181,6 @@ proto: --go-grpc_out=. \ routes.proto -wwapid: - go build -o ./wwapid internal/app/api/wwapid/wwapid.go - -wwapic: - go build -o ./wwapic internal/app/api/wwapic/wwapic.go - -wwapird: - go build -o ./wwapird internal/app/api/wwapird/wwapird.go - .PHONY: contclean contclean: rm -f Defaults.mk diff --git a/Variables.mk b/Variables.mk index aa22945a..7195d0d3 100644 --- a/Variables.mk +++ b/Variables.mk @@ -88,8 +88,6 @@ GOLANGCI_LINT_VERSION := v1.53.2 # helper functions godeps=$(shell go list -deps -f '{{if not .Standard}}{{ $$dep := . }}{{range .GoFiles}}{{$$dep.Dir}}/{{.}} {{end}}{{end}}' $(1) | sed "s%${PWD}/%%g") -WWCTL_DEPS:=$(call godeps,cmd/wwctl/main.go) -WWCLIENT_DEPS:=$(call godeps,cmd/wwclient/main.go) # use GOPROXY for older git clients and speed up downloads GOPROXY ?= https://proxy.golang.org From d2b63ecb750fae1afd0a003ccf0f548a475d391f Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 11:16:03 -0600 Subject: [PATCH 48/75] Simplier dist generation prep Signed-off-by: Jonathon Anderson --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6ed02448..11e577b4 100644 --- a/Makefile +++ b/Makefile @@ -148,7 +148,7 @@ wwapird: $(call godeps,internal/app/api/wwapird/wwapird.go) .PHONY: dist dist: vendor config - rm -rf .dist/$(WAREWULF)-$(VERSION) $(WAREWULF)-$(VERSION).tar.gz + rm -rf .dist/ $(WAREWULF)-$(VERSION).tar.gz mkdir -p .dist/$(WAREWULF)-$(VERSION) rsync -a --exclude=".*" --exclude "*~" * .dist/$(WAREWULF)-$(VERSION)/ cd .dist; tar -czf ../$(WAREWULF)-$(VERSION).tar.gz $(WAREWULF)-$(VERSION) From 9ea3381b9d4a1658fbdefcc9720e5a25810aa05a Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 11:16:18 -0600 Subject: [PATCH 49/75] Remove extraneous backup file Signed-off-by: Jonathon Anderson --- .../vers42/nodes.conf.bak | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 cmd/update_configuration/vers42/nodes.conf.bak diff --git a/cmd/update_configuration/vers42/nodes.conf.bak b/cmd/update_configuration/vers42/nodes.conf.bak deleted file mode 100644 index 792e48a2..00000000 --- a/cmd/update_configuration/vers42/nodes.conf.bak +++ /dev/null @@ -1,22 +0,0 @@ -nodeprofiles: - default: - comment: This profile is automatically included for each node - runtime overlay: "generic" - discoverable: false - leap: - comment: openSUSE leap - kernel version: "5.14.21" - ipmi netmask: "255.255.255.0" - keys: - foo: baar - network devices: - lan1: - gateway: 1.1.1.1 -nodes: - node01: - system overlay: "nodeoverlay" - discoverable: true - network devices: - eth0: - ipaddr: 1.2.3.4 - default: true From b3b759179647f9fc560881e0ec4c114c979bfa74 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 09:42:18 -0600 Subject: [PATCH 50/75] Audit Makefile clean target and gitignore Signed-off-by: Jonathon Anderson --- .gitignore | 14 +------------- Makefile | 15 ++------------- 2 files changed, 3 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 51b19d86..55581c1c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,34 +5,22 @@ /vendor # binaries -/warewulfd -/wwbuild /wwclient /wwctl -/bash_completion -/config_defaults /update_configuration /wwapic /wwapid /wwapird -/print_defaults # other created files -/docs/man/man1/*.1.gz +/docs/man/man1 /docs/man/man5/*.5.gz -/bash_completion.d /etc/bash_completion.d/ warewulf.spec -internal/pkg/buildconfig/setconfigs.go internal/pkg/config/buildconfig.go include/systemd/warewulfd.service -_dist/ -/config warewulf-*.tar.gz Defaults.mk -/etc/wwapid.config -/etc/wwapic.config -/etc/wwapird.config /etc/wwapid.conf /etc/wwapic.conf /etc/wwapird.conf diff --git a/Makefile b/Makefile index 11e577b4..6a65a2df 100644 --- a/Makefile +++ b/Makefile @@ -185,30 +185,19 @@ proto: contclean: rm -f Defaults.mk rm -f $(WAREWULF)-$(VERSION).tar.gz - rm -f bash_completion - rm -f config_defaults - rm -f etc/wwapi{c,d,rd}.conf - rm -f etc/wwapi{c,d,rd}.config + rm -f wwapi{c,d,rd} etc/wwapi{c,d,rd}.conf rm -f include/systemd/warewulfd.service - rm -f internal/pkg/buildconfig/setconfigs.go rm -f internal/pkg/config/buildconfig.go - rm -f print_defaults rm -f update_configuration rm -f warewulf.spec - rm -f warewulf-*.tar.gz - rm -f wwapic - rm -f wwapid - rm -f wwapird rm -f wwclient rm -f wwctl rm -rf $(TOOLS_DIR) - rm -rf bash_completion.d - rm -rf /config rm -rf .dist/ - rm -rf _dist/ rm -rf etc/bash_completion.d/ rm -rf userdocs/_* rm -rf userdocs/reference/* + rm -rf etc/defaults.conf .PHONY: clean clean: contclean From b6c774c4b56e11ec46f6c6d03cee95e1bfdcd0a7 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 11:20:52 -0600 Subject: [PATCH 51/75] Use no config file when generating completions Signed-off-by: Jonathon Anderson --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6a65a2df..7b763551 100644 --- a/Makefile +++ b/Makefile @@ -112,7 +112,7 @@ install: all etc/bash_completion.d/wwctl: wwctl mkdir -p etc/bash_completion.d/ - ./wwctl --warewulfconf etc/warewulf.conf genconfig completions >etc/bash_completion.d/wwctl + ./wwctl --emptyconf genconfig completions >etc/bash_completion.d/wwctl .PHONY: init init: From 526c96fb5ec519fa37ba2bf12156ae210bfa9322 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Thu, 10 Aug 2023 11:45:10 -0600 Subject: [PATCH 52/75] Update changelog to reflect Makefile changes Signed-off-by: Jonathon Anderson --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index df7cede1..e9530dbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Check for formal correct IP and MAC addresses for command line options and when reading in the configurations - Write log messages to stderr rather than stdout. #768 +- Updates to Makefile for clarity, notably removing genconfig and replacing + test-it with test. #890 ## [4.4.0] 2023-01-18 From 9a453c17022f7c3318226051f01d0e69bcd572d9 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Fri, 11 Aug 2023 16:15:33 -0600 Subject: [PATCH 53/75] Capture proto dependency installs as targets Signed-off-by: Jonathon Anderson --- Makefile | 29 ++++++++++++++++------------- Variables.mk | 8 ++++++++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 7b763551..3b51f42a 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ all: config vendor wwctl wwclient man_pages wwapid wwapic wwapird etc/defaults.c build: lint test vet all .PHONY: setup_tools -setup_tools: $(GO_TOOLS_BIN) $(GOLANGCI_LINT) +setup_tools: $(GO_TOOLS_BIN) $(GOLANGCI_LINT) $(PROTOC_GEN_GO) $(PROTOC_GEN_GO_GRPC) $(GO_TOOLS_BIN): GOBIN="$(PWD)/$(TOOLS_BIN)" go install -mod=vendor $(GO_TOOLS) @@ -15,6 +15,19 @@ $(GO_TOOLS_BIN): $(GOLANGCI_LINT): curl -qq -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TOOLS_BIN) $(GOLANGCI_LINT_VERSION) +$(PROTOC): + cd $(PWD)/$(TOOLS_DIR) && curl -LO $(PROTOC_URL) && unzip protoc-24.0-linux-aarch_64.zip + +$(PROTOC_GEN_GRPC_GATEWAY): + curl -L $(PROTOC_GEN_GRPC_GATEWAY_URL) -o $(PROTOC_GEN_GRPC_GATEWAY) + chmod +x $(PROTOC_GEN_GRPC_GATEWAY) + +$(PROTOC_GEN_GO): + GOBIN="$(PWD)/$(TOOLS_BIN)" go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 + +$(PROTOC_GEN_GO_GRPC): + GOBIN="$(PWD)/$(TOOLS_BIN)" go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 + .PHONY: setup setup: vendor $(TOOLS_DIR) setup_tools @@ -162,19 +175,9 @@ reference: wwctl latexpdf: reference make -C userdocs latexpdf -# 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 .PHONY: proto -proto: - protoc -I /usr/include -I internal/pkg/api/routes/v1 -I=. \ +proto: $(PROTOC) $(PROTOC_GEN_GRPC_GATEWAY) $(PROTOC_GEN_GO) $(PROTOC_GEN_GO_GRPC) + PATH=$(TOOLS_BIN):$(PATH) $(PROTOC) -I /usr/include -I internal/pkg/api/routes/v1 -I=. \ --grpc-gateway_out=. \ --grpc-gateway_opt logtostderr=true \ --go_out=. \ diff --git a/Variables.mk b/Variables.mk index 7195d0d3..9e650460 100644 --- a/Variables.mk +++ b/Variables.mk @@ -85,6 +85,14 @@ GO_TOOLS_BIN := $(addprefix $(TOOLS_BIN)/, $(notdir $(GO_TOOLS))) GO_TOOLS_VENDOR := $(addprefix vendor/, $(GO_TOOLS)) GOLANGCI_LINT := $(TOOLS_BIN)/golangci-lint GOLANGCI_LINT_VERSION := v1.53.2 +PROTOC_GEN_GO := $(TOOLS_BIN)/protoc-gen-go +PROTOC_GEN_GO_GRPC := $(TOOLS_BIN)/protoc-gen-go-grpc +PROTOC := $(TOOLS_BIN)/protoc +PROTOC_URL := https://github.com/protocolbuffers/protobuf/releases/download/v24.0/protoc-24.0-linux-x86_64.zip +#PROTOC_URL := https://github.com/protocolbuffers/protobuf/releases/download/v24.0/protoc-24.0-linux-aarch_64.zip +PROTOC_GEN_GRPC_GATEWAY := $(TOOLS_BIN)/protoc-gen-grpc-gateway +PROTOC_GEN_GRPC_GATEWAY_URL := https://github.com/grpc-ecosystem/grpc-gateway/releases/download/v2.16.2/protoc-gen-grpc-gateway-v2.16.2-linux-x86_64 +#PROTOC_GEN_GRPC_GATEWAY_URL := https://github.com/grpc-ecosystem/grpc-gateway/releases/download/v2.16.2/protoc-gen-grpc-gateway-v2.16.2-linux-arm64 # helper functions godeps=$(shell go list -deps -f '{{if not .Standard}}{{ $$dep := . }}{{range .GoFiles}}{{$$dep.Dir}}/{{.}} {{end}}{{end}}' $(1) | sed "s%${PWD}/%%g") From 320c92e55eece1fe97c0d4c381323bfbaa524193 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Mon, 14 Aug 2023 11:38:16 +0200 Subject: [PATCH 54/75] added example for host specific files Signed-off-by: Christian Goll --- userdocs/contents/overlays.rst | 2 +- userdocs/contents/templating.rst | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/userdocs/contents/overlays.rst b/userdocs/contents/overlays.rst index 84f6acd0..cd1fe5be 100644 --- a/userdocs/contents/overlays.rst +++ b/userdocs/contents/overlays.rst @@ -143,7 +143,7 @@ permissions, etc. .. note:: - There is now possibility to delete files with an overlay! [example needed] + There is no possibility to delete files with an overlay! Build ----- diff --git a/userdocs/contents/templating.rst b/userdocs/contents/templating.rst index 692adcbc..98114144 100644 --- a/userdocs/contents/templating.rst +++ b/userdocs/contents/templating.rst @@ -211,3 +211,17 @@ A given string can be split into substrings. {{ $x := "a:b:c" -}} {{ $y := (split $x ":") -}} {{ range $y }} {{.}} {{ end }} + + +Node specific files +------------------- + +Sometimes there is the need to have specific files for every node +which can't be generated by a template. You can include these files +with following template: + +.. code-block:: go + + {{- $filename := print "/root/" .Id "-payload" }} + {{ Include $filename }} + From ede1a9149e3fab27ecf4a05fc8d688a6ac598676 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Tue, 15 Aug 2023 17:16:08 +0200 Subject: [PATCH 55/75] remove and ignore *.coverprofile Signed-off-by: Christian Goll --- .gitignore | 1 + Makefile | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 55581c1c..42e1c380 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ Defaults.mk userdocs/_* userdocs/reference/* /warewulf-?.?.?/ +*.coverprofile \ No newline at end of file diff --git a/Makefile b/Makefile index 3b51f42a..0bbe2a59 100644 --- a/Makefile +++ b/Makefile @@ -201,6 +201,7 @@ contclean: rm -rf userdocs/_* rm -rf userdocs/reference/* rm -rf etc/defaults.conf + rm -rf *.coverprofile .PHONY: clean clean: contclean From 4d50f053d7619d4c344eef6a469cc1479ccf20a9 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 11 Aug 2023 07:38:13 -0600 Subject: [PATCH 56/75] Minor test fixes Signed-off-by: Christian Goll --- internal/pkg/node/transformer_test.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/pkg/node/transformer_test.go b/internal/pkg/node/transformer_test.go index 204b44a4..3c642e50 100644 --- a/internal/pkg/node/transformer_test.go +++ b/internal/pkg/node/transformer_test.go @@ -137,7 +137,7 @@ func Test_nodeYaml_SetFrom(t *testing.T) { t.Run("Get() default network mask", func(t *testing.T) { value := test_node1.NetDevs["net0"].Netmask.Get() if value != "255.255.255.0" { - t.Errorf("Get() returned wrong default netmask: %s", value) + t.Errorf("Get() returned wrong default netmask, got: %s want: 255.255.255.0", value) } }) t.Run("Get() default network mask", func(t *testing.T) { @@ -310,8 +310,7 @@ ipmi: {} }) t.Run("Set() netdev foo with device name baar for flattened empty node", func(t *testing.T) { - netdev := NewNetDevEntry() - test_node4.NetDevs["foo"] = &netdev + test_node4.NetDevs["foo"] = new(NetDevEntry) test_node4.NetDevs["foo"].Device.Set("baar") nodeConf := NewConf() nodeConf.GetFrom(test_node4) @@ -324,7 +323,8 @@ ipmi: {} if !(wanted == string(ymlByte)) { t.Errorf("Got wrong yml, wanted:\n'%s'\nGot:\n'%s'", wanted, string(ymlByte)) } - test_node4.NetDevs["foo"].Tags["netfoo"] = &Entry{} + test_node4.NetDevs["foo"].Tags = make(map[string]*Entry) + test_node4.NetDevs["foo"].Tags["netfoo"] = new(Entry) test_node4.NetDevs["foo"].Tags["netfoo"].Set("netbaar") nodeConf.GetFrom(test_node4) nodeConf.Flatten() @@ -336,10 +336,11 @@ ipmi: {} ` ymlByte, _ = yaml.Marshal(nodeConf) if string(ymlByte) != wanted { - t.Errorf("Couldn set nettag: '%s' got: '%s'", wanted, string(ymlByte)) + t.Errorf("Couldn't set nettag: '%s' got: '%s'", wanted, string(ymlByte)) } delete(test_node4.NetDevs, "foo") + nodeConf = NewConf() nodeConf.GetFrom(test_node4) nodeConf.Flatten() ymlByte, _ = yaml.Marshal(nodeConf) From f2bc70ad00300df9b4e0d33167cc1adb97f450ac Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 11 Aug 2023 03:40:54 -0600 Subject: [PATCH 57/75] Add structures for devices and filesystems Signed-off-by: Christian Goll --- internal/pkg/node/datastructure.go | 105 ++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 25 deletions(-) diff --git a/internal/pkg/node/datastructure.go b/internal/pkg/node/datastructure.go index ab22d3c8..b342f8f9 100644 --- a/internal/pkg/node/datastructure.go +++ b/internal/pkg/node/datastructure.go @@ -35,20 +35,22 @@ type NodeConf struct { IpmiInterface string `yaml:"ipmi interface,omitempty"` IpmiWrite string `yaml:"ipmi write,omitempty"` // Deprecated end - RuntimeOverlay []string `yaml:"runtime overlay,omitempty" lopt:"runtime" sopt:"R" comment:"Set the runtime overlay"` - SystemOverlay []string `yaml:"system overlay,omitempty" lopt:"wwinit" sopt:"O" comment:"Set the system overlay"` - Kernel *KernelConf `yaml:"kernel,omitempty"` - Ipmi *IpmiConf `yaml:"ipmi,omitempty"` - Init string `yaml:"init,omitempty" lopt:"init" sopt:"i" comment:"Define the init process to boot the container"` - Root string `yaml:"root,omitempty" lopt:"root" comment:"Define the rootfs" ` - AssetKey string `yaml:"asset key,omitempty" lopt:"asset" comment:"Set the node's Asset tag (key)"` - Discoverable string `yaml:"discoverable,omitempty" lopt:"discoverable" sopt:"e" comment:"Make discoverable in given network (true/false)" type:"bool"` - Profiles []string `yaml:"profiles,omitempty" lopt:"profile" sopt:"P" comment:"Set the node's profile members (comma separated)"` - NetDevs map[string]*NetDevs `yaml:"network devices,omitempty"` - Tags map[string]string `yaml:"tags,omitempty" lopt:"tagadd" comment:"base key"` - TagsDel []string `yaml:"tagsdel,omitempty" lopt:"tagdel" comment:"remove this tags"` // should not go to disk only to wire - Keys map[string]string `yaml:"keys,omitempty"` // Reverse compatibility - PrimaryNetDev string `yaml:"primary network,omitempty" lopt:"primarynet" sopt:"p" comment:"Set the primary network interface"` + RuntimeOverlay []string `yaml:"runtime overlay,omitempty" lopt:"runtime" sopt:"R" comment:"Set the runtime overlay"` + SystemOverlay []string `yaml:"system overlay,omitempty" lopt:"wwinit" sopt:"O" comment:"Set the system overlay"` + Kernel *KernelConf `yaml:"kernel,omitempty"` + Ipmi *IpmiConf `yaml:"ipmi,omitempty"` + Init string `yaml:"init,omitempty" lopt:"init" sopt:"i" comment:"Define the init process to boot the container"` + Root string `yaml:"root,omitempty" lopt:"root" comment:"Define the rootfs" ` + AssetKey string `yaml:"asset key,omitempty" lopt:"asset" comment:"Set the node's Asset tag (key)"` + Discoverable string `yaml:"discoverable,omitempty" lopt:"discoverable" sopt:"e" comment:"Make discoverable in given network (true/false)" type:"bool"` + Profiles []string `yaml:"profiles,omitempty" lopt:"profile" sopt:"P" comment:"Set the node's profile members (comma separated)"` + NetDevs map[string]*NetDevs `yaml:"network devices,omitempty"` + Tags map[string]string `yaml:"tags,omitempty" lopt:"tagadd" comment:"base key"` + TagsDel []string `yaml:"tagsdel,omitempty" lopt:"tagdel" comment:"remove this tags"` // should not go to disk only to wire + Keys map[string]string `yaml:"keys,omitempty"` // Reverse compatibility + PrimaryNetDev string `yaml:"primary network,omitempty" lopt:"primarynet" sopt:"p" comment:"Set the primary network interface"` + Disks map[string]*Disk `yaml:"disks,omitempty"` + FileSystems map[string]*FileSystem `yaml:"filesystems,omitempty"` } type IpmiConf struct { @@ -87,6 +89,42 @@ type NetDevs struct { TagsDel []string `yaml:"tagsdel,omitempty" lopt:"nettagdel" comment:"delete network tags"` // should not go to disk only to wire } +/* +Holds the disks of a node +*/ +type Disk struct { + WipeTable string `yaml:"wipe_table,omitempty" type:"bool" lopt:"diskwipe" comment:"whether or not the partition tables shall be wiped"` + Partitions map[string]*Partition `yaml:"partitions,omitempty"` +} + +/* +partition definition, the label must be uniq so its used as the key in the +Partitions map +*/ +type Partition struct { + Number string `yaml:"number,omitempty" lopt:"partnumber" comment:"set the partition number, if not set next free slot is used"` + SizeMiB string `yaml:"size_mib,omitempty" lopt:"partsize" comment:"set the size of the partition, if not set maximal possible size is used"` + StartMiB string `yaml:"start_mib,omitempty" comment:"the start of the partition"` + TypeGuid string `yaml:"type_guid,omitempty" comment:"Linux filesystem data will be used if empty"` + Guid string `yaml:"guid,omitempty" comment:"the GPT unique partition GUID"` + WipePartitionEntry string `yaml:"wipe_partition_entry,omitempty" comment:"if true, Ignition will clobber an existing partition if it does not match the config" type:"bool"` + ShouldExist string `yaml:"should_exist,omitempty" lopt:"partcreate" comment:"create partition if not exist" type:"bool"` + Resize string `yaml:"resize,omitempty" comment:" whether or not the existing partition should be resize" type:"bool"` +} + +/* +Definition of a filesystem. The device is uniq so its used as key +*/ +type FileSystem struct { + Format string `yaml:"format,omitempty" lopt:"fsformat" comment:"format of the file system"` + Path string `yaml:"path,omitempty" lopt:"fspath" comment:"the mount point of the file system"` + WipeFileSystem string `yaml:"wipe_filesystem,omitempty" lopt:"fswipe" comment:"wipe file system at boot" type:"bool"` + Label string `yaml:"label,omitempty" comment:"the label of the filesystem"` + Uuid string `yaml:"uuid,omitempty" comment:"the uuid of the filesystem"` + Options []string `yaml:"options,omitempty" comment:"any additional options to be passed to the format-specific mkfs utility"` + MountOptions []string `yaml:"mount_options,omitempty" comment:"any special options to be passed to the mount command"` +} + /****** * Internal code data representations ******/ @@ -126,6 +164,8 @@ type NodeInfo struct { PrimaryNetDev Entry NetDevs map[string]*NetDevEntry Tags map[string]*Entry + Disks map[string]*DiskEntry + FileSystems map[string]*FileSystemEntry } type IpmiEntry struct { @@ -161,17 +201,32 @@ type NetDevEntry struct { Tags map[string]*Entry } +type DiskEntry struct { + WipeTable Entry + Partitions map[string]*PartitionEntry +} + +type PartitionEntry struct { + Number Entry + SizeMiB Entry + StartMiB Entry + TypeGuid Entry + Guid Entry + WipePartitionEntry Entry + ShouldExist Entry + Resize Entry +} + +type FileSystemEntry struct { + Format Entry + Path Entry + WipeFileSystem Entry + Label Entry + Uuid Entry + Options Entry + MountOptions Entry +} + // string which is printed if no value is set const NoValue = "--" -/* -Has no real purpose as only New() needs it -func init() { - // Check that nodes.conf is found - if !util.IsFile(ConfigFile) { - wwlog.Warn("Missing node configuration file") - // just return silently, as init is also called for bash_completion - return - } -} -*/ From fdd233a25ffb6e7fbf2544998ea18e4e8a2d9bf2 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 11 Aug 2023 08:00:00 -0600 Subject: [PATCH 58/75] Add recursive getter functions When transforming a NodeConf struct to a NodeInfo (get the NodeInfo from the Nodconf) nested structures were seperately handled. This means there were seperate handles for e.g *Netdevs and *IpmiConfig. The new getter traverses through the datastucture and sets the right value for an Entry struct, but calls itself again if a Pointer is detected. This adds the posiblity to add new nested structures to NodeConf and NodeInfo without the need of seperate handling them in the transformer functions. Signed-off-by: Christian Goll --- internal/pkg/node/transformer_test.go | 3 +- internal/pkg/node/transformers.go | 226 +++++++------------------- 2 files changed, 63 insertions(+), 166 deletions(-) diff --git a/internal/pkg/node/transformer_test.go b/internal/pkg/node/transformer_test.go index 3c642e50..0f8746fe 100644 --- a/internal/pkg/node/transformer_test.go +++ b/internal/pkg/node/transformer_test.go @@ -299,13 +299,14 @@ ipmi: {} t.Errorf("Got wrong yml, wanted:\n'%s'\nGot:\n'%s'", wanted, string(ymlByte)) } delete(test_node4.Tags, "foo") + nodeConf = NewConf() nodeConf.GetFrom(test_node4) nodeConf.Flatten() ymlByte, _ = yaml.Marshal(nodeConf) wanted = `{} ` if string(ymlByte) != wanted { - t.Errorf("Couldn't remove tag'%s'", string(ymlByte)) + t.Errorf("Couldn't remove tag, wanted:\n%s\nGot:\n%s", wanted, string(ymlByte)) } }) diff --git a/internal/pkg/node/transformers.go b/internal/pkg/node/transformers.go index 9eceac5d..66d24328 100644 --- a/internal/pkg/node/transformers.go +++ b/internal/pkg/node/transformers.go @@ -1,6 +1,7 @@ package node import ( + "fmt" "reflect" "strings" @@ -15,7 +16,7 @@ the underlying entries using GetReal, so just the explicit values go do disk. */ func (nodeConf *NodeConf) GetRealFrom(nodeInfo NodeInfo) { - nodeConf.getterFrom(nodeInfo, (*Entry).GetReal, (*Entry).GetRealSlice) + recursiveGetter(&nodeInfo, nodeConf, (*Entry).GetReal, (*Entry).GetRealSlice) } /* @@ -23,168 +24,67 @@ Populates a NodeConf struct from a NodeInfo, with the combined values from the underlying entries using Get. */ func (nodeConf *NodeConf) GetFrom(nodeInfo NodeInfo) { - nodeConf.getterFrom(nodeInfo, (*Entry).Get, (*Entry).GetSlice) + recursiveGetter(&nodeInfo, nodeConf, (*Entry).Get, (*Entry).GetSlice) } /* Abstract function which populates a NodeConf from the given NodeInfo -via getter functions. +via getter functions. Calls recursive itself for nested structures. +Panics if the NodeConf has fields which are not type of string,[]string,map[string]*ptr */ -func (nodeConf *NodeConf) getterFrom(nodeInfo NodeInfo, +func recursiveGetter( + source, target interface{}, getter func(*Entry) string, getterSlice func(*Entry) []string) { - nodeInfoType := reflect.TypeOf(nodeInfo) - nodeInfoVal := reflect.ValueOf(nodeInfo) - configVal := reflect.ValueOf(nodeConf) - // now iterate of every field - for i := 0; i < nodeInfoType.NumField(); i++ { - // found field with same name for Conf and Info - confField := configVal.Elem().FieldByName(nodeInfoType.Field(i).Name) - if confField.IsValid() { - if nodeInfoVal.Field(i).Type() == reflect.TypeOf(Entry{}) { - if confField.Type().Kind() == reflect.String { - newValue := (confField.Addr().Interface()).(*string) - entryVal := nodeInfoVal.Field(i).Interface().(Entry) - *newValue = getter(&entryVal) - } else if confField.Type() == reflect.TypeOf([]string{}) { - newValue := (confField.Addr().Interface()).(*[]string) - entryVal := nodeInfoVal.Field(i).Interface().(Entry) - *newValue = getterSlice(&entryVal) - } - } else if nodeInfoVal.Field(i).Type() == reflect.TypeOf(map[string]*Entry{}) { - entryMap := nodeInfoVal.Field(i).Interface().(map[string]*Entry) - confMap := confField.Interface().(map[string]string) + sourceValue := reflect.ValueOf(source) + targetType := reflect.TypeOf(target) + targetValue := reflect.ValueOf(target) + if targetValue.Elem().Kind() == reflect.Struct && sourceValue.Elem().Kind() == reflect.Struct { + for i := 0; i < targetType.Elem().NumField(); i++ { + sourceValueMatched := sourceValue.Elem().FieldByName(targetType.Elem().Field(i).Name) + if sourceValueMatched.IsValid() { + if sourceValueMatched.Type() == reflect.TypeOf(Entry{}) { + // get the fields which are part of the struct + switch targetValue.Elem().Field(i).Type() { + case reflect.TypeOf(""): + newValue := (targetValue.Elem().Field(i).Addr().Interface()).(*string) + source := sourceValueMatched.Interface().(Entry) + *newValue = getter(&source) + case reflect.TypeOf([]string{}): + newValue := (targetValue.Elem().Field(i).Addr().Interface()).(*[]string) + source := sourceValueMatched.Interface().(Entry) + *newValue = getterSlice(&source) + default: + panic(fmt.Errorf("can't convert an Entry to %s", targetValue.Elem().Field(i).Type())) + } + } else if sourceValueMatched.Kind() == reflect.Ptr { + // if we get a pointer, initialize if empty and then have a recursive call + if targetValue.Elem().Field(i).IsZero() { + targetValue.Elem().Field(i).Set(reflect.New(targetType.Elem().Field(i).Type.Elem())) + } + recursiveGetter(sourceValueMatched.Interface(), targetValue.Elem().Field(i).Interface(), getter, getterSlice) + } else if sourceValueMatched.Type().Kind() == reflect.Map { + if targetValue.Elem().Field(i).IsZero() { + targetValue.Elem().Field(i).Set(reflect.MakeMap(targetType.Elem().Field(i).Type)) + } + sourceIter := sourceValueMatched.MapRange() + if sourceValueMatched.Type() == reflect.TypeOf(map[string]*Entry{}) { + // go over a simple map with strings + for sourceIter.Next() { + if !targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).IsValid() { + str := getter((sourceIter.Value().Interface()).(*Entry)) + targetValue.Elem().Field(i).SetMapIndex(sourceIter.Key(), reflect.ValueOf(str)) + } + } + } else { + // now the complicated map which contains pointers to objects + for sourceIter.Next() { + if !targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).IsValid() { + newPtr := reflect.New(targetType.Elem().Field(i).Type.Elem().Elem()) + targetValue.Elem().Field(i).SetMapIndex(sourceIter.Key(), newPtr) + } + recursiveGetter(sourceIter.Value().Interface(), targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).Interface(), getter, getterSlice) - if len(confMap) > len(entryMap) { - for confKey := range confMap { - foundKey := false - for entrKey := range entryMap { - if confKey == entrKey { - foundKey = true - } - } - if !foundKey { - delete(confMap, confKey) - } - } - } - for key, val := range entryMap { - confMap[key] = getter(val) - } - } else if nodeInfoVal.Field(i).Type().Kind() == reflect.Ptr && !nodeInfoVal.Field(i).IsNil() { - // initialize the nested NodeConf structs, but only if these will be set - if confField.Addr().Elem().IsZero() { - switch confField.Addr().Elem().Type() { - case reflect.TypeOf((*KernelConf)(nil)): - var newConf KernelConf - newConfPtr := (confField.Addr().Elem().Addr().Interface()).(**KernelConf) - *newConfPtr = &newConf - case reflect.TypeOf((*IpmiConf)(nil)): - var newConf IpmiConf - newConfPtr := (confField.Addr().Elem().Addr().Interface()).(**IpmiConf) - *newConfPtr = &newConf - } - } - nestedInfoType := reflect.TypeOf(nodeInfoVal.Field(i).Interface()) - nestedInfoVal := reflect.ValueOf(nodeInfoVal.Field(i).Interface()) - nestedConfVal := reflect.ValueOf(confField.Interface()) - for j := 0; j < nestedInfoType.Elem().NumField(); j++ { - nestedVal := nestedConfVal.Elem().FieldByName(nestedInfoType.Elem().Field(j).Name) - if nestedInfoVal.Elem().Field(j).Type() == reflect.TypeOf(Entry{}) { - if nestedVal.Type().Kind() == reflect.String { - newValue := (nestedVal.Addr().Interface()).(*string) - entryVal := nestedInfoVal.Elem().Field(j).Interface().(Entry) - *newValue = getter(&entryVal) - } else if nestedVal.Type() == reflect.TypeOf([]string{}) { - newValue := (nestedVal.Addr().Interface()).(*[]string) - entryVal := nestedInfoVal.Elem().Field(j).Interface().(Entry) - *newValue = getterSlice(&entryVal) - - } - } else if nestedInfoVal.Elem().Field(j).Type() == reflect.TypeOf(map[string]*Entry{}) { - if nestedVal.IsNil() { - mapPtr := nestedVal.Addr().Interface().(*map[string]string) - *mapPtr = make(map[string]string) - } - entryMap := nestedInfoVal.Elem().Field(j).Interface().(map[string]*Entry) - confMap := nestedVal.Interface().(map[string]string) - if len(confMap) > len(entryMap) { - for confKey := range confMap { - foundKey := false - for entrKey := range entryMap { - if confKey == entrKey { - foundKey = true - } - } - if !foundKey { - delete(confMap, confKey) - } - } - } - for key, val := range entryMap { - confMap[key] = getter(val) - } - } - } - - } else if nodeInfoVal.Field(i).Type() == reflect.TypeOf(map[string]*NetDevEntry{}) { - if confField.IsNil() { - netMapPtr := confField.Addr().Interface().(*map[string](*NetDevs)) - *netMapPtr = make(map[string](*NetDevs)) - } - nestedMap := nodeInfoVal.Field(i).Interface().(map[string]*NetDevEntry) - netMap := confField.Interface().(map[string](*NetDevs)) - // check if a network was deleted - if len(netMap) > len(nestedMap) { - for netMapKey := range netMap { - foundKey := false - for nestedMapKey := range nestedMap { - if netMapKey == nestedMapKey { - foundKey = true - } - } - if !foundKey { - delete(netMap, netMapKey) - } - } - } - for netName, netVal := range nestedMap { - netValsType := reflect.ValueOf(netVal) - if _, ok := netMap[netName]; !ok { - netMap[netName] = new(NetDevs) - } - netConfType := reflect.TypeOf(*netMap[netName]) - netConfVal := reflect.ValueOf(netMap[netName]) - for j := 0; j < netConfType.NumField(); j++ { - netVal := netValsType.Elem().FieldByName(netConfType.Field(j).Name) - if netVal.IsValid() { - if netVal.Type() == reflect.TypeOf(Entry{}) { - newVal := netConfVal.Elem().Field(j).Addr().Interface().((*string)) - *newVal = getter((netVal.Addr().Interface()).(*Entry)) - } else if netVal.Type() == reflect.TypeOf(map[string]*Entry{}) { - entryMap := netVal.Interface().(map[string](*Entry)) - confMap := netConfVal.Elem().Field(j).Interface().(map[string]string) - if confMap == nil { - confMapPtr := netConfVal.Elem().Field(j).Addr().Interface().(*map[string]string) - *confMapPtr = make(map[string]string) - } - if len(confMap) > len(entryMap) { - for confMapKey := range confMap { - foundKey := false - for entryMapKey := range entryMap { - if confMapKey == entryMapKey { - foundKey = true - } - } - if !foundKey { - delete(netConfVal.Elem().Field(j).Interface().(map[string]string), confMapKey) - } - } - } - for key, val := range entryMap { - netConfVal.Elem().Field(j).Interface().(map[string]string)[key] = getter(val) - } - } } } } @@ -350,8 +250,11 @@ or *KernelConf, these pointer will set to nil. This will remove something like ipmi: {} from nodes.conf */ func (info *NodeConf) Flatten() { - confType := reflect.TypeOf(info) - confVal := reflect.ValueOf(info) + recursiveFlatten(info) +} +func recursiveFlatten(strct interface{}) { + confType := reflect.TypeOf(strct) + confVal := reflect.ValueOf(strct) for j := 0; j < confType.Elem().NumField(); j++ { if confVal.Elem().Field(j).Type().Kind() == reflect.Ptr && !confVal.Elem().Field(j).IsNil() { // iterate now over the ptr fields @@ -371,14 +274,7 @@ func (info *NodeConf) Flatten() { } } if setToNil { - switch confType.Elem().Field(j).Type { - case reflect.TypeOf((*IpmiConf)(nil)): - ptr := confVal.Elem().Field(j).Addr().Interface().(**IpmiConf) - *ptr = (*IpmiConf)(nil) - case reflect.TypeOf((*KernelConf)(nil)): - ptr := confVal.Elem().Field(j).Addr().Interface().(**KernelConf) - *ptr = (*KernelConf)(nil) - } + confVal.Elem().Field(j).Set(reflect.Zero(confVal.Elem().Field(j).Type())) } } } From c55c5a2ac41ee7486281de065c34a06ebbc94316 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 11 Aug 2023 09:44:28 -0600 Subject: [PATCH 59/75] Add recursive setter functions when transforming a NodeInfo struct to a NodeConf (set the NodeConf from the NodeInfo) nested structures were seperately handled. This means a there were seperate handles for e.g *Netdevs and *IpmiConfig. The new getter traverses through the datastucture and sets the right value for a Entry struct, but calls itself again if a Pointer is detected. This adds the posiblity to add new nested structures to NodeConf and NodeInfo without the need of seperately handling them in the transformer functions. Signed-off-by: Christian Goll --- internal/pkg/node/constructors.go | 2 +- internal/pkg/node/methods.go | 5 - internal/pkg/node/modifiers.go | 14 +- internal/pkg/node/transformers.go | 247 +++++++++--------------------- 4 files changed, 88 insertions(+), 180 deletions(-) diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 612eacbd..d3d3bc1c 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -166,7 +166,7 @@ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) { n.SetFrom(node) // only now the netdevs start to exist so that default values can be set for _, netdev := range n.NetDevs { - netdev.SetDefFrom(defConfNet) + SetDefFrom(defConfNet, netdev) } // backward compatibility n.Ipmi.Ipaddr.Set(node.IpmiIpaddr) diff --git a/internal/pkg/node/methods.go b/internal/pkg/node/methods.go index 0d60254f..b92bb361 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -380,11 +380,6 @@ func NewInfo() (nodeInfo NodeInfo) { return nodeInfo } -func NewNetDevEntry() (netdev NetDevEntry) { - netdev.Tags = make(map[string]*Entry) - return -} - /* Get a entry by its name */ diff --git a/internal/pkg/node/modifiers.go b/internal/pkg/node/modifiers.go index 361e2365..455ea514 100644 --- a/internal/pkg/node/modifiers.go +++ b/internal/pkg/node/modifiers.go @@ -48,12 +48,19 @@ func (config *NodeYaml) DelNode(nodeID string) error { return nil } +/* +update the node in the database +*/ func (config *NodeYaml) NodeUpdate(node NodeInfo) error { nodeID := node.Id.Get() - + wwlog.Debug("updating node %s: %v", nodeID, node) if _, ok := config.Nodes[nodeID]; !ok { return errors.New("Nodename does not exist: " + nodeID) } + // maps may have deleted elements which will not be updated + // so we delete the node and call GetRealFrom on an empty NodeConv + delete(config.Nodes, nodeID) + config.Nodes[nodeID] = new(NodeConf) config.Nodes[nodeID].GetRealFrom(node) return nil } @@ -102,6 +109,10 @@ func (config *NodeYaml) ProfileUpdate(profile NodeInfo) error { if _, ok := config.NodeProfiles[profileID]; !ok { return errors.New("Profile name does not exist: " + profileID) } + // maps may have deleted elements which will not be updated + // so we delete the profile and call GetRealFrom on an empty NodeConv + delete(config.NodeProfiles, profileID) + config.NodeProfiles[profileID] = new(NodeConf) config.NodeProfiles[profileID].GetRealFrom(profile) return nil } @@ -133,7 +144,6 @@ func (config *NodeYaml) Persist() error { return nil } - // Dump returns a YAML document representing the nodeDb // instance. Passes through any errors generated by yaml.Marshal. func (config *NodeYaml) Dump() ([]byte, error) { diff --git a/internal/pkg/node/transformers.go b/internal/pkg/node/transformers.go index 66d24328..2dff47ea 100644 --- a/internal/pkg/node/transformers.go +++ b/internal/pkg/node/transformers.go @@ -104,17 +104,17 @@ func (node *NodeInfo) SetFrom(n *NodeConf) { setSliceWrap := func(entr *Entry, val []string, nameArg string) { entr.SetSlice(val) } - node.setterFrom(n, "", setWrap, setSliceWrap) + recursiveSetter(n, node, "", setWrap, setSliceWrap) } /* Populates all fields of NodeInfo with SetAlt from the values of NodeConf. The string profileName is used to -destermine from which source/NodeInfo the entry came +determine from which source/NodeInfo the entry came from. */ func (node *NodeInfo) SetAltFrom(n *NodeConf, profileName string) { - node.setterFrom(n, profileName, (*Entry).SetAlt, (*Entry).SetAltSlice) + recursiveSetter(n, node, profileName, (*Entry).SetAlt, (*Entry).SetAltSlice) } /* @@ -128,120 +128,95 @@ func (node *NodeInfo) SetDefFrom(n *NodeConf) { setSliceWrap := func(entr *Entry, val []string, nameArg string) { entr.SetDefaultSlice(val) } - node.setterFrom(n, "", setWrap, setSliceWrap) + recursiveSetter(n, node, "", setWrap, setSliceWrap) +} + +func SetDefFrom(source, target interface{}) { + setWrap := func(entr *Entry, val string, nameArg string) { + entr.SetDefault(val) + } + setSliceWrap := func(entr *Entry, val []string, nameArg string) { + entr.SetDefaultSlice(val) + } + recursiveSetter(source, target, "", setWrap, setSliceWrap) + } /* Abstract function which populates a NodeInfo from a NodeConf via -setter functionns. +setter functions. Panics if other type than string, []string *ptr is used in NodeConf. */ -func (node *NodeInfo) setterFrom(n *NodeConf, nameArg string, - setter func(*Entry, string, string), +func recursiveSetter(source, target interface{}, nameArg string, setter func(*Entry, string, string), setterSlice func(*Entry, []string, string)) { - // get the full memory, taking the shortcut and init Ipmi and Kernel directly - if node.Kernel == nil { - node.Kernel = new(KernelEntry) - } - if node.Ipmi == nil { - node.Ipmi = new(IpmiEntry) - } - // also n could be nil - if n == nil { - myn := NewConf() - n = &myn - } - nodeInfoVal := reflect.ValueOf(node) - nodeInfoType := reflect.TypeOf(node) - nodeConfVal := reflect.ValueOf(n) - // now iterate of every field - for i := 0; i < nodeInfoType.Elem().NumField(); i++ { - valField := nodeConfVal.Elem().FieldByName(nodeInfoType.Elem().Field(i).Name) - if valField.IsValid() { - // found field with same name for Conf and Info - if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(Entry{}) { - if valField.Type().Kind() == reflect.String { - setter(nodeInfoVal.Elem().Field(i).Addr().Interface().(*Entry), valField.String(), nameArg) - } else if valField.Type() == reflect.TypeOf([]string{}) { - setterSlice(nodeInfoVal.Elem().Field(i).Addr().Interface().(*Entry), valField.Interface().([]string), nameArg) - } - } else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr && !valField.IsZero() { - nestedInfoType := reflect.TypeOf(nodeInfoVal.Elem().Field(i).Interface()) - nestedInfoVal := reflect.ValueOf(nodeInfoVal.Elem().Field(i).Interface()) - nestedConfVal := reflect.ValueOf(valField.Interface()) - for j := 0; j < nestedInfoType.Elem().NumField(); j++ { - nestedVal := nestedConfVal.Elem().FieldByName(nestedInfoType.Elem().Field(j).Name) - if nestedVal.IsValid() { - if nestedInfoVal.Elem().Field(j).Type() == reflect.TypeOf(Entry{}) { - setter(nestedInfoVal.Elem().Field(j).Addr().Interface().(*Entry), nestedVal.String(), nameArg) - } else if nestedInfoVal.Elem().Field(j).Type() == reflect.TypeOf(map[string](*Entry){}) { - confMap := nestedVal.Interface().(map[string]string) - if nestedInfoVal.Elem().Field(j).IsNil() { - ptr := nestedInfoVal.Elem().Field(j).Addr().Interface().(*map[string](*Entry)) - *ptr = make(map[string]*Entry) - } - tagMap := nestedInfoVal.Elem().Field(j).Interface().(map[string](*Entry)) - for key, val := range confMap { - if entr, ok := tagMap[key]; ok { - setter(entr, val, nameArg) - } else { - entr := new(Entry) - tagMap[key] = entr - setter(entr, val, nameArg) + sourceValue := reflect.ValueOf(source) + targetType := reflect.TypeOf(target) + targetValue := reflect.ValueOf(target) + if targetValue.Elem().Kind() == reflect.Struct && sourceValue.Elem().Kind() == reflect.Struct { + for i := 0; i < targetType.Elem().NumField(); i++ { + sourceValueMatched := sourceValue.Elem().FieldByName(targetType.Elem().Field(i).Name) + if sourceValueMatched.IsValid() { + if targetValue.Elem().Field(i).Type() == reflect.TypeOf(Entry{}) { + // get the fields which are part of the struct + switch sourceValueMatched.Type() { + case reflect.TypeOf(""): + setter(targetValue.Elem().Field(i).Addr().Interface().(*Entry), sourceValueMatched.String(), nameArg) + case reflect.TypeOf([]string{}): + setterSlice(targetValue.Elem().Field(i).Addr().Interface().(*Entry), sourceValueMatched.Interface().([]string), nameArg) + default: + panic(fmt.Errorf("can't convert an Entry to %s", targetValue.Elem().Field(i).Type())) + } + } else if sourceValueMatched.Kind() == reflect.Ptr { + // if we get a pointer, initialize if empty and then have a recursive call + if targetValue.Elem().Field(i).IsZero() { + targetValue.Elem().Field(i).Set(reflect.New(targetType.Elem().Field(i).Type.Elem())) + } + recursiveSetter(sourceValueMatched.Interface(), targetValue.Elem().Field(i).Interface(), nameArg, setter, setterSlice) + } else if sourceValueMatched.Type().Kind() == reflect.Map { + if targetValue.Elem().Field(i).IsZero() { + targetValue.Elem().Field(i).Set(reflect.MakeMap(targetType.Elem().Field(i).Type)) + } + // delete a ap element which is only in the target + if targetValue.Elem().Field(i).Len() > 0 && targetValue.Elem().Field(i).Len() < 0 { + sourceIter := sourceValueMatched.MapRange() + targetIter := targetValue.Elem().Field(i).MapRange() + for targetIter.Next() { + sameKey := false + for sourceIter.Next() { + if sourceIter.Key() == targetIter.Key() { + sameKey = true } } + if !sameKey { + targetValue.Elem().Field(i).SetMapIndex(targetIter.Key(), reflect.Value{}) + } } } - } - } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string](*Entry)(nil)) { - confMap := valField.Interface().(map[string]string) - for key, val := range confMap { - tagMap := nodeInfoVal.Elem().Field(i).Interface().(map[string](*Entry)) - if nodeInfoVal.Elem().Field(i).IsNil() { - tagMap = make(map[string]*Entry) - } - if entr, ok := tagMap[key]; ok { - setter(entr, val, nameArg) - } else { - entr := new(Entry) - tagMap[key] = entr - setter(entr, val, nameArg) - } - } - } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string](*NetDevEntry)(nil)) { - netValMap := valField.Interface().(map[string](*NetDevs)) - for netName, netVals := range netValMap { - netValsType := reflect.ValueOf(netVals) - netMap := nodeInfoVal.Elem().Field(i).Interface().(map[string](*NetDevEntry)) - if nodeInfoVal.Elem().Field(i).IsNil() { - netMap = make(map[string]*NetDevEntry) - } - if _, ok := netMap[netName]; !ok { - var newNet NetDevEntry - newNet.Tags = make(map[string]*Entry) - netMap[netName] = &newNet - } - netInfoType := reflect.TypeOf(*netMap[netName]) - netInfoVal := reflect.ValueOf(netMap[netName]) - for j := 0; j < netInfoType.NumField(); j++ { - netVal := netValsType.Elem().FieldByName(netInfoType.Field(j).Name) - if netVal.IsValid() { - if netVal.Type().Kind() == reflect.String { - setter(netInfoVal.Elem().Field(j).Addr().Interface().((*Entry)), netVal.String(), nameArg) - } else if netVal.Type() == reflect.TypeOf(map[string]string{}) { - for key, val := range (netVal.Interface()).(map[string]string) { - //netTagMap := netInfoVal.Elem().Field(j).Interface().((map[string](*Entry))) - if _, ok := netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key]; !ok { - netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key] = new(Entry) - } - setter(netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key], val, nameArg) - } + sourceIter := sourceValueMatched.MapRange() + if sourceValueMatched.Type().Elem() == reflect.TypeOf("") { + // go over a simple map with strings + for sourceIter.Next() { + if !targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).IsValid() { + newEntr := new(Entry) + setter(newEntr, sourceIter.Value().String(), nameArg) + targetValue.Elem().Field(i).SetMapIndex(sourceIter.Key(), reflect.ValueOf(newEntr)) } } + } else { + // now the complicated map which contains pointers to objects + for sourceIter.Next() { + if !targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).IsValid() { + newPtr := reflect.New(targetType.Elem().Field(i).Type.Elem().Elem()) + targetValue.Elem().Field(i).SetMapIndex(sourceIter.Key(), newPtr) + } + recursiveSetter(sourceIter.Value().Interface(), targetValue.Elem().Field(i).MapIndex(sourceIter.Key()).Interface(), nameArg, setter, setterSlice) + + } } } } } } + } /* @@ -280,78 +255,6 @@ func recursiveFlatten(strct interface{}) { } } -/* -Populates all fields of NetDevEntry with Set from the -values of NetDevs. -Actually not used, just for completeness. -*/ -func (netDev *NetDevEntry) SetFrom(netYaml *NetDevs) { - setWrap := func(entr *Entry, val string, nameArg string) { - entr.Set(val) - } - setSliceWrap := func(entr *Entry, val []string, nameArg string) { - entr.SetSlice(val) - } - netDev.setterFrom(netYaml, "", setWrap, setSliceWrap) -} - -/* -Populates all fields of NetDevEntry with SetAlt from the -values of NetDevs. The string profileName is used to -destermine from which source/NodeInfo the entry came -from. -Actually not used, just for completeness. -*/ -func (netDev *NetDevEntry) SetAltFrom(netYaml *NetDevs, profileName string) { - netDev.setterFrom(netYaml, profileName, (*Entry).SetAlt, (*Entry).SetAltSlice) -} - -/* -Populates all fields of NodeInfo with SetDefault from the -values of NodeConf. -*/ -func (netDev *NetDevEntry) SetDefFrom(netYaml *NetDevs) { - setWrap := func(entr *Entry, val string, nameArg string) { - entr.SetDefault(val) - } - setSliceWrap := func(entr *Entry, val []string, nameArg string) { - entr.SetDefaultSlice(val) - } - netDev.setterFrom(netYaml, "", setWrap, setSliceWrap) -} - -/* -Abstract function for setting a NetDevEntry from a NetDevs -*/ -func (netDev *NetDevEntry) setterFrom(netYaml *NetDevs, nameArg string, - setter func(*Entry, string, string), - setterSlice func(*Entry, []string, string)) { - // check if netYaml is empty - if netYaml == nil { - netYaml = new(NetDevs) - } - netValues := reflect.ValueOf(netDev) - netInfoType := reflect.TypeOf(*netYaml) - netInfoVal := reflect.ValueOf(*netYaml) - for j := 0; j < netInfoType.NumField(); j++ { - netVal := netValues.Elem().FieldByName(netInfoType.Field(j).Name) - if netVal.IsValid() { - if netInfoVal.Field(j).Type().Kind() == reflect.String { - setter(netVal.Addr().Interface().((*Entry)), netInfoVal.Field(j).String(), nameArg) - } else if netVal.Type() == reflect.TypeOf(map[string]string{}) { - // danger zone following code is not tested - for key, val := range (netVal.Interface()).(map[string]string) { - //netTagMap := netInfoVal.Elem().Field(j).Interface().((map[string](*Entry))) - if _, ok := netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key]; !ok { - netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key] = new(Entry) - } - setter(netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key], val, nameArg) - } - } - } - } -} - /* Create a string slice, where every element represents a yaml entry */ From 45539a0d1f3c8f45b3bb81694a94108ea14db8b6 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 11 Aug 2023 10:31:29 -0600 Subject: [PATCH 60/75] Recursive handling for command line flags Every struct in a NodeConf with `lopt:"foo" set is now added as a command-line flag with the RecursiveCreateFlags call. For maps a struct with the key UNDEF is added so that it can be parsed out. As the flags for the command-line need variables which hold the values, for every map an element map[UNDEF] is added. When now calling the internal add, these map element can be filtered out and replace by the given name. (e.g., --netname) * rewrote node/profile add for recursive functions * rewrote node/profile set for recursive functions * rewrote node/profile list for recursive functions Signed-off-by: Christian Goll --- internal/app/wwctl/node/add/main.go | 39 +- internal/app/wwctl/node/add/main_test.go | 61 ++- internal/app/wwctl/node/add/root.go | 8 +- internal/app/wwctl/node/edit/main.go | 3 +- internal/app/wwctl/node/list/main.go | 2 + internal/app/wwctl/node/list/root.go | 10 +- internal/app/wwctl/node/set/main.go | 104 ++-- internal/app/wwctl/node/set/main_test.go | 348 ++++++++++++ internal/app/wwctl/node/set/root.go | 56 +- internal/app/wwctl/profile/add/main.go | 50 +- internal/app/wwctl/profile/add/main_test.go | 6 +- internal/app/wwctl/profile/add/root.go | 21 +- internal/app/wwctl/profile/edit/main.go | 4 +- internal/app/wwctl/profile/list/main.go | 5 +- internal/app/wwctl/profile/list/root.go | 6 +- internal/app/wwctl/profile/set/main.go | 92 +++- internal/app/wwctl/profile/set/root.go | 60 +- internal/pkg/api/node/list.go | 101 +--- internal/pkg/api/node/methods.go | 79 --- internal/pkg/api/node/node.go | 44 +- internal/pkg/api/profile/list.go | 96 +--- internal/pkg/api/profile/profile.go | 56 +- internal/pkg/api/routes/v1/routes.proto | 29 +- internal/pkg/api/routes/wwapiv1/routes.pb.go | 545 ++++++++++++------- internal/pkg/node/flags.go | 52 +- internal/pkg/node/list.go | 79 +++ internal/pkg/node/methods.go | 15 +- internal/pkg/node/methods_test.go | 8 +- internal/pkg/node/transformers.go | 81 ++- 29 files changed, 1346 insertions(+), 714 deletions(-) create mode 100644 internal/app/wwctl/node/set/main_test.go delete mode 100644 internal/pkg/api/node/methods.go create mode 100644 internal/pkg/node/list.go diff --git a/internal/app/wwctl/node/add/main.go b/internal/app/wwctl/node/add/main.go index 694e3e13..125c49e6 100644 --- a/internal/app/wwctl/node/add/main.go +++ b/internal/app/wwctl/node/add/main.go @@ -1,10 +1,14 @@ package add import ( + "fmt" + "strings" + "gopkg.in/yaml.v2" apinode "github.com/hpcng/warewulf/internal/pkg/api/node" "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" ) @@ -22,17 +26,36 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) error { return err } } - // remove the default network as all network values are assigned + // remove the UNDEF network as all network values are assigned // to this network - if _, ok := vars.nodeConf.NetDevs["default"]; ok && vars.netName != "" { - netDev := *vars.nodeConf.NetDevs["default"] + if !node.ObjectIsEmpty(vars.nodeConf.NetDevs["UNDEF"]) { + netDev := *vars.nodeConf.NetDevs["UNDEF"] vars.nodeConf.NetDevs[vars.netName] = &netDev - delete(vars.nodeConf.NetDevs, "default") - } else { - if vars.nodeConf.NetDevs["default"].Empty() { - delete(vars.nodeConf.NetDevs, "default") - } } + delete(vars.nodeConf.NetDevs, "UNDEF") + if vars.fsName != "" { + if !strings.HasPrefix(vars.fsName, "/dev") { + if vars.fsName == vars.partName { + vars.fsName = "/dev/disk/by-partlabel/" + vars.partName + } else { + return fmt.Errorf("filesystems need to have a underlying blockdev") + } + } + fs := *vars.nodeConf.FileSystems["UNDEF"] + vars.nodeConf.FileSystems[vars.fsName] = &fs + } + delete(vars.nodeConf.FileSystems, "UNDEF") + if vars.diskName != "" && vars.partName != "" { + prt := *vars.nodeConf.Disks["UNDEF"].Partitions["UNDEF"] + vars.nodeConf.Disks["UNDEF"].Partitions[vars.partName] = &prt + delete(vars.nodeConf.Disks["UNDEF"].Partitions, "UNDEF") + dsk := *vars.nodeConf.Disks["UNDEF"] + vars.nodeConf.Disks[vars.diskName] = &dsk + } + if (vars.diskName != "") != (vars.partName != "") { + return fmt.Errorf("partition and disk must be specified") + } + delete(vars.nodeConf.Disks, "UNDEF") buffer, err := yaml.Marshal(vars.nodeConf) if err != nil { wwlog.Error("Can't marshall nodeInfo", err) diff --git a/internal/app/wwctl/node/add/main_test.go b/internal/app/wwctl/node/add/main_test.go index 71513ea1..8a243724 100644 --- a/internal/app/wwctl/node/add/main_test.go +++ b/internal/app/wwctl/node/add/main_test.go @@ -5,8 +5,8 @@ import ( "os" "testing" - "github.com/hpcng/warewulf/internal/pkg/node" warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" + "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/warewulfd" "github.com/stretchr/testify/assert" ) @@ -234,6 +234,65 @@ nodes: network devices: foo: ipaddr: 10.10.0.3 +`}, + {name: "one node with filesystem", + args: []string{"--fsname=/dev/vda1", "--fspath=/var", "n01"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + profiles: + - default + filesystems: + /dev/vda1: + path: /var +`}, + {name: "one node with filesystem", + args: []string{"--fsname=dev/vda1", "--fspath=/var", "n01"}, + wantErr: true, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: {} +`}, + {name: "one node with filesystem and partition ", + args: []string{"--fsname=var", "--fspath=/var", "--partname=var", "--diskname=/dev/vda", "n01"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + profiles: + - default + disks: + /dev/vda: + partitions: + var: {} + filesystems: + /dev/disk/by-partlabel/var: + path: /var +`}, + {name: "one node with filesystem with btrfs and partition ", + args: []string{"--fsname=var", "--fspath=/var", "--fsformat=btrfs", "--partname=var", "--diskname=/dev/vda", "n01"}, + wantErr: false, + stdout: "", + outDb: `WW_INTERNAL: 43 +nodeprofiles: {} +nodes: + n01: + profiles: + - default + disks: + /dev/vda: + partitions: + var: {} + filesystems: + /dev/disk/by-partlabel/var: + format: btrfs + path: /var `}, } conf_yml := `WW_INTERNAL: 0` diff --git a/internal/app/wwctl/node/add/root.go b/internal/app/wwctl/node/add/root.go index a74a4cf4..caab5cee 100644 --- a/internal/app/wwctl/node/add/root.go +++ b/internal/app/wwctl/node/add/root.go @@ -13,6 +13,9 @@ import ( // Holds the variables which are needed in CobraRunE type variables struct { netName string + fsName string + partName string + diskName string nodeConf node.NodeConf converters []func() error } @@ -30,7 +33,10 @@ func GetCommand() *cobra.Command { Args: cobra.MinimumNArgs(1), } vars.converters = vars.nodeConf.CreateFlags(baseCmd, []string{"tagdel", "nettagdel", "ipmitagdel"}) - baseCmd.PersistentFlags().StringVar(&vars.netName, "netname", "", "Set network name for network options") + baseCmd.PersistentFlags().StringVar(&vars.netName, "netname", "default", "Set network name for network options") + baseCmd.PersistentFlags().StringVar(&vars.fsName, "fsname", "", "set the file system name which must match a partition name") + baseCmd.PersistentFlags().StringVar(&vars.partName, "partname", "", "set the partition name so it can be used by a file system") + baseCmd.PersistentFlags().StringVar(&vars.diskName, "diskname", "", "set disk device name for the partition") // register the command line completions if err := baseCmd.RegisterFlagCompletionFunc("container", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { list, _ := container.ListSources() diff --git a/internal/app/wwctl/node/edit/main.go b/internal/app/wwctl/node/edit/main.go index 5ac5a202..adde59aa 100644 --- a/internal/app/wwctl/node/edit/main.go +++ b/internal/app/wwctl/node/edit/main.go @@ -45,8 +45,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { wwlog.Error("Could not create temp file:%s \n", err) } defer os.Remove(file.Name()) - nodeConf := node.NewConf() - yamlTemplate := nodeConf.UnmarshalConf([]string{"tagsdel", "default", "profiles"}) + yamlTemplate := node.UnmarshalConf(node.NodeConf{}, []string{"tagsdel", "default", "profiles"}) for { _ = file.Truncate(0) _, _ = file.Seek(0, 0) diff --git a/internal/app/wwctl/node/list/main.go b/internal/app/wwctl/node/list/main.go index 6e748f8d..6e44fe92 100644 --- a/internal/app/wwctl/node/list/main.go +++ b/internal/app/wwctl/node/list/main.go @@ -26,6 +26,8 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err req.Type = wwapiv1.GetNodeList_Network } else if vars.showLong { req.Type = wwapiv1.GetNodeList_Long + } else if vars.showFullAll { + req.Type = wwapiv1.GetNodeList_FullAll } nodeInfo, err := apinode.NodeList(&req) if len(nodeInfo.Output) > 0 { diff --git a/internal/app/wwctl/node/list/root.go b/internal/app/wwctl/node/list/root.go index 37acc641..a3b226e7 100644 --- a/internal/app/wwctl/node/list/root.go +++ b/internal/app/wwctl/node/list/root.go @@ -6,10 +6,11 @@ import ( ) type variables struct { - showNet bool - showIpmi bool - showAll bool - showLong bool + showNet bool + showIpmi bool + showAll bool + showLong bool + showFullAll bool } func GetCommand() *cobra.Command { @@ -39,6 +40,7 @@ func GetCommand() *cobra.Command { baseCmd.PersistentFlags().BoolVarP(&vars.showNet, "net", "n", false, "Show node network configurations") baseCmd.PersistentFlags().BoolVarP(&vars.showIpmi, "ipmi", "i", false, "Show node IPMI configurations") baseCmd.PersistentFlags().BoolVarP(&vars.showAll, "all", "a", false, "Show all node configurations") + baseCmd.PersistentFlags().BoolVarP(&vars.showFullAll, "fullall", "A", false, "Show all node configurations inclusive empty entries") baseCmd.PersistentFlags().BoolVarP(&vars.showLong, "long", "l", false, "Show long or wide format") return baseCmd diff --git a/internal/app/wwctl/node/set/main.go b/internal/app/wwctl/node/set/main.go index 4bc44f21..00e08135 100644 --- a/internal/app/wwctl/node/set/main.go +++ b/internal/app/wwctl/node/set/main.go @@ -3,54 +3,86 @@ package set import ( "fmt" "os" + "strings" 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/wwlog" "github.com/spf13/cobra" "gopkg.in/yaml.v2" ) -func CobraRunE(cmd *cobra.Command, args []string) (err error) { - // run converters for different types - for _, c := range Converters { - if err := c(); err != nil { - return err +func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err error) { + return func(cmd *cobra.Command, args []string) error { + // run converters for different types + for _, c := range vars.converters { + if err := c(); err != nil { + return err + } } - } - // remove the default network as the all network values are assigned - // to this network - if NetName != "default" { - NodeConf.NetDevs[NetName] = NodeConf.NetDevs["default"] - delete(NodeConf.NetDevs, "default") - } - buffer, err := yaml.Marshal(NodeConf) - if err != nil { - wwlog.Error("Can't marshall nodeInfo", err) - os.Exit(1) - } - set := wwapiv1.NodeSetParameter{ - NodeConfYaml: string(buffer[:]), - - NetdevDelete: SetNetDevDel, - AllNodes: SetNodeAll, - Force: SetForce, - NodeNames: args, - } - - if !SetYes { - var nodeCount uint - // The checks run twice in the prompt case. - // Avoiding putting in a blocking prompt in an API. - _, nodeCount, err = apinode.NodeSetParameterCheck(&set, false) + // remove the default network as the all network values are assigned + // to this network + if !node.ObjectIsEmpty(vars.nodeConf.NetDevs["UNDEF"]) { + netDev := *vars.nodeConf.NetDevs["UNDEF"] + vars.nodeConf.NetDevs[vars.netName] = &netDev + } + delete(vars.nodeConf.NetDevs, "UNDEF") + if vars.fsName != "" { + if !strings.HasPrefix(vars.fsName, "/dev") { + if vars.fsName == vars.partName { + vars.fsName = "/dev/disk/by-partlabel/" + vars.partName + } else { + return fmt.Errorf("filesystems need to have a underlying blockdev") + } + } + fs := *vars.nodeConf.FileSystems["UNDEF"] + vars.nodeConf.FileSystems[vars.fsName] = &fs + } + delete(vars.nodeConf.FileSystems, "UNDEF") + if vars.diskName != "" && vars.partName != "" { + prt := *vars.nodeConf.Disks["UNDEF"].Partitions["UNDEF"] + vars.nodeConf.Disks["UNDEF"].Partitions[vars.partName] = &prt + delete(vars.nodeConf.Disks["UNDEF"].Partitions, "UNDEF") + dsk := *vars.nodeConf.Disks["UNDEF"] + vars.nodeConf.Disks[vars.diskName] = &dsk + } + if (vars.diskName != "") != (vars.partName != "") { + return fmt.Errorf("partition and disk must be specified") + } + delete(vars.nodeConf.Disks, "UNDEF") + buffer, err := yaml.Marshal(vars.nodeConf) if err != nil { - return + wwlog.Error("Can't marshall nodeInfo", err) + os.Exit(1) } - yes := util.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d nodes(s)", nodeCount)) - if !yes { - return + wwlog.Debug("sending following values: %s", string(buffer)) + set := wwapiv1.NodeSetParameter{ + NodeConfYaml: string(buffer[:]), + + NetdevDelete: vars.setNetDevDel, + PartitionDelete: vars.setPartDel, + DiskDelete: vars.setDiskDel, + FilesystemDelete: vars.setFsDel, + AllNodes: vars.setNodeAll, + Force: vars.setForce, + NodeNames: args, } + + if !vars.setYes { + var nodeCount uint + // The checks run twice in the prompt case. + // Avoiding putting in a blocking prompt in an API. + _, nodeCount, err = apinode.NodeSetParameterCheck(&set, false) + if err != nil { + return nil + } + yes := util.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d nodes(s)", nodeCount)) + if !yes { + return nil + } + } + return apinode.NodeSet(&set) } - return apinode.NodeSet(&set) } diff --git a/internal/app/wwctl/node/set/main_test.go b/internal/app/wwctl/node/set/main_test.go new file mode 100644 index 00000000..4feda0d3 --- /dev/null +++ b/internal/app/wwctl/node/set/main_test.go @@ -0,0 +1,348 @@ +package set + +import ( + "bytes" + "os" + "testing" + + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/warewulfd" + "github.com/stretchr/testify/assert" +) + +func Test_Add(t *testing.T) { + tests := []struct { + name string + args []string + wantErr bool + stdout string + chkout bool + outDb string + inDB string + }{ + {name: "single node change profile", + args: []string{"--profile=foo", "n01"}, + wantErr: false, + stdout: "", + inDB: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default`, + outDb: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - foo +`}, + {name: "multiple nodes change profile", + args: []string{"--profile=foo", "n0[1-2]"}, + wantErr: false, + stdout: "", + inDB: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default + n02: + profiles: + - default`, + outDb: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - foo + n02: + profiles: + - foo +`}, + {name: "single node set ipmitag", + args: []string{"--ipmitagadd", "foo=baar", "n01"}, + wantErr: false, + stdout: "", + inDB: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default`, + outDb: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + ipmi: + tags: + foo: baar + profiles: + - default +`}, + {name: "single node delete tag", + args: []string{"--tagdel", "tag1", "n01"}, + wantErr: false, + stdout: "", + inDB: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default + tags: + tag1: value1 + tag2: value2`, + outDb: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default + tags: + tag2: value2 +`}, + {name: "single node set fs,part and disk", + args: []string{"--fsname=var", "--fspath=/var", "--fsformat=btrfs", "--partname=var", "--diskname=/dev/vda", "n01"}, + wantErr: false, + stdout: "", + inDB: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default +`, + outDb: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default + disks: + /dev/vda: + partitions: + var: {} + filesystems: + /dev/disk/by-partlabel/var: + format: btrfs + path: /var +`}, + {name: "single delete not existing fs", + args: []string{"--fsdel=var", "n01"}, + wantErr: true, + stdout: "", + inDB: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default + disks: + /dev/vda: + partitions: + var: {} + path: /var + filesystems: + /dev/disk/by-partlabel/var: + format: btrfs + path: /var +`, + outDb: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default + disks: + /dev/vda: + partitions: + var: {} + filesystems: + /dev/disk/by-partlabel/var: + format: btrfs + path: /var +`}, + {name: "single node delete existing fs", + args: []string{"--fsdel=/dev/disk/by-partlabel/var", "n01"}, + wantErr: false, + stdout: "", + inDB: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default + disks: + /dev/vda: + partitions: + var: {} + path: /var + filesystems: + /dev/disk/by-partlabel/var: + format: btrfs + path: /var +`, + outDb: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default + disks: + /dev/vda: + partitions: + var: {} +`}, + {name: "single node delete existing partition", + args: []string{"--partdel=var", "n01"}, + wantErr: false, + stdout: "", + inDB: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default + disks: + /dev/vda: + partitions: + var: {} + path: /var + filesystems: + /dev/disk/by-partlabel/var: + format: btrfs + path: /var +`, + outDb: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default + disks: + /dev/vda: {} + filesystems: + /dev/disk/by-partlabel/var: + format: btrfs + path: /var +`}, + {name: "single node delete existing disk", + args: []string{"--diskdel=/dev/vda", "n01"}, + wantErr: false, + stdout: "", + inDB: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default + disks: + /dev/vda: + partitions: + var: {} + path: /var + filesystems: + /dev/disk/by-partlabel/var: + format: btrfs + path: /var +`, + outDb: `WW_INTERNAL: 43 +nodeprofiles: + default: + comment: testit +nodes: + n01: + profiles: + - default + filesystems: + /dev/disk/by-partlabel/var: + format: btrfs + path: /var +`}, + } + conf_yml := `WW_INTERNAL: 0` + tempWarewulfConf, warewulfConfErr := os.CreateTemp("", "warewulf.conf-") + assert.NoError(t, warewulfConfErr) + defer os.Remove(tempWarewulfConf.Name()) + _, warewulfConfErr = tempWarewulfConf.Write([]byte(conf_yml)) + assert.NoError(t, warewulfConfErr) + assert.NoError(t, tempWarewulfConf.Sync()) + assert.NoError(t, warewulfconf.New().Read(tempWarewulfConf.Name())) + + tempNodeConf, nodesConfErr := os.CreateTemp("", "nodes.conf-") + assert.NoError(t, nodesConfErr) + defer os.Remove(tempNodeConf.Name()) + node.ConfigFile = tempNodeConf.Name() + warewulfd.SetNoDaemon() + for _, tt := range tests { + var err error + _, err = tempNodeConf.Seek(0, 0) + assert.NoError(t, err) + assert.NoError(t, tempNodeConf.Truncate(0)) + _, err = tempNodeConf.Write([]byte(tt.inDB)) + assert.NoError(t, err) + assert.NoError(t, tempNodeConf.Sync()) + t.Logf("Running test: %s\n", tt.name) + t.Run(tt.name, func(t *testing.T) { + baseCmd := GetCommand() + tt.args = append(tt.args, "--yes") + baseCmd.SetArgs(tt.args) + buf := new(bytes.Buffer) + baseCmd.SetOut(buf) + baseCmd.SetErr(buf) + err = baseCmd.Execute() + if (err != nil) != tt.wantErr { + t.Errorf("Got unwanted error: %s", err) + t.FailNow() + } + config, configErr := node.New() + assert.NoError(t, configErr) + dumpBytes, _ := config.Dump() + dump := string(dumpBytes) + if dump != tt.outDb { + t.Errorf("DB dump is wrong, got:'%s'\nwant:'%s'", dump, tt.outDb) + t.FailNow() + } + if tt.chkout && buf.String() != tt.stdout { + t.Errorf("Got wrong output, got:'%s'\nwant:'%s'", buf.String(), tt.stdout) + t.FailNow() + } + }) + } +} diff --git a/internal/app/wwctl/node/set/root.go b/internal/app/wwctl/node/set/root.go index ac4c1f14..5a278518 100644 --- a/internal/app/wwctl/node/set/root.go +++ b/internal/app/wwctl/node/set/root.go @@ -10,14 +10,32 @@ import ( "github.com/spf13/cobra" ) -var ( - baseCmd = &cobra.Command{ +type variables struct { + setNetDevDel string + setDiskDel string + setPartDel string + setFsDel string + netName string + partName string + diskName string + fsName string + setNodeAll bool + setYes bool + setForce bool + nodeConf node.NodeConf + converters []func() error +} + +func GetCommand() *cobra.Command { + vars := variables{} + vars.nodeConf = node.NewConf() + baseCmd := &cobra.Command{ DisableFlagsInUseLine: true, Use: "set [OPTIONS] PATTERN [PATTERN ...]", Short: "Configure node properties", Long: "This command sets configuration properties for nodes matching PATTERN.\n\nNote: use the string 'UNSET' to remove a configuration", Args: cobra.MinimumNArgs(0), - RunE: CobraRunE, + RunE: CobraRunE(&vars), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { return nil, cobra.ShellCompDirectiveNoFileComp @@ -32,23 +50,19 @@ var ( return node_names, cobra.ShellCompDirectiveNoFileComp }, } - SetNetDevDel string - NetName string - SetNodeAll bool - SetYes bool - SetForce bool - NodeConf node.NodeConf - Converters []func() error -) -func init() { - NodeConf = node.NewConf() - Converters = NodeConf.CreateFlags(baseCmd, []string{}) - baseCmd.PersistentFlags().StringVarP(&SetNetDevDel, "netdel", "D", "", "Delete the node's network device") - baseCmd.PersistentFlags().StringVar(&NetName, "netname", "default", "Set network name for network options") - baseCmd.PersistentFlags().BoolVarP(&SetNodeAll, "all", "a", false, "Set all nodes") - baseCmd.PersistentFlags().BoolVarP(&SetYes, "yes", "y", false, "Set 'yes' to all questions asked") - baseCmd.PersistentFlags().BoolVarP(&SetForce, "force", "f", false, "Force configuration (even on error)") + vars.converters = vars.nodeConf.CreateFlags(baseCmd, []string{}) + baseCmd.PersistentFlags().StringVarP(&vars.setNetDevDel, "netdel", "D", "", "Delete the node's network device") + baseCmd.PersistentFlags().StringVar(&vars.netName, "netname", "default", "Set network name for network options") + baseCmd.PersistentFlags().BoolVarP(&vars.setNodeAll, "all", "a", false, "Set all nodes") + baseCmd.PersistentFlags().StringVar(&vars.fsName, "fsname", "", "set the file system name which must match a partition name") + baseCmd.PersistentFlags().StringVar(&vars.partName, "partname", "", "set the partition name so it can be used by a file system") + baseCmd.PersistentFlags().StringVar(&vars.diskName, "diskname", "", "set disk device name for the partition") + baseCmd.PersistentFlags().StringVar(&vars.setDiskDel, "diskdel", "", "delete the disk from the configuration") + baseCmd.PersistentFlags().StringVar(&vars.setPartDel, "partdel", "", "delete the partition from the configuration") + baseCmd.PersistentFlags().StringVar(&vars.setFsDel, "fsdel", "", "delete the partition from the configuration") + baseCmd.PersistentFlags().BoolVarP(&vars.setYes, "yes", "y", false, "Set 'yes' to all questions asked") + baseCmd.PersistentFlags().BoolVarP(&vars.setForce, "force", "f", false, "Force configuration (even on error)") // register the command line completions if err := baseCmd.RegisterFlagCompletionFunc("container", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { list, _ := container.ListSources() @@ -86,9 +100,5 @@ func init() { log.Println(err) } -} - -// GetRootCommand returns the root cobra.Command for the application. -func GetCommand() *cobra.Command { return baseCmd } diff --git a/internal/app/wwctl/profile/add/main.go b/internal/app/wwctl/profile/add/main.go index 40ad46a4..8c04ece8 100644 --- a/internal/app/wwctl/profile/add/main.go +++ b/internal/app/wwctl/profile/add/main.go @@ -3,8 +3,10 @@ package add import ( "fmt" "os" + "strings" apiprofile "github.com/hpcng/warewulf/internal/pkg/api/profile" + "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/wwlog" "gopkg.in/yaml.v2" @@ -16,7 +18,7 @@ import ( func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err error) { return func(cmd *cobra.Command, args []string) (err error) { // run converters for different types - for _, c := range Converters { + for _, c := range vars.Converters { if err := c(); err != nil { return err } @@ -24,25 +26,55 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err // remove the default network as the all network values are assigned // to this network if vars.netName != "" { - netDev := *vars.profileConf.NetDevs["default"] + netDev := *vars.profileConf.NetDevs["UNDEF"] vars.profileConf.NetDevs[vars.netName] = &netDev - delete(vars.profileConf.NetDevs, "default") + delete(vars.profileConf.NetDevs, "UNDEF") } + // remove the UNDEF network as all network values are assigned + // to this network + if !node.ObjectIsEmpty(vars.profileConf.NetDevs["UNDEF"]) { + netDev := *vars.profileConf.NetDevs["UNDEF"] + vars.profileConf.NetDevs[vars.netName] = &netDev + } + delete(vars.profileConf.NetDevs, "UNDEF") + if vars.fsName != "" { + if !strings.HasPrefix(vars.fsName, "/dev") { + if vars.fsName == vars.partName { + vars.fsName = "/dev/disk/by-partlabel/" + vars.partName + } else { + return fmt.Errorf("filesystems need to have a underlying blockdev") + } + } + fs := *vars.profileConf.FileSystems["UNDEF"] + vars.profileConf.FileSystems[vars.fsName] = &fs + } + delete(vars.profileConf.FileSystems, "UNDEF") + if vars.diskName != "" && vars.partName != "" { + prt := *vars.profileConf.Disks["UNDEF"].Partitions["UNDEF"] + vars.profileConf.Disks["UNDEF"].Partitions[vars.partName] = &prt + delete(vars.profileConf.Disks["UNDEF"].Partitions, "UNDEF") + dsk := *vars.profileConf.Disks["UNDEF"] + vars.profileConf.Disks[vars.diskName] = &dsk + } + if (vars.diskName != "") != (vars.partName != "") { + return fmt.Errorf("partition and disk must be specified") + } + delete(vars.profileConf.Disks, "UNDEF") buffer, err := yaml.Marshal(vars.profileConf) if err != nil { wwlog.Error("Cant marshall nodeInfo", err) os.Exit(1) } - set := wwapiv1.NodeSetParameter{ + set := wwapiv1.ProfileSetParameter{ NodeConfYaml: string(buffer[:]), - NetdevDelete: SetNetDevDel, - AllNodes: SetNodeAll, - Force: SetForce, - NodeNames: args, + NetdevDelete: vars.SetNetDevDel, + AllProfiles: vars.SetNodeAll, + Force: vars.SetForce, + ProfileNames: args, } - if !SetYes { + if !vars.SetYes { // The checks run twice in the prompt case. // Avoiding putting in a blocking prompt in an API. _, _, err = apiprofile.ProfileSetParameterCheck(&set, false) diff --git a/internal/app/wwctl/profile/add/main_test.go b/internal/app/wwctl/profile/add/main_test.go index f1a084e7..bbc49d0d 100644 --- a/internal/app/wwctl/profile/add/main_test.go +++ b/internal/app/wwctl/profile/add/main_test.go @@ -5,8 +5,8 @@ import ( "os" "testing" - "github.com/hpcng/warewulf/internal/pkg/node" warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" + "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/warewulfd" "github.com/stretchr/testify/assert" ) @@ -26,9 +26,7 @@ func Test_Add(t *testing.T) { stdout: "", outDb: `WW_INTERNAL: 43 nodeprofiles: - p01: - network devices: - default: {} + p01: {} nodes: {} `, }, diff --git a/internal/app/wwctl/profile/add/root.go b/internal/app/wwctl/profile/add/root.go index 3a0d4336..42db93d0 100644 --- a/internal/app/wwctl/profile/add/root.go +++ b/internal/app/wwctl/profile/add/root.go @@ -11,19 +11,18 @@ import ( ) type variables struct { - netName string - profileConf node.NodeConf -} - -var ( + netName string + profileConf node.NodeConf SetNetDevDel string SetNodeAll bool SetYes bool SetForce bool - NetName string + fsName string + partName string + diskName string ProfileConf node.NodeConf Converters []func() error -) +} // GetRootCommand returns the root cobra.Command for the application. func GetCommand() *cobra.Command { @@ -37,9 +36,12 @@ func GetCommand() *cobra.Command { RunE: CobraRunE(&vars), Args: cobra.ExactArgs(1), } - Converters = vars.profileConf.CreateFlags(baseCmd, + vars.Converters = vars.profileConf.CreateFlags(baseCmd, []string{"ipaddr", "ipaddr6", "ipmiaddr", "profile"}) baseCmd.PersistentFlags().StringVar(&vars.netName, "netname", "", "Set network name for network options") + baseCmd.PersistentFlags().StringVar(&vars.fsName, "fsname", "", "set the file system name which must match a partition name") + baseCmd.PersistentFlags().StringVar(&vars.partName, "partname", "", "set the partition name so it can be used by a file system") + baseCmd.PersistentFlags().StringVar(&vars.diskName, "diskname", "", "set disk device name for the partition") // register the command line completions if err := baseCmd.RegisterFlagCompletionFunc("container", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { list, _ := container.ListSources() @@ -65,7 +67,6 @@ func GetCommand() *cobra.Command { }); err != nil { log.Println(err) } - baseCmd.PersistentFlags().BoolVarP(&SetYes, "yes", "y", false, "Set 'yes' to all questions asked") - + baseCmd.PersistentFlags().BoolVarP(&vars.SetYes, "yes", "y", false, "Set 'yes' to all questions asked") return baseCmd } diff --git a/internal/app/wwctl/profile/edit/main.go b/internal/app/wwctl/profile/edit/main.go index cbe018b3..a91ca3d9 100644 --- a/internal/app/wwctl/profile/edit/main.go +++ b/internal/app/wwctl/profile/edit/main.go @@ -45,8 +45,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { wwlog.Error("Could not create temp file:%s \n", err) } defer os.Remove(file.Name()) - nodeConf := node.NewConf() - yamlTemplate := nodeConf.UnmarshalConf([]string{"tagsdel"}) + yamlTemplate := node.UnmarshalConf(node.NodeConf{}, []string{"tagsdel"}) for { _ = file.Truncate(0) _, _ = file.Seek(0, 0) @@ -112,7 +111,6 @@ func CobraRunE(cmd *cobra.Command, args []string) error { if yes { err = apiprofile.ProfileDelete(&wwapiv1.NodeDeleteParameter{NodeNames: pList, Force: true}) - if err != nil { wwlog.Verbose("Problem deleting nodes before modification %s") } diff --git a/internal/app/wwctl/profile/list/main.go b/internal/app/wwctl/profile/list/main.go index de4f55ac..57b8866c 100644 --- a/internal/app/wwctl/profile/list/main.go +++ b/internal/app/wwctl/profile/list/main.go @@ -16,8 +16,9 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err args = strings.FieldsFunc(args[0], func(r rune) bool { return r == ',' }) } req := wwapiv1.GetProfileList{ - ShowAll: vars.showAll, - Profiles: args, + ShowAll: vars.showAll, + ShowFullAll: vars.showFullAll, + Profiles: args, } profileInfo, err := apiprofile.ProfileList(&req) if err != nil { diff --git a/internal/app/wwctl/profile/list/root.go b/internal/app/wwctl/profile/list/root.go index 14689314..c686e525 100644 --- a/internal/app/wwctl/profile/list/root.go +++ b/internal/app/wwctl/profile/list/root.go @@ -3,7 +3,8 @@ package list import "github.com/spf13/cobra" type variables struct { - showAll bool + showAll bool + showFullAll bool } // GetRootCommand returns the root cobra.Command for the application. @@ -17,7 +18,8 @@ func GetCommand() *cobra.Command { RunE: CobraRunE(&vars), Aliases: []string{"ls"}, } - baseCmd.PersistentFlags().BoolVarP(&vars.showAll, "all", "a", false, "Show all node configurations") + baseCmd.PersistentFlags().BoolVarP(&vars.showAll, "all", "a", false, "Show all profile configurations") + baseCmd.PersistentFlags().BoolVarP(&vars.showFullAll, "fullall", "A", false, "Show all profile configurations inclusive empty entries") return baseCmd } diff --git a/internal/app/wwctl/profile/set/main.go b/internal/app/wwctl/profile/set/main.go index efa20fd2..fe553cbc 100644 --- a/internal/app/wwctl/profile/set/main.go +++ b/internal/app/wwctl/profile/set/main.go @@ -3,48 +3,80 @@ package set import ( "fmt" "os" + "strings" apiprofile "github.com/hpcng/warewulf/internal/pkg/api/profile" "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/wwlog" "github.com/spf13/cobra" "gopkg.in/yaml.v2" ) -func CobraRunE(cmd *cobra.Command, args []string) (err error) { +func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err error) { + return func(cmd *cobra.Command, args []string) error { - // remove the default network as the all network values are assigned - // to this network - if NetName != "default" { - ProfileConf.NetDevs[NetName] = ProfileConf.NetDevs["default"] - delete(ProfileConf.NetDevs, "default") - } - buffer, err := yaml.Marshal(ProfileConf) - if err != nil { - wwlog.Error("Cant marshall nodeInfo", err) - os.Exit(1) - } - set := wwapiv1.NodeSetParameter{ - NodeConfYaml: string(buffer[:]), - NetdevDelete: SetNetDevDel, - AllNodes: SetNodeAll, - Force: SetForce, - NodeNames: args, - } - - if !SetYes { - var profileCount uint - // The checks run twice in the prompt case. - // Avoiding putting in a blocking prompt in an API. - _, profileCount, err = apiprofile.ProfileSetParameterCheck(&set, false) + // remove the default network as the all network values are assigned + // to this network + if !node.ObjectIsEmpty(vars.profileConf.NetDevs["UNDEF"]) { + netDev := *vars.profileConf.NetDevs["UNDEF"] + vars.profileConf.NetDevs[vars.netName] = &netDev + } + delete(vars.profileConf.NetDevs, "UNDEF") + if vars.fsName != "" { + if !strings.HasPrefix(vars.fsName, "/dev") { + if vars.fsName == vars.partName { + vars.fsName = "/dev/disk/by-partlabel/" + vars.partName + } else { + return fmt.Errorf("filesystems need to have a underlying blockdev") + } + } + fs := *vars.profileConf.FileSystems["UNDEF"] + vars.profileConf.FileSystems[vars.fsName] = &fs + } + delete(vars.profileConf.FileSystems, "UNDEF") + if vars.diskName != "" && vars.partName != "" { + prt := *vars.profileConf.Disks["UNDEF"].Partitions["UNDEF"] + vars.profileConf.Disks["UNDEF"].Partitions[vars.partName] = &prt + delete(vars.profileConf.Disks["UNDEF"].Partitions, "UNDEF") + dsk := *vars.profileConf.Disks["UNDEF"] + vars.profileConf.Disks[vars.diskName] = &dsk + } + if (vars.diskName != "") != (vars.partName != "") { + return fmt.Errorf("partition and disk must be specified") + } + delete(vars.profileConf.Disks, "UNDEF") + buffer, err := yaml.Marshal(vars.profileConf) if err != nil { - return + wwlog.Error("Cant marshall nodeInfo", err) + os.Exit(1) } - yes := util.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d profile(s)", profileCount)) - if !yes { - return + wwlog.Debug("sending following values: %s", string(buffer)) + set := wwapiv1.ProfileSetParameter{ + NodeConfYaml: string(buffer[:]), + NetdevDelete: vars.setNetDevDel, + PartitionDelete: vars.setPartDel, + DiskDelete: vars.setDiskDel, + FilesystemDelete: vars.setFsDel, + AllProfiles: vars.setNodeAll, + Force: vars.setForce, + ProfileNames: args, } + + if !vars.setYes { + var profileCount uint + // The checks run twice in the prompt case. + // Avoiding putting in a blocking prompt in an API. + _, profileCount, err = apiprofile.ProfileSetParameterCheck(&set, false) + if err != nil { + return err + } + yes := util.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d profile(s)", profileCount)) + if !yes { + return err + } + } + return apiprofile.ProfileSet(&set) } - return apiprofile.ProfileSet(&set) } diff --git a/internal/app/wwctl/profile/set/root.go b/internal/app/wwctl/profile/set/root.go index 0c1fd56b..534e4d85 100644 --- a/internal/app/wwctl/profile/set/root.go +++ b/internal/app/wwctl/profile/set/root.go @@ -10,14 +10,33 @@ import ( "github.com/spf13/cobra" ) -var ( - baseCmd = &cobra.Command{ +type variables struct { + setNetDevDel string + setDiskDel string + setPartDel string + setFsDel string + setNodeAll bool + setYes bool + setForce bool + netName string + partName string + diskName string + fsName string + profileConf node.NodeConf + converters []func() error +} + +func GetCommand() *cobra.Command { + vars := variables{} + vars.profileConf = node.NewConf() + + baseCmd := &cobra.Command{ Use: "set [OPTIONS] [PROFILE ...]", Short: "Configure node profile properties", Long: "This command sets configuration properties for the node PROFILE(s).\n\n" + "Note: use the string 'UNSET' to remove a configuration", Args: cobra.MinimumNArgs(0), - RunE: CobraRunE, + RunE: CobraRunE(&vars), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { return nil, cobra.ShellCompDirectiveNoFileComp @@ -32,24 +51,20 @@ var ( return p_names, cobra.ShellCompDirectiveNoFileComp }, } - SetNetDevDel string - SetNodeAll bool - SetYes bool - SetForce bool - NetName string - ProfileConf node.NodeConf - Converters []func() error -) - -func init() { - ProfileConf = node.NewConf() - Converters = ProfileConf.CreateFlags(baseCmd, + vars.profileConf = node.NewConf() + vars.converters = vars.profileConf.CreateFlags(baseCmd, []string{"ipaddr", "ipaddr6", "ipmiaddr", "profile"}) - baseCmd.PersistentFlags().StringVar(&NetName, "netname", "default", "Set network name for network options") - baseCmd.PersistentFlags().StringVarP(&SetNetDevDel, "netdel", "D", "", "Delete the node's network device") - baseCmd.PersistentFlags().BoolVarP(&SetNodeAll, "all", "a", false, "Set all nodes") - baseCmd.PersistentFlags().BoolVarP(&SetYes, "yes", "y", false, "Set 'yes' to all questions asked") - baseCmd.PersistentFlags().BoolVarP(&SetForce, "force", "f", false, "Force configuration (even on error)") + baseCmd.PersistentFlags().StringVar(&vars.netName, "netname", "default", "Set network name for network options") + baseCmd.PersistentFlags().StringVarP(&vars.setNetDevDel, "netdel", "D", "", "Delete the node's network device") + baseCmd.PersistentFlags().BoolVarP(&vars.setNodeAll, "all", "a", false, "Set all nodes") + baseCmd.PersistentFlags().BoolVarP(&vars.setYes, "yes", "y", false, "Set 'yes' to all questions asked") + baseCmd.PersistentFlags().BoolVarP(&vars.setForce, "force", "f", false, "Force configuration (even on error)") + baseCmd.PersistentFlags().StringVar(&vars.fsName, "fsname", "", "set the file system name which must match a partition name") + baseCmd.PersistentFlags().StringVar(&vars.partName, "partname", "", "set the partition name so it can be used by a file system") + baseCmd.PersistentFlags().StringVar(&vars.diskName, "diskname", "", "set disk device name for the partition") + baseCmd.PersistentFlags().StringVar(&vars.setDiskDel, "diskdel", "", "delete the disk from the configuration") + baseCmd.PersistentFlags().StringVar(&vars.setPartDel, "partdel", "", "delete the partition from the configuration") + baseCmd.PersistentFlags().StringVar(&vars.setFsDel, "fsdel", "", "delete the from the configuration") // register the command line completions if err := baseCmd.RegisterFlagCompletionFunc("container", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { list, _ := container.ListSources() @@ -75,10 +90,5 @@ func init() { }); err != nil { log.Println(err) } - -} - -// GetRootCommand returns the root cobra.Command for the application. -func GetCommand() *cobra.Command { return baseCmd } diff --git a/internal/pkg/api/node/list.go b/internal/pkg/api/node/list.go index df8efddc..7f79f10c 100644 --- a/internal/pkg/api/node/list.go +++ b/internal/pkg/api/node/list.go @@ -2,7 +2,6 @@ package apinode import ( "fmt" - "reflect" "sort" "strings" @@ -77,100 +76,14 @@ func NodeList(nodeGet *wwapiv1.GetNodeList) (nodeList wwapiv1.NodeList, err erro n.ContainerName.Print(), n.SystemOverlay.Print()+"/"+n.RuntimeOverlay.Print())) } - } else if nodeGet.Type == wwapiv1.GetNodeList_All { - nodeList.Output = append(nodeList.Output, - fmt.Sprintf("%s:=:%s:=:%s:=:%s", "NODE", "FIELD", "PROFILE", "VALUE")) + } else if nodeGet.Type == wwapiv1.GetNodeList_All || nodeGet.Type == wwapiv1.GetNodeList_FullAll { for _, n := range node.FilterByName(nodes, nodeGet.Nodes) { - nType := reflect.TypeOf(n) - nVal := reflect.ValueOf(n) - nConfType := reflect.TypeOf(node.NodeConf{}) - for i := 0; i < nType.NumField(); i++ { - var fieldName, fieldSource, fieldVal string - nConfField, ok := nConfType.FieldByName(nType.Field(i).Name) - if ok { - fieldName = nConfField.Tag.Get("lopt") - } else { - fieldName = nType.Field(i).Name - } - if nType.Field(i).Type == reflect.TypeOf(node.Entry{}) { - entr := nVal.Field(i).Interface().(node.Entry) - fieldSource = entr.Source() - fieldVal = entr.Print() - nodeList.Output = append(nodeList.Output, - fmt.Sprintf("%s:=:%s:=:%s:=:%s", n.Id.Print(), fieldName, fieldSource, fieldVal)) - } else if nType.Field(i).Type == reflect.TypeOf(map[string]*node.Entry{}) { - entrMap := nVal.Field(i).Interface().(map[string]*node.Entry) - for key, val := range entrMap { - nodeList.Output = append(nodeList.Output, - fmt.Sprintf("%s:=:%s:=:%s:=:%s", n.Id.Print(), key, val.Source(), val.Print())) - } - } else if nType.Field(i).Type == reflect.TypeOf(map[string]*node.NetDevEntry{}) { - netDevs := nVal.Field(i).Interface().(map[string]*node.NetDevEntry) - for netName, netWork := range netDevs { - netInfoType := reflect.TypeOf(*netWork) - netInfoVal := reflect.ValueOf(*netWork) - netConfType := reflect.TypeOf(node.NetDevs{}) - for j := 0; j < netInfoType.NumField(); j++ { - netConfField, ok := netConfType.FieldByName(netInfoType.Field(j).Name) - if ok { - if netConfField.Tag.Get("lopt") != "nettagadd" { - fieldName = netName + ":" + netConfField.Tag.Get("lopt") - } else { - fieldName = netName + ":tag" - } - } else { - fieldName = netName + ":" + netInfoType.Field(j).Name - } - if netInfoType.Field(j).Type == reflect.TypeOf(node.Entry{}) { - entr := netInfoVal.Field(j).Interface().(node.Entry) - fieldSource = entr.Source() - fieldVal = entr.Print() - // only print fields with lopt - if netConfField.Tag.Get("lopt") != "" { - nodeList.Output = append(nodeList.Output, - fmt.Sprintf("%s:=:%s:=:%s:=:%s", n.Id.Print(), fieldName, fieldSource, fieldVal)) - } - } else if netInfoType.Field(j).Type == reflect.TypeOf(map[string]*node.Entry{}) { - for key, val := range netInfoVal.Field(j).Interface().(map[string]*node.Entry) { - keyfieldName := fieldName + ":" + key - fieldSource = val.Source() - fieldVal = val.Print() - nodeList.Output = append(nodeList.Output, - fmt.Sprintf("%s:=:%s:=:%s:=:%s", n.Id.Print(), keyfieldName, fieldSource, fieldVal)) - } - } - - } - } - } else if nType.Field(i).Type.Kind() == reflect.Ptr { - nestInfoType := reflect.TypeOf(nVal.Field(i).Interface()) - nestInfoVal := reflect.ValueOf(nVal.Field(i).Interface()) - // nestConfType := reflect.TypeOf(nConfField.Type.Elem().FieldByName()) - for j := 0; j < nestInfoType.Elem().NumField(); j++ { - nestConfField, ok := nConfField.Type.Elem().FieldByName(nestInfoType.Elem().Field(j).Name) - if ok { - fieldName = nestConfField.Tag.Get("lopt") - } else { - fieldName = nestInfoType.Elem().Field(j).Name - } - if nestInfoType.Elem().Field(j).Type == reflect.TypeOf(node.Entry{}) { - entr := nestInfoVal.Elem().Field(j).Interface().(node.Entry) - fieldSource = entr.Source() - fieldVal = entr.Print() - nodeList.Output = append(nodeList.Output, - fmt.Sprintf("%s:=:%s:=:%s:=:%s", n.Id.Print(), fieldName, fieldSource, fieldVal)) - } else if nestInfoType.Elem().Field(j).Type == reflect.TypeOf(map[string]*node.Entry{}) { - for key, val := range nestInfoVal.Elem().Field(j).Interface().(map[string]*node.Entry) { - fieldName = fieldName + ":" + key - fieldSource = val.Source() - fieldVal = val.Print() - nodeList.Output = append(nodeList.Output, - fmt.Sprintf("%s:=:%s:=:%s:=:%s", n.Id.Print(), fieldName, fieldSource, fieldVal)) - } - } - } - } - + nodeList.Output = append(nodeList.Output, + fmt.Sprintf("%s:=:%s:=:%s:=:%s", "NODE", "FIELD", "PROFILE", "VALUE")) + fields := n.GetFields(wwapiv1.GetNodeList_FullAll == nodeGet.Type) + for _, f := range fields { + nodeList.Output = append(nodeList.Output, + fmt.Sprintf("%s:=:%s:=:%s:=:%s", n.Id.Print(), f.Field, f.Source, f.Value)) } } } diff --git a/internal/pkg/api/node/methods.go b/internal/pkg/api/node/methods.go deleted file mode 100644 index 8943d455..00000000 --- a/internal/pkg/api/node/methods.go +++ /dev/null @@ -1,79 +0,0 @@ -package apinode - -import ( - "fmt" - "reflect" - "strings" - - "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" - "github.com/hpcng/warewulf/internal/pkg/node" -) - -/* -Get the all the fields of a NodeInfo, keys are the lopt of NodeConf. -func GetFields(n interface{}) map[string]*wwapiv1.NodeField { - return getFieldsOf(n, node.NodeConf{}) -} -*/ - -func GetFields(n interface{}) map[string]*wwapiv1.NodeField { - nodeType := reflect.TypeOf(n) - nodeVal := reflect.ValueOf(n) - fieldMap := make(map[string]*wwapiv1.NodeField) - for i := 0; i < nodeType.NumField(); i++ { - switch nodeType.Field(i).Type { - case reflect.TypeOf(node.Entry{}): - var myField wwapiv1.NodeField - entry := nodeVal.Field(i).Interface().(node.Entry) - myField.Source = entry.Source() - myField.Value = entry.Get() - myField.Print = entry.Print() - fieldMap[nodeType.Field(i).Name] = &myField - case reflect.TypeOf([]string{}): - var myField wwapiv1.NodeField - entry := nodeVal.Field(i).Interface().([]string) - if len(entry) == 0 { - myField.Value = node.NoValue - myField.Print = node.NoValue - myField.Source = node.NoValue - } else { - myField.Value = strings.Join(entry, ",") - myField.Print = strings.Join(entry, ",") - myField.Source = node.NoValue - fieldMap[nodeType.Field(i).Name] = &myField - } - case reflect.TypeOf((*node.KernelEntry)(nil)): - entry := nodeVal.Field(i).Elem().Interface().(node.KernelEntry) - kernelMap := GetFields(entry) - for key, val := range kernelMap { - fieldMap["KernelEntry:"+key] = val - } - case reflect.TypeOf((*node.IpmiEntry)(nil)): - entry := nodeVal.Field(i).Elem().Interface().(node.IpmiEntry) - kernelMap := GetFields(entry) - for key, val := range kernelMap { - fieldMap["IpmiEntry:"+key] = val - } - case reflect.TypeOf(map[string]*node.Entry(nil)): - keyMap := nodeVal.Field(i).Interface().(map[string]*node.Entry) - for key, entr := range keyMap { - var myField wwapiv1.NodeField - myField.Source = entr.Source() - myField.Value = entr.Get() - myField.Print = entr.Print() - fieldMap["key:"+key] = &myField - } - case reflect.TypeOf(map[string]*node.NetDevEntry(nil)): - netMap := nodeVal.Field(i).Interface().(map[string]*node.NetDevEntry) - for net, netdev := range netMap { - netMapEntr := GetFields(*netdev) - for key, val := range netMapEntr { - fieldMap["NetDevEntry:"+net+":"+key] = val - } - } - default: - panic(fmt.Sprintf("Can't handle: %s\n", nodeType.Field(i).Type)) - } - } - return fieldMap -} diff --git a/internal/pkg/api/node/node.go b/internal/pkg/api/node/node.go index 13a10c15..f8d76565 100644 --- a/internal/pkg/api/node/node.go +++ b/internal/pkg/api/node/node.go @@ -8,9 +8,9 @@ import ( "os" "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/util" - warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/hpcng/warewulf/pkg/hostlist" "github.com/pkg/errors" @@ -270,15 +270,51 @@ func NodeSetParameterCheck(set *wwapiv1.NodeSetParameter, console bool) (nodeDB wwlog.Error(fmt.Sprintf("%v", err.Error())) return } - wwlog.Verbose("Node: %s, Deleting network device: %s", n.Id.Get(), set.NetdevDelete) delete(n.NetDevs, set.NetdevDelete) } + if set.PartitionDelete != "" { + deletedPart := false + for diskname, disk := range n.Disks { + if _, ok := disk.Partitions[set.PartitionDelete]; ok { + wwlog.Verbose("Node: %s, on disk %, deleting partition: %s", n.Id.Get(), diskname, set.PartitionDelete) + deletedPart = true + delete(disk.Partitions, set.PartitionDelete) + } + if !deletedPart { + wwlog.Error(fmt.Sprintf("%v", err.Error())) + err = fmt.Errorf("partition doesn't exist: %s", set.PartitionDelete) + return + } + } + } + if set.DiskDelete != "" { + if _, ok := n.Disks[set.DiskDelete]; !ok { + err = fmt.Errorf("disk doesn't exist: %s", set.DiskDelete) + wwlog.Error(fmt.Sprintf("%v", err.Error())) + return + } + wwlog.Verbose("Node: %s, deleting disk: %s", n.Id.Get(), set.DiskDelete) + delete(n.Disks, set.DiskDelete) + } + if set.FilesystemDelete != "" { + if _, ok := n.FileSystems[set.FilesystemDelete]; !ok { + err = fmt.Errorf("filesystem doesn't exist: %s", set.FilesystemDelete) + wwlog.Error(fmt.Sprintf("%v", err.Error())) + return + } + wwlog.Verbose("Node: %s, deleting filesystem: %s", n.Id.Get(), set.FilesystemDelete) + delete(n.FileSystems, set.FilesystemDelete) + } for _, key := range nodeConf.TagsDel { + wwlog.Debug("deleting tag %s", key) delete(n.Tags, key) } - for _, key := range nodeConf.Ipmi.TagsDel { - delete(n.Ipmi.Tags, key) + if nodeConf.Ipmi != nil { + for _, key := range nodeConf.Ipmi.TagsDel { + wwlog.Debug("deleting Ipmi tag %s", key) + delete(n.Ipmi.Tags, key) + } } for net := range nodeConf.NetDevs { for _, key := range nodeConf.NetDevs[net].TagsDel { diff --git a/internal/pkg/api/profile/list.go b/internal/pkg/api/profile/list.go index a06a4c42..dd153240 100644 --- a/internal/pkg/api/profile/list.go +++ b/internal/pkg/api/profile/list.go @@ -2,7 +2,6 @@ package apiprofile import ( "fmt" - "reflect" "sort" "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" @@ -30,99 +29,14 @@ func ProfileList(ShowOpt *wwapiv1.GetProfileList) (profileList wwapiv1.ProfileLi sort.Slice(profiles, func(i, j int) bool { return profiles[i].Id.Get() < profiles[j].Id.Get() }) - if ShowOpt.ShowAll { + if ShowOpt.ShowAll || ShowOpt.ShowFullAll { for _, p := range profiles { profileList.Output = append(profileList.Output, fmt.Sprintf("%s=%s=%s=%s", "PROFILE", "FIELD", "PROFILE", "VALUE")) - nType := reflect.TypeOf(p) - nVal := reflect.ValueOf(p) - nConfType := reflect.TypeOf(node.NodeConf{}) - for i := 0; i < nType.NumField(); i++ { - var fieldName, fieldSource, fieldVal string - nConfField, ok := nConfType.FieldByName(nType.Field(i).Name) - if ok { - fieldName = nConfField.Tag.Get("lopt") - } else { - fieldName = nType.Field(i).Name - } - if nType.Field(i).Type == reflect.TypeOf(node.Entry{}) { - entr := nVal.Field(i).Interface().(node.Entry) - fieldSource = entr.Source() - fieldVal = entr.Print() - profileList.Output = append(profileList.Output, - fmt.Sprintf("%s=%s=%s=%s", p.Id.Print(), fieldName, fieldSource, fieldVal)) - } else if nType.Field(i).Type == reflect.TypeOf(map[string]*node.Entry{}) { - entrMap := nVal.Field(i).Interface().(map[string]*node.Entry) - for key, val := range entrMap { - profileList.Output = append(profileList.Output, - fmt.Sprintf("%s=%s=%s=%s", p.Id.Print(), key, val.Source(), val.Print())) - } - } else if nType.Field(i).Type == reflect.TypeOf(map[string]*node.NetDevEntry{}) { - netDevs := nVal.Field(i).Interface().(map[string]*node.NetDevEntry) - for netName, netWork := range netDevs { - netInfoType := reflect.TypeOf(*netWork) - netInfoVal := reflect.ValueOf(*netWork) - netConfType := reflect.TypeOf(node.NetDevs{}) - for j := 0; j < netInfoType.NumField(); j++ { - netConfField, ok := netConfType.FieldByName(netInfoType.Field(j).Name) - if ok { - if netConfField.Tag.Get("lopt") != "nettagadd" { - fieldName = netName + ":" + netConfField.Tag.Get("lopt") - } else { - fieldName = netName + ":tag" - } - } else { - fieldName = netName + ":" + netInfoType.Field(j).Name - } - if netInfoType.Field(j).Type == reflect.TypeOf(node.Entry{}) { - entr := netInfoVal.Field(j).Interface().(node.Entry) - fieldSource = entr.Source() - fieldVal = entr.Print() - // only print fields with lopt - if netConfField.Tag.Get("lopt") != "" { - profileList.Output = append(profileList.Output, - fmt.Sprintf("%s=%s=%s=%s", p.Id.Print(), fieldName, fieldSource, fieldVal)) - } - } else if netInfoType.Field(j).Type == reflect.TypeOf(map[string]*node.Entry{}) { - for key, val := range netInfoVal.Field(j).Interface().(map[string]*node.Entry) { - keyfieldName := fieldName + ":" + key - fieldSource = val.Source() - fieldVal = val.Print() - profileList.Output = append(profileList.Output, - fmt.Sprintf("%s=%s=%s=%s", p.Id.Print(), keyfieldName, fieldSource, fieldVal)) - } - } - - } - } - } else if nType.Field(i).Type.Kind() == reflect.Ptr { - nestInfoType := reflect.TypeOf(nVal.Field(i).Interface()) - nestInfoVal := reflect.ValueOf(nVal.Field(i).Interface()) - // nestConfType := reflect.TypeOf(nConfField.Type.Elem().FieldByName()) - for j := 0; j < nestInfoType.Elem().NumField(); j++ { - nestConfField, ok := nConfField.Type.Elem().FieldByName(nestInfoType.Elem().Field(j).Name) - if ok { - fieldName = nestConfField.Tag.Get("lopt") - } else { - fieldName = nestInfoType.Elem().Field(j).Name - } - if nestInfoType.Elem().Field(j).Type == reflect.TypeOf(node.Entry{}) { - entr := nestInfoVal.Elem().Field(j).Interface().(node.Entry) - fieldSource = entr.Source() - fieldVal = entr.Print() - profileList.Output = append(profileList.Output, - fmt.Sprintf("%s=%s=%s=%s", p.Id.Print(), fieldName, fieldSource, fieldVal)) - } else if nestInfoType.Elem().Field(j).Type == reflect.TypeOf(map[string]*node.Entry{}) { - for key, val := range nestInfoVal.Elem().Field(j).Interface().(map[string]*node.Entry) { - fieldName = fieldName + ":" + key - fieldSource = val.Source() - fieldVal = val.Print() - profileList.Output = append(profileList.Output, - fmt.Sprintf("%s=%s=%s=%s", p.Id.Print(), fieldName, fieldSource, fieldVal)) - } - } - } - } + fields := p.GetFields(ShowOpt.ShowFullAll) + for _, f := range fields { + profileList.Output = append(profileList.Output, + fmt.Sprintf("%s=%s=%s=%s", p.Id.Print(), f.Field, f.Source, f.Value)) } } } else { diff --git a/internal/pkg/api/profile/profile.go b/internal/pkg/api/profile/profile.go index 5b0a50c3..e6ca6c68 100644 --- a/internal/pkg/api/profile/profile.go +++ b/internal/pkg/api/profile/profile.go @@ -14,7 +14,7 @@ import ( ) // NodeSet is the wwapiv1 implmentation for updating nodeinfo fields. -func ProfileSet(set *wwapiv1.NodeSetParameter) (err error) { +func ProfileSet(set *wwapiv1.ProfileSetParameter) (err error) { if set == nil { return fmt.Errorf("NodeAddParameter is nil") } @@ -31,7 +31,7 @@ func ProfileSet(set *wwapiv1.NodeSetParameter) (err error) { // 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 ProfileSetParameterCheck(set *wwapiv1.NodeSetParameter, console bool) (nodeDB node.NodeYaml, profileCount uint, err error) { +func ProfileSetParameterCheck(set *wwapiv1.ProfileSetParameter, console bool) (nodeDB node.NodeYaml, profileCount uint, err error) { if set == nil { err = fmt.Errorf("profile set parameter is nil") if console { @@ -40,7 +40,7 @@ func ProfileSetParameterCheck(set *wwapiv1.NodeSetParameter, console bool) (node } } - if set.NodeNames == nil { + if set.ProfileNames == nil { err = fmt.Errorf("profile set parameter: ProfileNames is nil") if console { fmt.Printf("%v\n", err) @@ -62,7 +62,7 @@ func ProfileSetParameterCheck(set *wwapiv1.NodeSetParameter, console bool) (node // Note: This does not do expansion on the nodes. - if set.AllNodes || (len(set.NodeNames) == 0) { + if set.AllProfiles || (len(set.ProfileNames) == 0) { if console { fmt.Printf("\n*** WARNING: This command will modify all profiles! ***\n\n") } @@ -82,12 +82,8 @@ func ProfileSetParameterCheck(set *wwapiv1.NodeSetParameter, console bool) (node } for _, p := range profiles { - if util.InSlice(set.NodeNames, p.Id.Get()) { + if util.InSlice(set.ProfileNames, p.Id.Get()) { wwlog.Verbose("Evaluating profile: %s", p.Id.Get()) - // Fix issue: https://github.com/hpcng/warewulf/issues/661 - if p.Id.Get() == "default" && len(p.NetDevs) == 0 { - set.NetdevDelete = p.Id.Get() - } p.SetFrom(&pConf) if set.NetdevDelete != "" { if _, ok := p.NetDevs[set.NetdevDelete]; !ok { @@ -98,6 +94,40 @@ func ProfileSetParameterCheck(set *wwapiv1.NodeSetParameter, console bool) (node wwlog.Verbose("Profile: %s, Deleting network device: %s", p.Id.Get(), set.NetdevDelete) delete(p.NetDevs, set.NetdevDelete) } + if set.PartitionDelete != "" { + deletedPart := false + for diskname, disk := range p.Disks { + if _, ok := disk.Partitions[set.PartitionDelete]; ok { + wwlog.Verbose("Node: %s, on disk %, deleting partition: %s", p.Id.Get(), diskname, set.PartitionDelete) + deletedPart = true + delete(disk.Partitions, set.PartitionDelete) + } + if !deletedPart { + wwlog.Error(fmt.Sprintf("%v", err.Error())) + err = fmt.Errorf("partition doesn't exist: %s", set.PartitionDelete) + return + } + } + } + if set.DiskDelete != "" { + if _, ok := p.Disks[set.DiskDelete]; !ok { + err = fmt.Errorf("disk doesn't exist: %s", set.DiskDelete) + wwlog.Error(fmt.Sprintf("%v", err.Error())) + return + } + wwlog.Verbose("Node: %s, deleting disk: %s", p.Id.Get(), set.DiskDelete) + delete(p.Disks, set.DiskDelete) + } + if set.FilesystemDelete != "" { + if _, ok := p.FileSystems[set.FilesystemDelete]; !ok { + err = fmt.Errorf("disk doesn't exist: %s", set.FilesystemDelete) + wwlog.Error(fmt.Sprintf("%v", err.Error())) + return + } + wwlog.Verbose("Node: %s, deleting filesystem: %s", p.Id.Get(), set.FilesystemDelete) + delete(p.FileSystems, set.FilesystemDelete) + } + for _, key := range pConf.TagsDel { delete(p.Tags, key) } @@ -127,7 +157,7 @@ func ProfileSetParameterCheck(set *wwapiv1.NodeSetParameter, console bool) (node Adds a new profile with the given name */ -func AddProfile(nsp *wwapiv1.NodeSetParameter) error { +func AddProfile(nsp *wwapiv1.ProfileSetParameter) error { if nsp == nil { return fmt.Errorf("NodeSetParameter is nill") } @@ -137,8 +167,8 @@ func AddProfile(nsp *wwapiv1.NodeSetParameter) error { return errors.Wrap(err, "Could not open database") } - if util.InSlice(nodeDB.ListAllProfiles(), nsp.NodeNames[0]) { - return errors.New(fmt.Sprintf("profile with name %s allready exists", nsp.NodeNames[0])) + if util.InSlice(nodeDB.ListAllProfiles(), nsp.ProfileNames[0]) { + return errors.New(fmt.Sprintf("profile with name %s allready exists", nsp.ProfileNames[0])) } var nodeConf node.NodeConf @@ -147,7 +177,7 @@ func AddProfile(nsp *wwapiv1.NodeSetParameter) error { return errors.Wrap(err, "failed to decode nodeConf") } - n, err := nodeDB.AddProfile(nsp.NodeNames[0]) + n, err := nodeDB.AddProfile(nsp.ProfileNames[0]) if err != nil { return errors.Wrap(err, "failed to add node") } diff --git a/internal/pkg/api/routes/v1/routes.proto b/internal/pkg/api/routes/v1/routes.proto index 336eba66..83b844c5 100644 --- a/internal/pkg/api/routes/v1/routes.proto +++ b/internal/pkg/api/routes/v1/routes.proto @@ -118,6 +118,7 @@ message GetNodeList { Network = 2; Long = 3; All = 4; + FullAll = 5; } ListType type = 7; repeated string Nodes = 8; @@ -131,7 +132,8 @@ message NodeList { // Request a profile list view message GetProfileList { bool ShowAll = 1; - repeated string Profiles = 2; + bool ShowFullAll = 2; + repeated string Profiles = 3; } // Get the formated output as string message ProfileList { @@ -169,13 +171,26 @@ message NodeDeleteParameter { // by Warewulf. message NodeSetParameter { string nodeConfYaml = 1; - string container = 2; - string netdevDelete = 14; - bool allNodes = 27; - bool force = 31; - repeated string nodeNames = 39; + string netdevDelete = 2; + string diskDelete = 3; + string partitionDelete = 4; + string filesystemDelete = 5; + bool allNodes = 6; + bool force = 7; + repeated string nodeNames = 8; +} +// ProfileSetParameter contains all fields for updating aspects of profiles managed. Basically a copy of NodeSetParameters +// by Warewulf. +message ProfileSetParameter { + string nodeConfYaml = 1; + string netdevDelete = 2; + string diskDelete = 3; + string partitionDelete = 4; + string filesystemDelete = 5; + bool allProfiles = 6; + bool force = 7; + repeated string profileNames = 8; } - // NodeStatus contains information about the imaging status per node. message NodeStatus { string nodeName = 1; // Name (Id) of the node. diff --git a/internal/pkg/api/routes/wwapiv1/routes.pb.go b/internal/pkg/api/routes/wwapiv1/routes.pb.go index e3dbce97..f13faca9 100644 --- a/internal/pkg/api/routes/wwapiv1/routes.pb.go +++ b/internal/pkg/api/routes/wwapiv1/routes.pb.go @@ -33,6 +33,7 @@ const ( GetNodeList_Network GetNodeList_ListType = 2 GetNodeList_Long GetNodeList_ListType = 3 GetNodeList_All GetNodeList_ListType = 4 + GetNodeList_FullAll GetNodeList_ListType = 5 ) // Enum value maps for GetNodeList_ListType. @@ -43,6 +44,7 @@ var ( 2: "Network", 3: "Long", 4: "All", + 5: "FullAll", } GetNodeList_ListType_value = map[string]int32{ "Simple": 0, @@ -50,6 +52,7 @@ var ( "Network": 2, "Long": 3, "All": 4, + "FullAll": 5, } ) @@ -1047,8 +1050,9 @@ type GetProfileList struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ShowAll bool `protobuf:"varint,1,opt,name=ShowAll,proto3" json:"ShowAll,omitempty"` - Profiles []string `protobuf:"bytes,2,rep,name=Profiles,proto3" json:"Profiles,omitempty"` + ShowAll bool `protobuf:"varint,1,opt,name=ShowAll,proto3" json:"ShowAll,omitempty"` + ShowFullAll bool `protobuf:"varint,2,opt,name=ShowFullAll,proto3" json:"ShowFullAll,omitempty"` + Profiles []string `protobuf:"bytes,3,rep,name=Profiles,proto3" json:"Profiles,omitempty"` } func (x *GetProfileList) Reset() { @@ -1090,6 +1094,13 @@ func (x *GetProfileList) GetShowAll() bool { return false } +func (x *GetProfileList) GetShowFullAll() bool { + if x != nil { + return x.ShowFullAll + } + return false +} + func (x *GetProfileList) GetProfiles() []string { if x != nil { return x.Profiles @@ -1350,12 +1361,14 @@ type NodeSetParameter struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NodeConfYaml string `protobuf:"bytes,1,opt,name=nodeConfYaml,proto3" json:"nodeConfYaml,omitempty"` - Container string `protobuf:"bytes,2,opt,name=container,proto3" json:"container,omitempty"` - NetdevDelete string `protobuf:"bytes,14,opt,name=netdevDelete,proto3" json:"netdevDelete,omitempty"` - AllNodes bool `protobuf:"varint,27,opt,name=allNodes,proto3" json:"allNodes,omitempty"` - Force bool `protobuf:"varint,31,opt,name=force,proto3" json:"force,omitempty"` - NodeNames []string `protobuf:"bytes,39,rep,name=nodeNames,proto3" json:"nodeNames,omitempty"` + NodeConfYaml string `protobuf:"bytes,1,opt,name=nodeConfYaml,proto3" json:"nodeConfYaml,omitempty"` + NetdevDelete string `protobuf:"bytes,2,opt,name=netdevDelete,proto3" json:"netdevDelete,omitempty"` + DiskDelete string `protobuf:"bytes,3,opt,name=diskDelete,proto3" json:"diskDelete,omitempty"` + PartitionDelete string `protobuf:"bytes,4,opt,name=partitionDelete,proto3" json:"partitionDelete,omitempty"` + FilesystemDelete string `protobuf:"bytes,5,opt,name=filesystemDelete,proto3" json:"filesystemDelete,omitempty"` + AllNodes bool `protobuf:"varint,6,opt,name=allNodes,proto3" json:"allNodes,omitempty"` + Force bool `protobuf:"varint,7,opt,name=force,proto3" json:"force,omitempty"` + NodeNames []string `protobuf:"bytes,8,rep,name=nodeNames,proto3" json:"nodeNames,omitempty"` } func (x *NodeSetParameter) Reset() { @@ -1397,16 +1410,30 @@ func (x *NodeSetParameter) GetNodeConfYaml() string { return "" } -func (x *NodeSetParameter) GetContainer() string { +func (x *NodeSetParameter) GetNetdevDelete() string { if x != nil { - return x.Container + return x.NetdevDelete } return "" } -func (x *NodeSetParameter) GetNetdevDelete() string { +func (x *NodeSetParameter) GetDiskDelete() string { if x != nil { - return x.NetdevDelete + return x.DiskDelete + } + return "" +} + +func (x *NodeSetParameter) GetPartitionDelete() string { + if x != nil { + return x.PartitionDelete + } + return "" +} + +func (x *NodeSetParameter) GetFilesystemDelete() string { + if x != nil { + return x.FilesystemDelete } return "" } @@ -1432,6 +1459,111 @@ func (x *NodeSetParameter) GetNodeNames() []string { return nil } +// ProfileSetParameter contains all fields for updating aspects of profiles managed. Basically a copy of NodeSetParameters +// by Warewulf. +type ProfileSetParameter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NodeConfYaml string `protobuf:"bytes,1,opt,name=nodeConfYaml,proto3" json:"nodeConfYaml,omitempty"` + NetdevDelete string `protobuf:"bytes,2,opt,name=netdevDelete,proto3" json:"netdevDelete,omitempty"` + DiskDelete string `protobuf:"bytes,3,opt,name=diskDelete,proto3" json:"diskDelete,omitempty"` + PartitionDelete string `protobuf:"bytes,4,opt,name=partitionDelete,proto3" json:"partitionDelete,omitempty"` + FilesystemDelete string `protobuf:"bytes,5,opt,name=filesystemDelete,proto3" json:"filesystemDelete,omitempty"` + AllProfiles bool `protobuf:"varint,6,opt,name=allProfiles,proto3" json:"allProfiles,omitempty"` + Force bool `protobuf:"varint,7,opt,name=force,proto3" json:"force,omitempty"` + ProfileNames []string `protobuf:"bytes,8,rep,name=profileNames,proto3" json:"profileNames,omitempty"` +} + +func (x *ProfileSetParameter) Reset() { + *x = ProfileSetParameter{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProfileSetParameter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProfileSetParameter) ProtoMessage() {} + +func (x *ProfileSetParameter) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[22] + 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 ProfileSetParameter.ProtoReflect.Descriptor instead. +func (*ProfileSetParameter) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{22} +} + +func (x *ProfileSetParameter) GetNodeConfYaml() string { + if x != nil { + return x.NodeConfYaml + } + return "" +} + +func (x *ProfileSetParameter) GetNetdevDelete() string { + if x != nil { + return x.NetdevDelete + } + return "" +} + +func (x *ProfileSetParameter) GetDiskDelete() string { + if x != nil { + return x.DiskDelete + } + return "" +} + +func (x *ProfileSetParameter) GetPartitionDelete() string { + if x != nil { + return x.PartitionDelete + } + return "" +} + +func (x *ProfileSetParameter) GetFilesystemDelete() string { + if x != nil { + return x.FilesystemDelete + } + return "" +} + +func (x *ProfileSetParameter) GetAllProfiles() bool { + if x != nil { + return x.AllProfiles + } + return false +} + +func (x *ProfileSetParameter) GetForce() bool { + if x != nil { + return x.Force + } + return false +} + +func (x *ProfileSetParameter) GetProfileNames() []string { + if x != nil { + return x.ProfileNames + } + return nil +} + // NodeStatus contains information about the imaging status per node. type NodeStatus struct { state protoimpl.MessageState @@ -1448,7 +1580,7 @@ type NodeStatus struct { func (x *NodeStatus) Reset() { *x = NodeStatus{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[22] + mi := &file_routes_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1461,7 +1593,7 @@ func (x *NodeStatus) String() string { func (*NodeStatus) ProtoMessage() {} func (x *NodeStatus) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[22] + mi := &file_routes_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1474,7 +1606,7 @@ func (x *NodeStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeStatus.ProtoReflect.Descriptor instead. func (*NodeStatus) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{22} + return file_routes_proto_rawDescGZIP(), []int{23} } func (x *NodeStatus) GetNodeName() string { @@ -1524,7 +1656,7 @@ type NodeStatusResponse struct { func (x *NodeStatusResponse) Reset() { *x = NodeStatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[23] + mi := &file_routes_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1537,7 +1669,7 @@ func (x *NodeStatusResponse) String() string { func (*NodeStatusResponse) ProtoMessage() {} func (x *NodeStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[23] + mi := &file_routes_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1550,7 +1682,7 @@ func (x *NodeStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeStatusResponse.ProtoReflect.Descriptor instead. func (*NodeStatusResponse) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{23} + return file_routes_proto_rawDescGZIP(), []int{24} } func (x *NodeStatusResponse) GetNodeStatus() []*NodeStatus { @@ -1574,7 +1706,7 @@ type VersionResponse struct { func (x *VersionResponse) Reset() { *x = VersionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[24] + mi := &file_routes_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1587,7 +1719,7 @@ func (x *VersionResponse) String() string { func (*VersionResponse) ProtoMessage() {} func (x *VersionResponse) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[24] + mi := &file_routes_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1600,7 +1732,7 @@ func (x *VersionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VersionResponse.ProtoReflect.Descriptor instead. func (*VersionResponse) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{24} + return file_routes_proto_rawDescGZIP(), []int{25} } func (x *VersionResponse) GetApiPrefix() string { @@ -1636,7 +1768,7 @@ type CanWriteConfig struct { func (x *CanWriteConfig) Reset() { *x = CanWriteConfig{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[25] + mi := &file_routes_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1649,7 +1781,7 @@ func (x *CanWriteConfig) String() string { func (*CanWriteConfig) ProtoMessage() {} func (x *CanWriteConfig) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[25] + mi := &file_routes_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1662,7 +1794,7 @@ func (x *CanWriteConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use CanWriteConfig.ProtoReflect.Descriptor instead. func (*CanWriteConfig) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{25} + return file_routes_proto_rawDescGZIP(), []int{26} } func (x *CanWriteConfig) GetCanWriteConfig() bool { @@ -1802,151 +1934,179 @@ var file_routes_proto_rawDesc = []byte{ 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, 0x99, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, + 0x6f, 0x64, 0x65, 0x73, 0x22, 0xa6, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x6f, 0x64, 0x65, - 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x40, + 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x4d, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x70, 0x6d, 0x69, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x6f, 0x6e, 0x67, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x6c, 0x6c, 0x10, 0x04, - 0x22, 0x22, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, - 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x22, 0x46, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x6c, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x6c, 0x6c, - 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x25, 0x0a, 0x0b, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x4f, - 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x4f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x22, 0x7e, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, - 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, - 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x66, - 0x6f, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x68, 0x61, 0x73, 0x68, 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, 0x22, 0x48, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x59, 0x61, 0x6d, 0x6c, 0x12, - 0x28, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x4d, 0x61, 0x70, 0x59, 0x61, - 0x6d, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x4d, 0x61, 0x70, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, - 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0x5d, 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, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0xc8, 0x01, 0x0a, - 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, - 0x66, 0x59, 0x61, 0x6d, 0x6c, 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, 0x22, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, - 0x76, 0x44, 0x65, 0x6c, 0x65, 0x74, 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, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x1f, 0x20, 0x01, + 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x75, 0x6c, 0x6c, 0x41, 0x6c, 0x6c, 0x10, 0x05, 0x22, 0x22, 0x0a, + 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x22, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4c, + 0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x12, 0x20, 0x0a, + 0x0b, 0x53, 0x68, 0x6f, 0x77, 0x46, 0x75, 0x6c, 0x6c, 0x41, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0b, 0x53, 0x68, 0x6f, 0x77, 0x46, 0x75, 0x6c, 0x6c, 0x41, 0x6c, 0x6c, 0x12, + 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x25, 0x0a, 0x0b, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x4f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x22, 0x7e, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, + 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, + 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, + 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x68, 0x61, 0x73, 0x68, 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, 0x22, 0x48, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x28, + 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x4d, 0x61, 0x70, 0x59, 0x61, 0x6d, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, + 0x66, 0x4d, 0x61, 0x70, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0x5d, 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, 0x27, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, - 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 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, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, + 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0xa0, 0x02, 0x0a, 0x10, + 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x65, 0x74, 0x64, + 0x65, 0x76, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x73, 0x6b, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, + 0x73, 0x6b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x70, 0x61, 0x72, 0x74, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x08, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, + 0x72, 0x63, 0x65, 0x18, 0x07, 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, 0x08, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xaf, + 0x02, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, + 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, + 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x65, + 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x64, 0x69, 0x73, 0x6b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, 0x73, 0x6b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x28, + 0x0a, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x22, 0x0a, 0x0c, + 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 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, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x61, 0x72, 0x65, 0x77, 0x75, 0x6c, 0x66, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x38, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x57, 0x72, - 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x61, 0x6e, - 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0e, 0x63, 0x61, 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 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, + 0x22, 0x38, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x63, 0x61, 0x6e, 0x57, + 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 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, + 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, 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, + 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 ( @@ -1962,7 +2122,7 @@ func file_routes_proto_rawDescGZIP() []byte { } var file_routes_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_routes_proto_msgTypes = make([]protoimpl.MessageInfo, 32) +var file_routes_proto_msgTypes = make([]protoimpl.MessageInfo, 33) var file_routes_proto_goTypes = []interface{}{ (GetNodeList_ListType)(0), // 0: wwapi.v1.GetNodeList.ListType (*NodeDBHash)(nil), // 1: wwapi.v1.NodeDBHash @@ -1987,29 +2147,30 @@ var file_routes_proto_goTypes = []interface{}{ (*NodeYaml)(nil), // 20: wwapi.v1.NodeYaml (*NodeDeleteParameter)(nil), // 21: wwapi.v1.NodeDeleteParameter (*NodeSetParameter)(nil), // 22: wwapi.v1.NodeSetParameter - (*NodeStatus)(nil), // 23: wwapi.v1.NodeStatus - (*NodeStatusResponse)(nil), // 24: wwapi.v1.NodeStatusResponse - (*VersionResponse)(nil), // 25: wwapi.v1.VersionResponse - (*CanWriteConfig)(nil), // 26: wwapi.v1.CanWriteConfig - nil, // 27: wwapi.v1.NetDev.FieldEntry - nil, // 28: wwapi.v1.NetDev.TagsEntry - nil, // 29: wwapi.v1.NodeInfo.FieldsEntry - nil, // 30: wwapi.v1.NodeInfo.NetDevsEntry - nil, // 31: wwapi.v1.NodeInfo.TagsEntry - nil, // 32: wwapi.v1.NodeInfo.KeysEntry - (*empty.Empty)(nil), // 33: google.protobuf.Empty + (*ProfileSetParameter)(nil), // 23: wwapi.v1.ProfileSetParameter + (*NodeStatus)(nil), // 24: wwapi.v1.NodeStatus + (*NodeStatusResponse)(nil), // 25: wwapi.v1.NodeStatusResponse + (*VersionResponse)(nil), // 26: wwapi.v1.VersionResponse + (*CanWriteConfig)(nil), // 27: wwapi.v1.CanWriteConfig + nil, // 28: wwapi.v1.NetDev.FieldEntry + nil, // 29: wwapi.v1.NetDev.TagsEntry + nil, // 30: wwapi.v1.NodeInfo.FieldsEntry + nil, // 31: wwapi.v1.NodeInfo.NetDevsEntry + nil, // 32: wwapi.v1.NodeInfo.TagsEntry + nil, // 33: wwapi.v1.NodeInfo.KeysEntry + (*empty.Empty)(nil), // 34: google.protobuf.Empty } var file_routes_proto_depIdxs = []int32{ 5, // 0: wwapi.v1.ContainerListResponse.containers:type_name -> wwapi.v1.ContainerInfo - 27, // 1: wwapi.v1.NetDev.Field:type_name -> wwapi.v1.NetDev.FieldEntry - 28, // 2: wwapi.v1.NetDev.Tags:type_name -> wwapi.v1.NetDev.TagsEntry - 29, // 3: wwapi.v1.NodeInfo.Fields:type_name -> wwapi.v1.NodeInfo.FieldsEntry - 30, // 4: wwapi.v1.NodeInfo.NetDevs:type_name -> wwapi.v1.NodeInfo.NetDevsEntry - 31, // 5: wwapi.v1.NodeInfo.Tags:type_name -> wwapi.v1.NodeInfo.TagsEntry - 32, // 6: wwapi.v1.NodeInfo.Keys:type_name -> wwapi.v1.NodeInfo.KeysEntry + 28, // 1: wwapi.v1.NetDev.Field:type_name -> wwapi.v1.NetDev.FieldEntry + 29, // 2: wwapi.v1.NetDev.Tags:type_name -> wwapi.v1.NetDev.TagsEntry + 30, // 3: wwapi.v1.NodeInfo.Fields:type_name -> wwapi.v1.NodeInfo.FieldsEntry + 31, // 4: wwapi.v1.NodeInfo.NetDevs:type_name -> wwapi.v1.NodeInfo.NetDevsEntry + 32, // 5: wwapi.v1.NodeInfo.Tags:type_name -> wwapi.v1.NodeInfo.TagsEntry + 33, // 6: wwapi.v1.NodeInfo.Keys:type_name -> wwapi.v1.NodeInfo.KeysEntry 13, // 7: wwapi.v1.NodeListResponse.nodes:type_name -> wwapi.v1.NodeInfo 0, // 8: wwapi.v1.GetNodeList.type:type_name -> wwapi.v1.GetNodeList.ListType - 23, // 9: wwapi.v1.NodeStatusResponse.nodeStatus:type_name -> wwapi.v1.NodeStatus + 24, // 9: wwapi.v1.NodeStatusResponse.nodeStatus:type_name -> wwapi.v1.NodeStatus 11, // 10: wwapi.v1.NetDev.FieldEntry.value:type_name -> wwapi.v1.NodeField 11, // 11: wwapi.v1.NetDev.TagsEntry.value:type_name -> wwapi.v1.NodeField 11, // 12: wwapi.v1.NodeInfo.FieldsEntry.value:type_name -> wwapi.v1.NodeField @@ -2019,25 +2180,25 @@ var file_routes_proto_depIdxs = []int32{ 2, // 16: wwapi.v1.WWApi.ContainerBuild:input_type -> wwapi.v1.ContainerBuildParameter 3, // 17: wwapi.v1.WWApi.ContainerDelete:input_type -> wwapi.v1.ContainerDeleteParameter 4, // 18: wwapi.v1.WWApi.ContainerImport:input_type -> wwapi.v1.ContainerImportParameter - 33, // 19: wwapi.v1.WWApi.ContainerList:input_type -> google.protobuf.Empty + 34, // 19: wwapi.v1.WWApi.ContainerList:input_type -> google.protobuf.Empty 7, // 20: wwapi.v1.WWApi.ContainerShow:input_type -> wwapi.v1.ContainerShowParameter 19, // 21: wwapi.v1.WWApi.NodeAdd:input_type -> wwapi.v1.NodeAddParameter 21, // 22: wwapi.v1.WWApi.NodeDelete:input_type -> wwapi.v1.NodeDeleteParameter 10, // 23: wwapi.v1.WWApi.NodeList:input_type -> wwapi.v1.NodeNames 22, // 24: wwapi.v1.WWApi.NodeSet:input_type -> wwapi.v1.NodeSetParameter 10, // 25: wwapi.v1.WWApi.NodeStatus:input_type -> wwapi.v1.NodeNames - 33, // 26: wwapi.v1.WWApi.Version:input_type -> google.protobuf.Empty + 34, // 26: wwapi.v1.WWApi.Version:input_type -> google.protobuf.Empty 6, // 27: wwapi.v1.WWApi.ContainerBuild:output_type -> wwapi.v1.ContainerListResponse - 33, // 28: wwapi.v1.WWApi.ContainerDelete:output_type -> google.protobuf.Empty + 34, // 28: wwapi.v1.WWApi.ContainerDelete:output_type -> google.protobuf.Empty 6, // 29: wwapi.v1.WWApi.ContainerImport:output_type -> wwapi.v1.ContainerListResponse 6, // 30: wwapi.v1.WWApi.ContainerList:output_type -> wwapi.v1.ContainerListResponse 8, // 31: wwapi.v1.WWApi.ContainerShow:output_type -> wwapi.v1.ContainerShowResponse 14, // 32: wwapi.v1.WWApi.NodeAdd:output_type -> wwapi.v1.NodeListResponse - 33, // 33: wwapi.v1.WWApi.NodeDelete:output_type -> google.protobuf.Empty + 34, // 33: wwapi.v1.WWApi.NodeDelete:output_type -> google.protobuf.Empty 14, // 34: wwapi.v1.WWApi.NodeList:output_type -> wwapi.v1.NodeListResponse 14, // 35: wwapi.v1.WWApi.NodeSet:output_type -> wwapi.v1.NodeListResponse - 24, // 36: wwapi.v1.WWApi.NodeStatus:output_type -> wwapi.v1.NodeStatusResponse - 25, // 37: wwapi.v1.WWApi.Version:output_type -> wwapi.v1.VersionResponse + 25, // 36: wwapi.v1.WWApi.NodeStatus:output_type -> wwapi.v1.NodeStatusResponse + 26, // 37: wwapi.v1.WWApi.Version:output_type -> wwapi.v1.VersionResponse 27, // [27:38] is the sub-list for method output_type 16, // [16:27] is the sub-list for method input_type 16, // [16:16] is the sub-list for extension type_name @@ -2316,7 +2477,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeStatus); i { + switch v := v.(*ProfileSetParameter); i { case 0: return &v.state case 1: @@ -2328,7 +2489,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeStatusResponse); i { + switch v := v.(*NodeStatus); i { case 0: return &v.state case 1: @@ -2340,7 +2501,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VersionResponse); i { + switch v := v.(*NodeStatusResponse); i { case 0: return &v.state case 1: @@ -2352,6 +2513,18 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[25].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 + } + } + file_routes_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CanWriteConfig); i { case 0: return &v.state @@ -2370,7 +2543,7 @@ func file_routes_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_routes_proto_rawDesc, NumEnums: 1, - NumMessages: 32, + NumMessages: 33, NumExtensions: 0, NumServices: 1, }, diff --git a/internal/pkg/node/flags.go b/internal/pkg/node/flags.go index e15b769c..2b5488d5 100644 --- a/internal/pkg/node/flags.go +++ b/internal/pkg/node/flags.go @@ -17,46 +17,48 @@ must be called, as the commandline parser returns e.g. netip.IP objects which mu back to strings. */ func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command, excludeList []string) (converters []func() error) { - nodeInfoType := reflect.TypeOf(nodeConf) - nodeInfoVal := reflect.ValueOf(nodeConf) + /* + nodeInfoType := reflect.TypeOf(nodeConf) + nodeInfoVal := reflect.ValueOf(nodeConf) + */ + return RecursiveCreateFlags(nodeConf, baseCmd, excludeList) +} + +func RecursiveCreateFlags(obj interface{}, baseCmd *cobra.Command, excludeList []string) (converters []func() error) { // now iterate of every field + nodeInfoType := reflect.TypeOf(obj) + nodeInfoVal := reflect.ValueOf(obj) for i := 0; i < nodeInfoVal.Elem().NumField(); i++ { if nodeInfoType.Elem().Field(i).Tag.Get("comment") != "" && !util.InSlice(excludeList, nodeInfoType.Elem().Field(i).Tag.Get("lopt")) { field := nodeInfoVal.Elem().Field(i) converters = append(converters, createFlags(baseCmd, excludeList, nodeInfoType.Elem().Field(i), &field)...) } else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr { - nestType := reflect.TypeOf(nodeInfoVal.Elem().Field(i).Interface()) - nestVal := reflect.ValueOf(nodeInfoVal.Elem().Field(i).Interface()) - for j := 0; j < nestType.Elem().NumField(); j++ { - field := nestVal.Elem().Field(j) - converters = append(converters, createFlags(baseCmd, excludeList, nestType.Elem().Field(j), &field)...) - } - } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string]*NetDevs(nil)) { - netMap := nodeInfoVal.Elem().Field(i).Interface().(map[string]*NetDevs) - // add a default network so that it can hold values - key := "default" - if len(netMap) == 0 { - netMap[key] = new(NetDevs) - } else { - for keyIt := range netMap { - key = keyIt - break + newConv := RecursiveCreateFlags(nodeInfoVal.Elem().Field(i).Interface(), baseCmd, excludeList) + converters = append(converters, newConv...) + + } else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Map && + nodeInfoType.Elem().Field(i).Type != reflect.TypeOf(map[string]string{}) { + // add a map with key UNDEF so that it can hold values N.B. UNDEF can never be added through command line + key := reflect.ValueOf("UNDEF") + if nodeInfoVal.Elem().Field(i).Len() == 0 { + if nodeInfoVal.Elem().Field(i).IsNil() { + nodeInfoVal.Elem().Field(i).Set(reflect.MakeMap(nodeInfoType.Elem().Field(i).Type)) } + newPtr := reflect.New(nodeInfoType.Elem().Field(i).Type.Elem().Elem()) + nodeInfoVal.Elem().Field(i).SetMapIndex(key, newPtr) + } else { + key = nodeInfoVal.Elem().Field(i).MapKeys()[0] } - netType := reflect.TypeOf(netMap[key]) - netVal := reflect.ValueOf(netMap[key]) - for j := 0; j < netType.Elem().NumField(); j++ { - field := netVal.Elem().Field(j) - converters = append(converters, createFlags(baseCmd, excludeList, netType.Elem().Field(j), &field)...) - } + newConv := RecursiveCreateFlags(nodeInfoVal.Elem().Field(i).MapIndex(key).Interface(), baseCmd, excludeList) + converters = append(converters, newConv...) } } return converters } /* -Helper function to create the different PerisitantFlags() for different types. +Helper function to create the different PersistentFlags() for different types. */ func createFlags(baseCmd *cobra.Command, excludeList []string, myType reflect.StructField, myVal *reflect.Value) (converters []func() error) { diff --git a/internal/pkg/node/list.go b/internal/pkg/node/list.go new file mode 100644 index 00000000..4a728ece --- /dev/null +++ b/internal/pkg/node/list.go @@ -0,0 +1,79 @@ +package node + +import ( + "reflect" +) + +/* +struct to hold the fields of GetFields +*/ +type NodeFields struct { + Field string + Source string + Value string +} + +/* +Get all the info out of NodeInfo. If emptyFields is set true, all fields +are shown not only the ones with effective values +*/ +func (node *NodeInfo) GetFields(emptyFields bool) (output []NodeFields) { + return recursiveFields(node, emptyFields, "") +} + +/* +Internal function which travels through all fields of a NodeInfo and for this +reason needs tb called via interface{} +*/ +func recursiveFields(obj interface{}, emptyFields bool, prefix string) (output []NodeFields) { + valObj := reflect.ValueOf(obj) + typeObj := reflect.TypeOf(obj) + for i := 0; i < typeObj.Elem().NumField(); i++ { + if typeObj.Elem().Field(i).Type == reflect.TypeOf(Entry{}) { + myField := valObj.Elem().Field(i).Interface().(Entry) + if emptyFields || myField.Get() != "" { + output = append(output, NodeFields{ + Field: prefix + typeObj.Elem().Field(i).Name, + Source: myField.Source(), + Value: myField.Print(), + }) + } + } else if typeObj.Elem().Field(i).Type == reflect.TypeOf(map[string]*Entry{}) { + for key, val := range valObj.Elem().Field(i).Interface().(map[string]*Entry) { + if emptyFields || val.Get() != "" { + output = append(output, NodeFields{ + Field: prefix + typeObj.Elem().Field(i).Name + "[" + key + "]", + Source: val.Source(), + Value: val.Print(), + }) + } + } + if valObj.Elem().Field(i).Len() == 0 && emptyFields { + output = append(output, NodeFields{ + Field: prefix + typeObj.Elem().Field(i).Name + "[]", + }) + } + } else if typeObj.Elem().Field(i).Type.Kind() == reflect.Map { + mapIter := valObj.Elem().Field(i).MapRange() + for mapIter.Next() { + nestedOut := recursiveFields(mapIter.Value().Interface(), emptyFields, prefix+typeObj.Elem().Field(i).Name+"["+mapIter.Key().String()+"].") + if len(nestedOut) == 0 { + output = append(output, NodeFields{ + Field: prefix + typeObj.Elem().Field(i).Name + "[" + mapIter.Key().String() + "]", + }) + } else { + output = append(output, nestedOut...) + } + } + if valObj.Elem().Field(i).Len() == 0 && emptyFields { + output = append(output, NodeFields{ + Field: prefix + typeObj.Elem().Field(i).Name + "[]", + }) + } + } else if typeObj.Elem().Field(i).Type.Kind() == reflect.Ptr { + nestedOut := recursiveFields(valObj.Elem().Field(i).Interface(), emptyFields, prefix+typeObj.Elem().Field(i).Name+".") + output = append(output, nestedOut...) + } + } + return +} diff --git a/internal/pkg/node/methods.go b/internal/pkg/node/methods.go index b92bb361..88ee41ac 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -399,12 +399,15 @@ func GetByName(node interface{}, name string) (string, error) { /* Check if the Netdev is empty, so has no values set */ -func (dev *NetDevs) Empty() bool { - if dev == nil { +func ObjectIsEmpty(obj interface{}) bool { + if obj == nil { return true } - varType := reflect.TypeOf(*dev) - varVal := reflect.ValueOf(*dev) + varType := reflect.TypeOf(obj) + varVal := reflect.ValueOf(obj) + if varType.Kind() == reflect.Ptr && !varVal.IsNil() { + return ObjectIsEmpty(varVal.Elem().Interface()) + } if varVal.IsZero() { return true } @@ -418,6 +421,10 @@ func (dev *NetDevs) Empty() bool { if len(varVal.Field(i).Interface().(map[string]string)) != 0 { return false } + } else if varType.Field(i).Type.Kind() == reflect.Ptr { + if !ObjectIsEmpty(varVal.Field(i).Interface()) { + return false + } } } return true diff --git a/internal/pkg/node/methods_test.go b/internal/pkg/node/methods_test.go index d17f1529..56e33356 100644 --- a/internal/pkg/node/methods_test.go +++ b/internal/pkg/node/methods_test.go @@ -7,25 +7,25 @@ func Test_Empty(t *testing.T) { var netdevPtr *NetDevs t.Run("test for empty", func(t *testing.T) { - if netdev.Empty() != true { + if ObjectIsEmpty(netdev) != true { t.Errorf("netdev must be empty") } }) t.Run("test for non empty", func(t *testing.T) { netdev.Device = "foo" - if netdev.Empty() == true { + if ObjectIsEmpty(netdev) == true { t.Errorf("netdev must be non empty") } }) t.Run("test for nil pointer", func(t *testing.T) { - if netdevPtr.Empty() != true { + if ObjectIsEmpty(netdevPtr) != true { t.Errorf("netdev must be empty") } }) t.Run("test for pointer assigned", func(t *testing.T) { netdev.Ipaddr = "10.10.10.1" netdevPtr = &netdev - if netdevPtr.Empty() == true { + if ObjectIsEmpty(netdevPtr) == true { t.Errorf("netdev must be empty") } }) diff --git a/internal/pkg/node/transformers.go b/internal/pkg/node/transformers.go index 2dff47ea..1ea8fb7a 100644 --- a/internal/pkg/node/transformers.go +++ b/internal/pkg/node/transformers.go @@ -256,63 +256,47 @@ func recursiveFlatten(strct interface{}) { } /* -Create a string slice, where every element represents a yaml entry +Create a string slice, where every element represents a yaml entry, used for node/profile edit +in order to get a summary of all available elements */ -func (nodeConf *NodeConf) UnmarshalConf(excludeList []string) (lines []string) { - nodeInfoType := reflect.TypeOf(nodeConf) - nodeInfoVal := reflect.ValueOf(nodeConf) +func UnmarshalConf(obj interface{}, excludeList []string) (lines []string) { + objType := reflect.TypeOf(obj) // now iterate of every field - for i := 0; i < nodeInfoVal.Elem().NumField(); i++ { - if nodeInfoType.Elem().Field(i).Tag.Get("lopt") != "" { - if ymlStr, ok := getYamlString(nodeInfoType.Elem().Field(i), excludeList); ok { + for i := 0; i < objType.NumField(); i++ { + if objType.Field(i).Tag.Get("comment") != "" { + if ymlStr, ok := getYamlString(objType.Field(i), excludeList); ok { lines = append(lines, ymlStr...) } - } else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr { - nestType := reflect.TypeOf(nodeInfoVal.Elem().Field(i).Interface()) - if ymlStr, ok := getYamlString(nodeInfoType.Elem().Field(i), excludeList); ok { - lines = append(lines, ymlStr...) + } + if objType.Field(i).Type.Kind() == reflect.Ptr && objType.Field(i).Tag.Get("yaml") != "" { + typeLine := objType.Field(i).Tag.Get("yaml") + if len(strings.Split(typeLine, ",")) > 1 { + typeLine = strings.Split(typeLine, ",")[0] + ":" } - for j := 0; j < nestType.Elem().NumField(); j++ { - if nestType.Elem().Field(j).Tag.Get("lopt") != "" && - !util.InSlice(excludeList, nestType.Elem().Field(j).Tag.Get("lopt")) { - if ymlStr, ok := getYamlString(nestType.Elem().Field(j), excludeList); ok { - for _, str := range ymlStr { - lines = append(lines, " "+str) - } - } - } + lines = append(lines, typeLine) + nestedLine := UnmarshalConf(reflect.New(objType.Field(i).Type.Elem()).Elem().Interface(), excludeList) + for _, ln := range nestedLine { + lines = append(lines, " "+ln) } - } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string]*NetDevs(nil)) { - netMap := nodeInfoVal.Elem().Field(i).Interface().(map[string]*NetDevs) - // add a default network so that it can hold values - key := "default" - if len(netMap) == 0 { - netMap[key] = new(NetDevs) - } else { - for keyIt := range netMap { - key = keyIt - break - } + } else if objType.Field(i).Type.Kind() == reflect.Map && objType.Field(i).Type.Elem().Kind() == reflect.Ptr { + typeLine := objType.Field(i).Tag.Get("yaml") + if len(strings.Split(typeLine, ",")) > 1 { + typeLine = strings.Split(typeLine, ",")[0] + ":" } - if ymlStr, ok := getYamlString(nodeInfoType.Elem().Field(i), excludeList); ok { - lines = append(lines, ymlStr[0]+":", " "+key+":") - netType := reflect.TypeOf(netMap[key]) - for j := 0; j < netType.Elem().NumField(); j++ { - if ymlStr, ok := getYamlString(netType.Elem().Field(j), excludeList); ok { - for _, str := range ymlStr { - lines = append(lines, " "+str) - } - } - } // lines - } // this - } //not - } //do + lines = append(lines, typeLine, " element:") + nestedLine := UnmarshalConf(reflect.New(objType.Field(i).Type.Elem().Elem()).Elem().Interface(), excludeList) + for _, ln := range nestedLine { + lines = append(lines, " "+ln) + } + } + } return lines } /* Get the string of the yaml tag */ + func getYamlString(myType reflect.StructField, excludeList []string) ([]string, bool) { ymlStr := myType.Tag.Get("yaml") if len(strings.Split(ymlStr, ",")) > 1 { @@ -320,11 +304,15 @@ func getYamlString(myType reflect.StructField, excludeList []string) ([]string, } if util.InSlice(excludeList, ymlStr) { return []string{""}, false - } else if myType.Tag.Get("lopt") == "" && myType.Type.Kind() == reflect.String { + } else if myType.Tag.Get("comment") == "" && myType.Type.Kind() == reflect.String { return []string{""}, false } if myType.Type.Kind() == reflect.String { - ymlStr += ": string" + fieldType := myType.Tag.Get("type") + if fieldType == "" { + fieldType = "string" + } + ymlStr += ": " + fieldType return []string{ymlStr}, true } else if myType.Type == reflect.TypeOf([]string{}) { return []string{ymlStr + ":", " - string"}, true @@ -347,7 +335,6 @@ func (nodeConf *NodeConf) SetLopt(lopt string, value string) (found bool) { nodeInfoVal := reflect.ValueOf(nodeConf) // try to find the normal fields, networks come later for i := 0; i < nodeInfoVal.Elem().NumField(); i++ { - //fmt.Println(nodeInfoType.Elem().Field(i).Tag.Get("lopt"), lopt) if nodeInfoType.Elem().Field(i).Tag.Get("lopt") == lopt { if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.String { wwlog.Verbose("Found lopt %s mapping to %s, setting to %s\n", From a7df560a3026c858860c035cc066db63c2ed04a2 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Tue, 15 Aug 2023 17:37:03 -0600 Subject: [PATCH 61/75] Add json output for ignition Signed-off-by: Christian Goll --- go.mod | 9 +- go.sum | 30 +++++- internal/app/wwctl/overlay/show/main.go | 2 +- internal/pkg/configure/hostfile.go | 2 +- internal/pkg/node/ignition.go | 127 ++++++++++++++++++++++++ internal/pkg/node/methods.go | 32 ++++++ internal/pkg/overlay/datastructure.go | 14 +-- internal/pkg/overlay/funcmap.go | 24 ++++- internal/pkg/overlay/overlay.go | 18 +++- 9 files changed, 242 insertions(+), 16 deletions(-) create mode 100644 internal/pkg/node/ignition.go diff --git a/go.mod b/go.mod index 61146ac7..c766128a 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ require ( github.com/containers/image/v5 v5.7.0 github.com/containers/storage v1.30.0 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e + github.com/coreos/ignition/v2 v2.15.0 + github.com/coreos/vcontext v0.0.0-20230201181013-d72178a18687 github.com/creasty/defaults v1.7.0 github.com/fatih/color v1.15.0 github.com/golang/glog v1.0.0 @@ -18,7 +20,7 @@ require ( github.com/opencontainers/umoci v0.4.6 github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.6.1 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.2 github.com/talos-systems/go-smbios v0.1.1 golang.org/x/term v0.5.0 google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 @@ -40,6 +42,8 @@ require ( github.com/containerd/containerd v1.5.0-beta.4 // indirect github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b // indirect github.com/containers/ocicrypt v1.1.0 // indirect + github.com/coreos/go-semver v0.3.1 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cyphar/filepath-securejoin v0.2.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -76,13 +80,14 @@ require ( github.com/rootless-containers/proto v0.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace // indirect github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect github.com/ulikunitz/xz v0.5.10 // indirect github.com/urfave/cli v1.22.4 // indirect github.com/vbatts/go-mtree v0.5.0 // indirect github.com/vbauerster/mpb/v5 v5.3.0 // indirect + github.com/vincent-petithory/dataurl v1.0.0 // indirect go.etcd.io/bbolt v1.3.5 // indirect go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 // indirect golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect diff --git a/go.sum b/go.sum index 249ac530..6a7ee2ff 100644 --- a/go.sum +++ b/go.sum @@ -350,6 +350,7 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= @@ -447,7 +448,9 @@ github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3st github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.44.204/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= +github.com/beevik/etree v1.1.1-0.20200718192613-4a2f8b9d084c/go.mod h1:0yGO2rna3S9DkITDWHY1bMtcY4IJ4w+4S+EooZUR0bE= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -572,17 +575,26 @@ github.com/containers/storage v1.23.6/go.mod h1:haFs0HRowKwyzvWEx9EgI3WsL8XCSnBD github.com/containers/storage v1.30.0 h1:KS6zmoPyy0Qcx1HCCiseQ0ysSckRvtiuoVpIGh9iwQA= github.com/containers/storage v1.30.0/go.mod h1:M/xn0pg6ReYFrLtWl5YELI/a4Xjq+Z3e5GJxQrJCcDI= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-json v0.0.0-20230131223807-18775e0fb4fb/go.mod h1:rcFZM3uxVvdyNmsAV2jopgPD1cs5SPWJWU5dOz2LUnw= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/ignition/v2 v2.15.0 h1:v2fQ6QvkcAF+La5PHHpnpBS1eGZo+LYL1wTOPvDKAcs= +github.com/coreos/ignition/v2 v2.15.0/go.mod h1:+7BiKurzCFg3P427Ml0wqnKzIuhLimnil6LhFV2DkJM= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/vcontext v0.0.0-20230201181013-d72178a18687 h1:uSmlDgJGbUB0bwQBcZomBTottKwEDF5fF8UjSwKSzWM= +github.com/coreos/vcontext v0.0.0-20230201181013-d72178a18687/go.mod h1:Salmysdw7DAVuobBW/LwsKKgpyCPHUhjyJoMJD+ZJiI= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= @@ -681,6 +693,7 @@ github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblf github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -767,6 +780,7 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= 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= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -831,6 +845,8 @@ github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6t github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -975,6 +991,7 @@ github.com/opencontainers/umoci v0.4.6 h1:nUULYM+jSLLJCVN2gd4wldm8/yuVMahC36UXna github.com/opencontainers/umoci v0.4.6/go.mod h1:ZyTwgJPvYl9xv1Cf3ykU41BxMSCxW3AtoueA5Bmxs1E= github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pin/tftp v2.1.0+incompatible/go.mod h1:xVpZOMCXTy+A5QMjEVN0Glwa1sUvaJhFXbr/aAxuxGY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1059,8 +1076,9 @@ github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzu github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace h1:9PNP1jnUjRhfmGMlkXHjYPishpcw4jpSt/V/xYY3FMA= +github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 h1:lIOOHPEbXzO3vnmx2gok1Tfs31Q8GQqKLc8vVqyQq/I= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1078,8 +1096,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= @@ -1110,10 +1129,13 @@ github.com/vbatts/go-mtree v0.5.0/go.mod h1:7JbaNHyBMng+RP8C3Q4E+4Ca8JnGQA2R/MB+ github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= github.com/vbauerster/mpb/v5 v5.3.0 h1:vgrEJjUzHaSZKDRRxul5Oh4C72Yy/5VEMb0em+9M0mQ= github.com/vbauerster/mpb/v5 v5.3.0/go.mod h1:4yTkvAb8Cm4eylAp6t0JRq6pXDkFJ4krUlDqWYkakAs= +github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI= +github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vmware/vmw-guestinfo v0.0.0-20220317130741-510905f0efa3/go.mod h1:CSBTxrhePCm0cmXNKDGeu+6bOQzpaEklfCqEpn89JWk= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1272,6 +1294,7 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -1425,6 +1448,7 @@ golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1432,6 +1456,7 @@ golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/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/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= @@ -1580,6 +1605,7 @@ google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91 google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= diff --git a/internal/app/wwctl/overlay/show/main.go b/internal/app/wwctl/overlay/show/main.go index d1a27958..8475c85e 100644 --- a/internal/app/wwctl/overlay/show/main.go +++ b/internal/app/wwctl/overlay/show/main.go @@ -69,7 +69,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { wwlog.Error("%v does not identify a single node", NodeName) os.Exit(1) } - tstruct := overlay.InitStruct(filteredNodes[0]) + tstruct := overlay.InitStruct(&filteredNodes[0]) tstruct.BuildSource = overlayFile buffer, backupFile, writeFile, err := overlay.RenderTemplateFile(overlayFile, tstruct) if err != nil { diff --git a/internal/pkg/configure/hostfile.go b/internal/pkg/configure/hostfile.go index 27866775..89f63731 100644 --- a/internal/pkg/configure/hostfile.go +++ b/internal/pkg/configure/hostfile.go @@ -24,7 +24,7 @@ func Hostfile() error { nodeInfo := node.NewInfo() hostname, _ := os.Hostname() nodeInfo.Id.Set(hostname) - tstruct := overlay.InitStruct(nodeInfo) + tstruct := overlay.InitStruct(&nodeInfo) buffer, backupFile, writeFile, err := overlay.RenderTemplateFile( hostTemplate, tstruct) diff --git a/internal/pkg/node/ignition.go b/internal/pkg/node/ignition.go new file mode 100644 index 00000000..3d8e756f --- /dev/null +++ b/internal/pkg/node/ignition.go @@ -0,0 +1,127 @@ +package node + +import ( + "fmt" + "sort" + + types_3_4 "github.com/coreos/ignition/v2/config/v3_2/types" + "github.com/coreos/vcontext/path" + "github.com/hpcng/warewulf/internal/pkg/wwlog" +) + +/* +Create a ignition struct class for ignition +*/ +func (node *NodeInfo) GetStorage() (stor types_3_4.Storage, err error, rep string) { + var fileSystems []types_3_4.Filesystem + for fsdevice, fs := range node.FileSystems { + var mountOptions []types_3_4.MountOption + for _, opt := range fs.MountOptions.GetSlice() { + mountOptions = append(mountOptions, types_3_4.MountOption(opt)) + } + var fsOption []types_3_4.FilesystemOption + for _, opt := range fs.Options.GetSlice() { + fsOption = append(fsOption, types_3_4.FilesystemOption(opt)) + } + wipe := fs.WipeFileSystem.GetB() + myFs := types_3_4.Filesystem{ + Device: fsdevice, + Path: fs.Path.GetPointer(), + WipeFilesystem: &wipe, + } + if fs.Format.Get() != "" { + myFs.Format = fs.Format.GetPointer() + } + if fs.Label.Get() != "" { + myFs.Label = fs.Label.GetPointer() + } + if fs.MountOptions.Get() != "" { + myFs.MountOptions = mountOptions + } + if fs.Options.Get() != "" { + myFs.Options = fsOption + } + if fs.Options.Get() != "" { + myFs.UUID = fs.Uuid.GetPointer() + } + wwlog.Debug("created file system struct: %v", myFs) + fileSystems = append(fileSystems, myFs) + } + var disks []types_3_4.Disk + for diskDev, disk := range node.Disks { + var partitions []types_3_4.Partition + for partlabel, part := range disk.Partitions { + resize := part.Resize.GetB() + shouldExist := part.ShouldExist.GetB() + wipe := part.WipePartitionEntry.GetB() + label := partlabel + myPart := types_3_4.Partition{ + Label: &label, + Number: part.Number.GetInt(), + ShouldExist: &shouldExist, + WipePartitionEntry: &wipe, + } + if part.Guid.Get() != "" { + myPart.GUID = part.Guid.GetPointer() + } + if part.Resize.Get() != "" { + myPart.Resize = &resize + } + if part.SizeMiB.Get() != "" { + myPart.SizeMiB = part.SizeMiB.GetIntPtr() + } + if part.StartMiB.Get() != "" { + myPart.StartMiB = part.StartMiB.GetIntPtr() + } + if part.TypeGuid.Get() != "" { + myPart.TypeGUID = part.TypeGuid.GetPointer() + } + partitions = append(partitions, myPart) + } + sort.SliceStable(partitions, func(i int, j int) bool { + if partitions[i].Number == partitions[j].Number { + if partitions[i].SizeMiB != nil && partitions[j].SizeMiB == nil { + return true + } + if partitions[j].SizeMiB != nil && partitions[i].SizeMiB == nil { + return false + } + return *partitions[i].SizeMiB < *partitions[j].SizeMiB + } + return partitions[i].Number < partitions[j].Number + }) + wipe := disk.WipeTable.GetB() + disks = append(disks, types_3_4.Disk{ + Device: diskDev, + Partitions: partitions, + WipeTable: &wipe, + }) + } + stor = types_3_4.Storage{ + Disks: disks, + Filesystems: fileSystems, + } + report := stor.Validate(path.ContextPath{}) + if report.IsFatal() { + err = fmt.Errorf(report.String()) + } + rep = report.String() + return +} + +type MyIgnition struct { + Version string `json:"version"` +} +type SimpleIgnitionConfig struct { + Ignition MyIgnition `json:"ignition"` + Storage types_3_4.Storage `json:"storage"` +} + +/* +Get a simple config which can be marshalled to json +*/ +func (node *NodeInfo) GetConfig() (conf SimpleIgnitionConfig, rep string, err error) { + conf.Storage, err, rep = node.GetStorage() + conf.Ignition.Version = "3.1.0" + return +} diff --git a/internal/pkg/node/methods.go b/internal/pkg/node/methods.go index 88ee41ac..4d768ddb 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -5,6 +5,7 @@ import ( "reflect" "regexp" "sort" + "strconv" "strings" "github.com/hpcng/warewulf/internal/pkg/util" @@ -281,6 +282,37 @@ func (ent *Entry) GotReal() bool { return len(ent.value) != 0 } +/* +Get a pointer to the value +*/ +func (ent *Entry) GetPointer() *string { + ret := ent.Get() + return &ret +} + +/* +Try to get a int of a value, 0 if value can't be parsed! +*/ +func (ent *Entry) GetInt() int { + var ret int + if len(ent.value) != 0 { + ret, _ = strconv.Atoi(ent.value[0]) + } else if len(ent.altvalue) != 0 { + ret, _ = strconv.Atoi(ent.altvalue[0]) + } else if len(ent.def) != 0 { + ret, _ = strconv.Atoi(ent.def[0]) + } + return ret +} + +/* +Ptr to int +*/ +func (ent *Entry) GetIntPtr() *int { + ret := ent.GetInt() + return &ret +} + /********** * * Misc diff --git a/internal/pkg/overlay/datastructure.go b/internal/pkg/overlay/datastructure.go index 6af7adb2..f9806802 100644 --- a/internal/pkg/overlay/datastructure.go +++ b/internal/pkg/overlay/datastructure.go @@ -6,8 +6,8 @@ import ( "strconv" "time" - "github.com/hpcng/warewulf/internal/pkg/node" warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" + "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) @@ -37,13 +37,15 @@ type TemplateStruct struct { node.NodeConf // backward compatiblity Container string + ThisNode *node.NodeInfo } /* Initialize an TemplateStruct with the given node.NodeInfo */ -func InitStruct(nodeInfo node.NodeInfo) TemplateStruct { +func InitStruct(nodeInfo *node.NodeInfo) TemplateStruct { var tstruct TemplateStruct + tstruct.ThisNode = nodeInfo controller := warewulfconf.Get() nodeDB, err := node.New() if err != nil { @@ -55,7 +57,7 @@ func InitStruct(nodeInfo node.NodeInfo) TemplateStruct { wwlog.Error("%s", err) os.Exit(1) } - // init some convininence vars + // init some convenience vars tstruct.Id = nodeInfo.Id.Get() tstruct.Hostname = nodeInfo.Id.Get() // Backwards compatibility for templates using "Keys" @@ -81,17 +83,17 @@ func InitStruct(nodeInfo node.NodeInfo) TemplateStruct { tstruct.BuildTime = dt.Format("01-02-2006 15:04:05 MST") tstruct.BuildTimeUnix = strconv.FormatInt(dt.Unix(), 10) tstruct.NodeConf.Tags = map[string]string{} - tstruct.NodeConf.GetFrom(nodeInfo) + tstruct.NodeConf.GetFrom(*nodeInfo) // FIXME: Set ipCIDR address at this point, will fail with // invalid ipv4 addr - for _, network := range tstruct.NetDevs { + for _, network := range tstruct.NodeConf.NetDevs { ipCIDR := net.IPNet{ IP: net.ParseIP(network.Ipaddr), Mask: net.IPMask(net.ParseIP(network.Netmask))} network.IpCIDR = ipCIDR.String() } // backward compatibilty - tstruct.Container = tstruct.ContainerName + tstruct.Container = tstruct.NodeConf.ContainerName return tstruct diff --git a/internal/pkg/overlay/funcmap.go b/internal/pkg/overlay/funcmap.go index 4acf6705..dc688dc2 100644 --- a/internal/pkg/overlay/funcmap.go +++ b/internal/pkg/overlay/funcmap.go @@ -2,13 +2,16 @@ package overlay import ( "bufio" + "encoding/json" + "fmt" "os" "path" "strings" - "github.com/hpcng/warewulf/internal/pkg/container" - "github.com/hpcng/warewulf/internal/pkg/util" warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" + "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/wwlog" ) @@ -100,3 +103,20 @@ func templateContainerFileInclude(containername string, filepath string) string } return strings.TrimSuffix(string(content), "\n") } + +func createIgnitionJson(node *node.NodeInfo) string { + conf, rep, err := node.GetConfig() + if len(conf.Storage.Disks) == 0 && len(conf.Storage.Filesystems) == 0 { + wwlog.Debug("no disks or filesystems present, don't create a json object") + return "" + } + if err != nil { + wwlog.Error("disk, filesystem configuration has following error: ", fmt.Sprint(err)) + return fmt.Sprint(err) + } + if rep != "" { + wwlog.Warn("%s storage configuration has following non fatal problems: %s", node.Id, rep) + } + tmpYaml, _ := json.Marshal(&conf) + return string(tmpYaml) +} diff --git a/internal/pkg/overlay/overlay.go b/internal/pkg/overlay/overlay.go index ec505c28..969ea291 100644 --- a/internal/pkg/overlay/overlay.go +++ b/internal/pkg/overlay/overlay.go @@ -245,7 +245,7 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir wwlog.Debug("Created directory in overlay: %s", location) } else if filepath.Ext(location) == ".ww" { - tstruct := InitStruct(nodeInfo) + tstruct := InitStruct(&nodeInfo) tstruct.BuildSource = path.Join(overlaySourceDir, location) wwlog.Verbose("Evaluating overlay template file: %s", location) destFile := strings.TrimSuffix(location, ".ww") @@ -266,7 +266,7 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir line := fileScanner.Text() filenameFromTemplate := reg.FindAllStringSubmatch(line, -1) if len(filenameFromTemplate) != 0 { - wwlog.Debug("Found multifile comment, new filename %s", filenameFromTemplate[0][1]) + wwlog.Debug("Found multiple comment, new filename %s", filenameFromTemplate[0][1]) if foundFileComment { err = CarefulWriteBuffer(path.Join(outputDir, destFileName), fileBuffer, backupFile, info.Mode()) @@ -384,6 +384,14 @@ func RenderTemplateFile(fileName string, data TemplateStruct) ( "inc": func(i int) int { return i + 1 }, "dec": func(i int) int { return i - 1 }, "file": func(str string) string { return fmt.Sprintf("{{ /* file \"%s\" */ }}", str) }, + "IgnitionJson": func() string { + str := createIgnitionJson(data.ThisNode) + if str != "" { + return str + } + writeFile = false + return "" + }, "abort": func() string { wwlog.Debug("abort file called in %s", fileName) writeFile = false @@ -397,6 +405,12 @@ func RenderTemplateFile(fileName string, data TemplateStruct) ( "split": func(s string, d string) []string { return strings.Split(s, d) }, + "tr": func(source, old, new string) string { + return strings.Replace(source, old, new, -1) + }, + "replace": func(source, old, new string) string { + return strings.Replace(source, old, new, -1) + }, // }).ParseGlob(path.Join(OverlayDir, destFile+".ww*")) }).ParseGlob(fileName) if err != nil { From cfbb38d570a668de342fead1ff8aff7c275a2636 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Tue, 15 Aug 2023 17:31:15 -0600 Subject: [PATCH 62/75] Use ignition as a systemd service simple init.d ignition fails as systemd is needed to set up drivers, etc. Signed-off-by: Christian Goll --- .../systemd/system/ignition-ww4-disks.service | 32 +++++++++++++++++++ .../systemd/system/ignition-ww4-mount.service | 16 ++++++++++ .../ignition-ww4-disks.service | 1 + .../ignition-ww4-mount.service | 1 + overlays/wwinit/warewulf/ignition.json.ww | 1 + 5 files changed, 51 insertions(+) create mode 100644 overlays/wwinit/etc/systemd/system/ignition-ww4-disks.service create mode 100644 overlays/wwinit/etc/systemd/system/ignition-ww4-mount.service create mode 120000 overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-disks.service create mode 120000 overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-mount.service create mode 100644 overlays/wwinit/warewulf/ignition.json.ww diff --git a/overlays/wwinit/etc/systemd/system/ignition-ww4-disks.service b/overlays/wwinit/etc/systemd/system/ignition-ww4-disks.service new file mode 100644 index 00000000..ce377b79 --- /dev/null +++ b/overlays/wwinit/etc/systemd/system/ignition-ww4-disks.service @@ -0,0 +1,32 @@ +[Unit] +Description=Ignition warewulf (disks) +Documentation=https://hpcng.github.io/warewulf +ConditionPathExists=/warewulf/ignition.json +ConditionPathExists=/usr/lib/dracut/modules.d/30ignition/ignition +DefaultDependencies=false + +Before=ignition-ww4-mount.service + +# This stage runs between `basic.target` and `initrd-root-device.target`, +# see https://www.freedesktop.org/software/systemd/man/bootup.html +# Make sure to run before the file system checks, as sgdisk will trigger +# udev events, potentially resulting in race conditions due to disappearing +# devices. + +# Note that CL runs this before `local-fs-pre.target` to allow for configs that +# completely wipe the rootfs. Though we're not there yet. But we still run +# before `sysroot.mount` on principle. +Before=initrd-root-device.target +Before=sysroot.mount + +#OnFailure=emergency.target +#OnFailureJobMode=isolate + +# This stage requires udevd to detect disk partitioning changes. +Requires=systemd-udevd.service +After=systemd-udevd.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/lib/dracut/modules.d/30ignition/ignition --platform=metal --stage=disks --config-cache /warewulf/ignition.json diff --git a/overlays/wwinit/etc/systemd/system/ignition-ww4-mount.service b/overlays/wwinit/etc/systemd/system/ignition-ww4-mount.service new file mode 100644 index 00000000..35986da7 --- /dev/null +++ b/overlays/wwinit/etc/systemd/system/ignition-ww4-mount.service @@ -0,0 +1,16 @@ +[Unit] +Description=Ignition warewulf (mount) +Documentation=https://hpcng.github.io/warewulf +ConditionPathExists=/warewulf/ignition.json +ConditionPathExists=/usr/lib/dracut/modules.d/30ignition/ignition +DefaultDependencies=false +Before=ignition-complete.target + +After=ignition-ww4-disks.service + + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/lib/dracut/modules.d/30ignition/ignition --platform=metal --stage=mount --config-cache /warewulf/ignition.json +ExecStop=/usr/lib/dracut/modules.d/30ignition/ignition --platform=metal --stage=umount --config-cache /warewulf/ignition.json diff --git a/overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-disks.service b/overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-disks.service new file mode 120000 index 00000000..726d662c --- /dev/null +++ b/overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-disks.service @@ -0,0 +1 @@ +/etc/systemd/system/ignition-ww4-disks.service \ No newline at end of file diff --git a/overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-mount.service b/overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-mount.service new file mode 120000 index 00000000..112c95c9 --- /dev/null +++ b/overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-mount.service @@ -0,0 +1 @@ +/etc/systemd/system/ignition-ww4-mount.service \ No newline at end of file diff --git a/overlays/wwinit/warewulf/ignition.json.ww b/overlays/wwinit/warewulf/ignition.json.ww new file mode 100644 index 00000000..7b800e85 --- /dev/null +++ b/overlays/wwinit/warewulf/ignition.json.ww @@ -0,0 +1 @@ +{{ IgnitionJson }} From d96c55022d84ab0797372679c4336fb573e83d53 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Tue, 15 Aug 2023 17:36:40 -0600 Subject: [PATCH 63/75] Use systemd mount units for mounting ignition Signed-off-by: Christian Goll --- overlays/wwinit/etc/fstab.ww | 9 +++++ .../systemd/system/ignition-ww4-mount.service | 16 -------- .../ignition-ww4-disks.service | 0 .../local-fs.target.wants/ww4-disks.target | 1 + .../ignition-ww4-mount.service | 1 - .../etc/systemd/system/ww4-disks.target.ww | 20 ++++++++++ .../wwinit/etc/systemd/system/ww4-mounts.ww | 38 +++++++++++++++++++ 7 files changed, 68 insertions(+), 17 deletions(-) delete mode 100644 overlays/wwinit/etc/systemd/system/ignition-ww4-mount.service rename overlays/wwinit/etc/systemd/system/{multi-user.target.wants => local-fs.target.wants}/ignition-ww4-disks.service (100%) create mode 120000 overlays/wwinit/etc/systemd/system/local-fs.target.wants/ww4-disks.target delete mode 120000 overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-mount.service create mode 100644 overlays/wwinit/etc/systemd/system/ww4-disks.target.ww create mode 100644 overlays/wwinit/etc/systemd/system/ww4-mounts.ww diff --git a/overlays/wwinit/etc/fstab.ww b/overlays/wwinit/etc/fstab.ww index 58fc3767..555d6b89 100644 --- a/overlays/wwinit/etc/fstab.ww +++ b/overlays/wwinit/etc/fstab.ww @@ -7,8 +7,17 @@ devpts /dev/pts devpts gid=5,mode=620 0 0 tmpfs /run/shm tmpfs defaults 0 0 sysfs /sys sysfs defaults 0 0 proc /proc proc defaults 0 0 +{{- if .FileSystems }} +# mounts for local file systems created with ignition in nodes.conf +# all with noauto as mounts happens with systemd units +{{- range $fsdevice,$fs := .FileSystems }} +{{ $fsdevice }} {{ $fs.Path }} {{ $fs.Format }} +{{- if $fs.MountOptions }} noauto{{range $index,$opt := $fs.MountOptions }},{{ $opt }}{{ end }} 0 0 +{{- else }} noauto,defaults 0 0 +{{- end }}{{ end }}{{ end }} {{- $IP:=.Ipaddr }} {{- if .Nfs.Enabled }} +# nfs mounts provided in warewulf.conf {{- range .Nfs.ExportsExtended }} {{- if .Mount }} {{ $IP }}:{{ .Path }} {{ .Path }} nfs {{ .MountOptions }} 0 0 diff --git a/overlays/wwinit/etc/systemd/system/ignition-ww4-mount.service b/overlays/wwinit/etc/systemd/system/ignition-ww4-mount.service deleted file mode 100644 index 35986da7..00000000 --- a/overlays/wwinit/etc/systemd/system/ignition-ww4-mount.service +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=Ignition warewulf (mount) -Documentation=https://hpcng.github.io/warewulf -ConditionPathExists=/warewulf/ignition.json -ConditionPathExists=/usr/lib/dracut/modules.d/30ignition/ignition -DefaultDependencies=false -Before=ignition-complete.target - -After=ignition-ww4-disks.service - - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=/usr/lib/dracut/modules.d/30ignition/ignition --platform=metal --stage=mount --config-cache /warewulf/ignition.json -ExecStop=/usr/lib/dracut/modules.d/30ignition/ignition --platform=metal --stage=umount --config-cache /warewulf/ignition.json diff --git a/overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-disks.service b/overlays/wwinit/etc/systemd/system/local-fs.target.wants/ignition-ww4-disks.service similarity index 100% rename from overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-disks.service rename to overlays/wwinit/etc/systemd/system/local-fs.target.wants/ignition-ww4-disks.service diff --git a/overlays/wwinit/etc/systemd/system/local-fs.target.wants/ww4-disks.target b/overlays/wwinit/etc/systemd/system/local-fs.target.wants/ww4-disks.target new file mode 120000 index 00000000..6bebd069 --- /dev/null +++ b/overlays/wwinit/etc/systemd/system/local-fs.target.wants/ww4-disks.target @@ -0,0 +1 @@ +/etc/systemd/system/ww4-disks.target \ No newline at end of file diff --git a/overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-mount.service b/overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-mount.service deleted file mode 120000 index 112c95c9..00000000 --- a/overlays/wwinit/etc/systemd/system/multi-user.target.wants/ignition-ww4-mount.service +++ /dev/null @@ -1 +0,0 @@ -/etc/systemd/system/ignition-ww4-mount.service \ No newline at end of file diff --git a/overlays/wwinit/etc/systemd/system/ww4-disks.target.ww b/overlays/wwinit/etc/systemd/system/ww4-disks.target.ww new file mode 100644 index 00000000..b8e9d429 --- /dev/null +++ b/overlays/wwinit/etc/systemd/system/ww4-disks.target.ww @@ -0,0 +1,20 @@ +# This file is autogenerated by warewulf +# Host: {{.BuildHost}} +# Time: {{.BuildTime}} +# Source: {{.BuildSource}} +[Unit] +Description=mount ww4 disks +# make sure that the disks are available +Requires=ignition-ww4-disks.service +After=ignition-ww4-disks.service +Prequisite=ignition-ww4-disks.service +# Get the mounts +{{- range $fsdevice,$fs := .FileSystems }} +{{- $prefix := $fsdevice }}{{ $suffix := "mount" }} +{{- if eq $fs.Format "swap"}} +{{- $prefix = tr $fsdevice "/dev/disk/by-partlabel/" "dev-disk-by\\x2dpartlabel-"}}{{ $suffix = "swap"}} +{{- else }} +{{- if $fs.Path }}{{ $prefix = tr $fs.Path "/" "" }}{{ end }} +{{- end }} +Wants={{ print $prefix "." $suffix }} +{{- end }} \ No newline at end of file diff --git a/overlays/wwinit/etc/systemd/system/ww4-mounts.ww b/overlays/wwinit/etc/systemd/system/ww4-mounts.ww new file mode 100644 index 00000000..39a5fb9a --- /dev/null +++ b/overlays/wwinit/etc/systemd/system/ww4-mounts.ww @@ -0,0 +1,38 @@ +{{- $host := .BuildHost }} +{{- $time := .BuildTime }} +{{- $source := .BuildSource }} +{{- range $fsdevice,$fs := .FileSystems }} +{{- $prefix := tr $fs.Path "/" "" }}{{ $suffix:="mount"}} +{{- if $fs.Label }}{{ $prefix = $fs.Label }}{{ end }} +{{- if eq $fs.Format "swap"}}{{ $prefix = tr $fsdevice "/dev/disk/by-partlabel/" "dev-disk-by\\x2dpartlabel-"}}{{ $suffix = "swap"}}{{ end }} +{{- $filename := print $prefix "." $suffix }} +{{- file $filename }} +# This file is autogenerated by warewulf +# Host: {{ $host }} +# Time: {{ $time }} +# Source: {{ $source }} +{{- if eq $fs.Format "swap"}} +[Unit] +ConditionPathExists=/warewulf/ignition.json +After=ignition-ww4-disks.service +Before=swap.target +[Swap] +What={{ $fsdevice }} +[Install] +RequiredBy=swap.target +{{- else }} +[Unit] +ConditionPathExists=/warewulf/ignition.json +Before=local-fs.target +After=ignition-ww4-disks.service +[Mount] +Where={{ $fs.Path }} +What={{ $fsdevice }} +Type={{ $fs.Format }} +{{- if $fs.MountOptions }} +Options={{range $index,$opt := $fs.MountOptions }}{{if $index }},{{ end }}{{ $opt }}{{ end }} +{{- end }} +[Install] +RequiredBy=local-fs.target +{{- end }} +{{- end }} From 2892781078a50d7763e5572df73e313b9423e30f Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 11 Aug 2023 13:39:11 -0600 Subject: [PATCH 64/75] Update tested golang version Signed-off-by: Christian Goll --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e582f30d..dc9d8664 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: [ '1.17', '1.18', '1.19' ] + go: [ '1.18', '1.19', '1.20' ] steps: - uses: actions/checkout@v3 From d060c3d447be13362595dc7a1e749e4b299c1eb3 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Fri, 11 Aug 2023 13:55:29 -0600 Subject: [PATCH 65/75] Add ignition documentation Signed-off-by: Christian Goll --- CHANGELOG.md | 22 +++++++++ userdocs/contents/disks.rst | 89 +++++++++++++++++++++++++++++++++++++ userdocs/index.rst | 1 + 3 files changed, 112 insertions(+) create mode 100644 userdocs/contents/disks.rst diff --git a/CHANGELOG.md b/CHANGELOG.md index efb38a74..f0c7243b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 test-it with test. #890 - realy reboot also without systemd +- Specify primary network device per-node rather than per-netdev +- refactored output `wwctl node/profile list` so that `-a` will only show all the + set values and `-A` will show all fields included the ones without a set value +- Added support for file systems, partitions and disks. Values for these objects can + be set with `wwctl profile/node set/add`. The format of this objects is inspired by + butane/ignition, but where butane/ignition uses lists for holding disks, partitions + and file systems, warewulf uses maps instead. For disks the map key is the underlying + block device, for partitions its the partition label and for file systems its the path + to the partitions (e.g. `/dev/disk/by-partlabel/scratch`). + Not all available options of butane/ignition are exposed to the commandline, but are + available via `wwctl node/profile edit`. +- Added the template function `{{ createIgnitionJson }}` which will create a json object + compatible with ignition. +- Container images need ignition and sgdisk installed in order to the disk management. +- Added boootup services based on ignition which will manage the disks, partitions and file + systems. The services are systemd services as sgdisk needs systemd in order to work + correctly. All service use the existence of `/warewulf/ignition.json` as perquisite so + that they can be place in the `wwinit` overlay and will only become active if disk management + is configured for this node. The service `ignition-disks-ww4.service` will partition and + format and create the file systems on the disks. For every a file system a systemd mount unit + file is create and will executed after the `ignition-disks-ww4.service` has finished. + Entries in `/etc/fstab` for every file system are created with the `noauto` option. ## [4.4.0] 2023-01-18 diff --git a/userdocs/contents/disks.rst b/userdocs/contents/disks.rst new file mode 100644 index 00000000..36152178 --- /dev/null +++ b/userdocs/contents/disks.rst @@ -0,0 +1,89 @@ +=============== +Disk Management +=============== + +Warewulf itself does not manage disks, partitions, or file systems +directly, but provides structures in the configuration for these +objects. At the moment warewulf supports `ignition` to create the +partitions and file systems. + +.. note:: + + It is not currently possible to manage the root file system with + Warewulf. + +Warewulf can be used, for example, to create `swap` partitions or +`/scratch` file systems. + +Storage objects +=============== + +The format of the storage objects is inspired by `butane/ignition`; +but, where `butane/ignition` uses lists for holding disks, partitions +and file systems, Warewulf uses maps instead. + +A node or profile can have several disks, where each disk is +identified by the path to its block device. Every disks holds a map to +its partitions and a `bool` switch to indicate if an existing +partition table should be overwritten if it does not matched the +desired configuration. + +Each partition is identified by its label. The partition number can be +omitted, but specifying it is recommended as `ignition` may fail +without it. Partition sizes should also be set (specified in MiB), +except of the last partition: if no size is given, the maximum +available size is used. Each partition has the switches `should_exist` +and `wipe_partition_entry` which control the partition creation +process. + +File systems are identified by their underlying block device, +preferably using the `/dev/by-partlabel` format. Except for a `swap` +partition, an absolute path for the mount point must be specified for +each file system. Depending on the container used, valid formats are +`btrfs`, `ext3`, `ext4`, and `xfs`. Each file system has the switch +`wipe_filesystem` to control whether an existing file system is wiped. + +Ignition Implementation +======================= + +The ignition implementation uses systemd services, as the underlying +`sgdisk` command relies on dbus notifications. All necessary services +are distributed by the `wwinit` overlay and depends on the existence +of the file `/warewulf/ignition.json`. This file is created by the +template function `{{ createIgnitionJson }}` only if the configuration +contains necessary specifications for disks, partitions, and file +systems. If the file `/warewulf/ignition.json` exists, the service +`ignition-disks-ww4.service` calls the ignition binary which takes +creates partitions and file systems. A systemd `.mount` unit is +created for each configured file system, which also creates the +necessary mount points in the root file system. These mount units are +required by the enabled `ww4-disks.target`. Entries in `/etc/fstab` +are created with the `no_auto` option so that file systems can be +easily mounted. + +Example +======= + +The following command will create a `/scratch` file system on the node +`n01` + +.. code-block:: shell + + wwctl node set n01 \ + --diskname /dev/vda --diskwipe \ + --partname scratch --partcreate \ + --fsname scratch --fsformat btrfs --fspath /scratch --fswipe + +As this is a single file system, the partition number can be omitted. + +A swap partition with 1Gig can be added with + +.. code-block:: shell + + wwctl node set n01 \ + --diskname /dev/vda \ + --partname swap --partsize=1024 --partnumber 1 \ + --fsname swap --fsformat swap --fspath swap + +which has the partition number `1` so that it will be added before the +`/scratch` partition. diff --git a/userdocs/index.rst b/userdocs/index.rst index 7b651a90..f18504d1 100644 --- a/userdocs/index.rst +++ b/userdocs/index.rst @@ -23,6 +23,7 @@ Welcome to the Warewulf User Guide! Warewulf Overlays Node Provisioning IPMI + Disk Management Security Templating From 9058682b18a80f11a1a8fb689e75c043af058ab0 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Tue, 22 Aug 2023 14:24:16 -0600 Subject: [PATCH 66/75] Add tests for disk data and ignition json Signed-off-by: Jonathon Anderson --- internal/pkg/node/node_test.go | 43 ++++++++++++++++++- internal/pkg/overlay/funcmap_test.go | 62 ++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 internal/pkg/overlay/funcmap_test.go diff --git a/internal/pkg/node/node_test.go b/internal/pkg/node/node_test.go index 988b4144..13003a06 100644 --- a/internal/pkg/node/node_test.go +++ b/internal/pkg/node/node_test.go @@ -37,4 +37,45 @@ nodes: }, ) assert.NoError(t, err) -} \ No newline at end of file +} + + +func TestNodeDisk(t *testing.T) { + node_config := `WW_INTERNAL: 43 +nodes: + n1: + disks: + /dev/vda: + wipe_table: "true" + partitions: + scratch: + should_exist: "true" + filesystems: + /dev/disk/by-partlabel/scratch: + format: btrfs + path: /scratch + wipe_filesystem: "true"` + + config, parse_error := Parse([]byte(node_config)) + assert.Empty(t, parse_error) + + nodeInfos, info_error := config.FindAllNodes() + assert.Empty(t, info_error) + assert.Len(t, nodeInfos, 1) + + node := nodeInfos[0] + assert.Len(t, node.Disks, 1) + assert.Len(t, node.FileSystems, 1) + + disk := node.Disks["/dev/vda"] + assert.True(t, disk.WipeTable.GetB()) + assert.Len(t, disk.Partitions, 1) + + partition := disk.Partitions["scratch"] + assert.True(t, partition.ShouldExist.GetB()) + + filesystem := node.FileSystems["/dev/disk/by-partlabel/scratch"] + assert.Equal(t, "btrfs", filesystem.Format.Get()) + assert.Equal(t, "/scratch", filesystem.Path.Get()) + assert.True(t, filesystem.WipeFileSystem.GetB()) +} diff --git a/internal/pkg/overlay/funcmap_test.go b/internal/pkg/overlay/funcmap_test.go new file mode 100644 index 00000000..ab1a4be2 --- /dev/null +++ b/internal/pkg/overlay/funcmap_test.go @@ -0,0 +1,62 @@ +package overlay + +import ( + "testing" + "github.com/stretchr/testify/assert" + "github.com/hpcng/warewulf/internal/pkg/node" +) + +func Test_createIgnitionJson(t *testing.T) { + node_config := `WW_INTERNAL: 43 +nodes: + n1: + disks: + /dev/vda: + wipe_table: "true" + partitions: + scratch: + should_exist: "true" + filesystems: + /dev/disk/by-partlabel/scratch: + format: btrfs + path: /scratch + wipe_filesystem: "true"` + + expected_json := `{ + "ignition": { + "version": "3.1.0" + }, + "storage": { + "disks": [ + { + "device": "/dev/vda", + "partitions": [ + { + "label": "scratch", + "shouldExist": true, + "wipePartitionEntry": false + } + ], + "wipeTable": true + } + ], + "filesystems": [ + { + "device": "/dev/disk/by-partlabel/scratch", + "format": "btrfs", + "path": "/scratch", + "wipeFilesystem": true + } + ] + } +}` + + config, parse_error := node.Parse([]byte(node_config)) + assert.Empty(t, parse_error) + + nodeInfos, info_error := config.FindAllNodes() + assert.Empty(t, info_error) + + node := nodeInfos[0] + assert.JSONEq(t, expected_json, createIgnitionJson(&node)) +} From 9a8396adea87a843a9c0f63088a9f7e127c81d2e Mon Sep 17 00:00:00 2001 From: Nicholas Porter <132295364+nap23carc@users.noreply.github.com> Date: Tue, 22 Aug 2023 16:17:50 -0600 Subject: [PATCH 67/75] Update el8.rst --- userdocs/quickstart/el8.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/userdocs/quickstart/el8.rst b/userdocs/quickstart/el8.rst index 4e2e0510..f2bdae40 100644 --- a/userdocs/quickstart/el8.rst +++ b/userdocs/quickstart/el8.rst @@ -9,6 +9,7 @@ Install Warewulf and dependencies sudo dnf groupinstall "Development Tools" sudo dnf install epel-release + sudo dnf config-manager --set-enabled powertools sudo dnf install golang tftp-server dhcp-server nfs-utils gpgme-devel libassuan-devel git clone https://github.com/hpcng/warewulf.git From 00ad5db153aa9977311e39e6093ea94fbfa17102 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Wed, 23 Aug 2023 14:16:52 -0600 Subject: [PATCH 68/75] Remove redundant entries from changelog Signed-off-by: Christian Goll --- CHANGELOG.md | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0c7243b..8752c6c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,19 +40,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The primary hostname and warewulf server fqdn are now the canonical name in `/etc/hosts` -- new subcommand `wwctl genconf` is available with following subcommands: - * `completions` which will create the files used for bash-completion. Also - fish an zsh completions can be generated - * `defaults` which will generate a valid `defaults.conf` - * `man` which will generate the man pages in the specified directory - * `reference` which will generate a reference documentation for the wwctl commands - * `warwulfconf print` which will print the used `warewulf.conf`. If there is no valid - `warewulf.conf` a valid configuration is provided, prefilled with default values - and an IP configuration derived from the network configuration of the host -- All paths can now be configured in `warewulf.conf`, check the paths section of of - `wwctl --emptyconf genconfig warewulfconf print` for the available paths. -- Added experimental dnsmasq support. + - Refactored `profile add` command to make it alike `node add`. #658 #659 + - The ifcfg ONBOOT parameter is no longer statically `true`, so unconfigured interfaces may not be enabled by default. (#644) @@ -65,22 +55,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `warwulfconf print` which will print the used `warewulf.conf`. If there is no valid `warewulf.conf` a valid configuration is provided, prefilled with default values and an IP configuration derived from the network configuration of the host + - All paths can now be configured in `warewulf.conf`, check the paths section of of `wwctl --emptyconf genconfig warewulfconf print` for the available paths. + - Added experimental dnsmasq support. -- new subcommand `wwctl genconf` is available with following subcommands: - * `completions` which will create the files used for bash-completion. Also - fish an zsh completions can be generated - * `defaults` which will generate a valid `defaults.conf` - * `man` which will generate the man pages in the specified directory - * `reference` which will generate a reference documentation for the wwctl commands - * `warwulfconf print` which will print the used `warewulf.conf`. If there is no valid - `warewulf.conf` a valid configuration is provided, prefilled with default values - and an IP configuration derived from the network configuration of the host -- All paths can now be configured in `warewulf.conf`, check the paths section of of - `wwctl --emptyconf genconfig warewulfconf print` for the available paths. -- Added experimental dnsmasq support. - Check for formal correct IP and MAC addresses for command line options and when reading in the configurations - Write log messages to stderr rather than stdout. #768 From 7ac2970d8333959b811b83b4bac832852d0f7676 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Wed, 23 Aug 2023 14:18:43 -0600 Subject: [PATCH 69/75] fix warewulf.conf location for wwclient Signed-off-by: Christian Goll --- CHANGELOG.md | 1 + internal/app/wwclient/root.go | 23 +++++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8752c6c6..22fe4306 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 file is create and will executed after the `ignition-disks-ww4.service` has finished. Entries in `/etc/fstab` for every file system are created with the `noauto` option. +- wwclient has now a commandline switch for the location of warewulf.conf ## [4.4.0] 2023-01-18 ### Added diff --git a/internal/app/wwclient/root.go b/internal/app/wwclient/root.go index 5af79840..88df49e1 100644 --- a/internal/app/wwclient/root.go +++ b/internal/app/wwclient/root.go @@ -16,8 +16,8 @@ import ( "github.com/coreos/go-systemd/daemon" "github.com/google/uuid" - "github.com/hpcng/warewulf/internal/pkg/pidfile" warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" + "github.com/hpcng/warewulf/internal/pkg/pidfile" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" "github.com/talos-systems/go-smbios/smbios" @@ -31,14 +31,16 @@ var ( RunE: CobraRunE, SilenceUsage: true, } - DebugFlag bool - PIDFile string - Webclient *http.Client + DebugFlag bool + PIDFile string + Webclient *http.Client + WarewulfConfArg string ) func init() { rootCmd.PersistentFlags().BoolVarP(&DebugFlag, "debug", "d", false, "Run with debugging messages enabled.") rootCmd.PersistentFlags().StringVarP(&PIDFile, "pidfile", "p", "/var/run/wwclient.pid", "PIDFile to use") + rootCmd.PersistentFlags().StringVar(&WarewulfConfArg, "warewulfconf", "", "Set the warewulf configuration file") } @@ -48,9 +50,18 @@ func GetRootCommand() *cobra.Command { return rootCmd } -func CobraRunE(cmd *cobra.Command, args []string) error { +func CobraRunE(cmd *cobra.Command, args []string) (err error) { conf := warewulfconf.Get() - + if WarewulfConfArg != "" { + err = conf.Read(WarewulfConfArg) + } else if os.Getenv("WAREWULFCONF") != "" { + err = conf.Read(os.Getenv("WAREWULFCONF")) + } else { + err = conf.Read(warewulfconf.ConfigFile) + } + if err != nil { + return + } pid, err := pidfile.Write(PIDFile) if err != nil && pid == -1 { wwlog.Warn("%v. starting new wwclient", err) From ab537a81362f14876f83dde0d7ea97e0dea10c6c Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Mon, 26 Jun 2023 22:59:18 -0500 Subject: [PATCH 70/75] Update ciq.com URLs Signed-off-by: Jonathon Anderson --- CONTRIBUTORS.md | 8 ++++---- README.md | 2 +- warewulf.spec.in | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 45ee2d7d..c3dcbeb1 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -5,8 +5,8 @@ * Jeremy Siadal @jcsiadal * Niko Kivel @kivel * Shannon V. Davidson -* Brian Clemens @brianclemens -* Michael L. Young @ajyounge +* Brian Clemens @brianclemens +* Michael L. Young @ajyounge * Ian Kaneshiro @ikaneshiro * Carter Dodd @kcdodd * MatthewHink @MatthewHink @@ -22,7 +22,7 @@ * David McFarlane @Prepultrue * Jean-Baptiste Denis * TerranWorks -* Jonathon Anderson @anderbubble -* Brian Phan +* Jonathon Anderson @anderbubble +* Brian Phan * Jeffrey Frey @jtfrey * Xu Yang(Jason Yang) @JasonYangShadow diff --git a/README.md b/README.md index 53ab8058..2100a7b2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ * [Documentation](https://warewulf.org/docs) * [Download / Releases](http://github.com/hpcng/warewulf/releases) * [Slack](http://hpcng.slack.com/#warewulf) -* [Support](http://www.ciq.co/warewulf) +* [Support](https://ciq.com/products/warewulf) ## About Warewulf diff --git a/warewulf.spec.in b/warewulf.spec.in index 8a6b15bc..725db9a7 100644 --- a/warewulf.spec.in +++ b/warewulf.spec.in @@ -165,10 +165,10 @@ getent group %{wwgroup} >/dev/null || groupadd -r %{wwgroup} - Update license string - Make shared store relocatable -* Fri Sep 24 2021 Michael L. Young - 4.2.0-1 +* Fri Sep 24 2021 Michael L. Young - 4.2.0-1 - Update spec file to use systemd macros - Use macros to refer to system paths - Update syntax -* Tue Jan 26 2021 14:46:24 JST Brian Clemens - 4.0.0 +* Tue Jan 26 2021 14:46:24 JST Brian Clemens - 4.0.0 - Initial release From b56f024e6f4dba35e74df0a9f18c00fba1fe4eb6 Mon Sep 17 00:00:00 2001 From: Arnaud Lecomte Date: Fri, 25 Aug 2023 13:54:39 +0200 Subject: [PATCH 71/75] Core of image's duplication feature --- internal/app/api/wwapid/wwapid.go | 21 ++++++++- internal/app/wwctl/container/copy/main.go | 48 ++++++++++++++++++++ internal/app/wwctl/container/copy/root.go | 33 ++++++++++++++ internal/app/wwctl/container/root.go | 2 + internal/pkg/api/routes/v1/routes.proto | 7 +++ internal/pkg/api/routes/wwapiv1/routes.pb.go | 10 ++++ internal/pkg/container/build.go | 2 +- internal/pkg/container/util.go | 31 +++++++++++-- 8 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 internal/app/wwctl/container/copy/main.go create mode 100644 internal/app/wwctl/container/copy/root.go diff --git a/internal/app/api/wwapid/wwapid.go b/internal/app/api/wwapid/wwapid.go index 5042b8c7..1d5419eb 100644 --- a/internal/app/api/wwapid/wwapid.go +++ b/internal/app/api/wwapid/wwapid.go @@ -15,8 +15,8 @@ import ( "github.com/hpcng/warewulf/internal/pkg/api/container" apinode "github.com/hpcng/warewulf/internal/pkg/api/node" "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" - "github.com/hpcng/warewulf/internal/pkg/version" warewulfconf "github.com/hpcng/warewulf/internal/pkg/config" + "github.com/hpcng/warewulf/internal/pkg/version" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" @@ -157,6 +157,25 @@ func (s *apiServer) ContainerBuild(ctx context.Context, request *wwapiv1.Contain return } +// ContainerCopy duplicates a container. +func (s *apiServer) ContainerCopy(ctx context.Context, request *wwapiv1.ContainerCopyParameter) (response *wwapiv1.ContainerListResponse, err error) { + + // Parameter checks. + if request == nil { + return response, status.Errorf(codes.InvalidArgument, "nil request") + } + + if request.ContainerSource == "" { + return response, status.Errorf(codes.InvalidArgument, "nil request.ContainerSource") + } + + if request.ContainerDestination == "" { + return response, status.Errorf(codes.InvalidArgument, "nil request.ContainerDest") + } + + return +} + // ContainerDelete deletes one or more containers from Warewulf. func (s *apiServer) ContainerDelete(ctx context.Context, request *wwapiv1.ContainerDeleteParameter) (response *emptypb.Empty, err error) { diff --git a/internal/app/wwctl/container/copy/main.go b/internal/app/wwctl/container/copy/main.go new file mode 100644 index 00000000..f3499f78 --- /dev/null +++ b/internal/app/wwctl/container/copy/main.go @@ -0,0 +1,48 @@ +package copy + +import ( + "fmt" + + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/container" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/spf13/cobra" +) + +func CobraRunE(cmd *cobra.Command, args []string) (err error) { + + if len(args) > 2 { + wwlog.Warn("cp only requires 2 arguments but you provided %d arguments. Hence, they will be ignored.", len(args)) + } + + cdp := &wwapiv1.ContainerCopyParameter{ + ContainerSource: args[0], + ContainerDestination: args[1], + } + + if !container.DoesSourceExist(cdp.ContainerSource) { + wwlog.Error("Container's source doesn't exists: %s", cdp.ContainerSource) + return + } + + if !container.ValidName(cdp.ContainerDestination) { + wwlog.Error("Container name contains illegal characters : %s", cdp.ContainerDestination) + return + } + + if container.DoesSourceExist(cdp.ContainerDestination) { + wwlog.Error("An other container with name: %s already exists in sources.", cdp.ContainerDestination) + return + } + + err = container.Duplicate(cdp.ContainerSource, cdp.ContainerDestination) + if err != nil { + err = fmt.Errorf("could not duplicate image: %s", err.Error()) + wwlog.Error(err.Error()) + return + } + + wwlog.Info("Container %s successfully duplicated as %s", cdp.ContainerSource, cdp.ContainerDestination) + return + +} diff --git a/internal/app/wwctl/container/copy/root.go b/internal/app/wwctl/container/copy/root.go new file mode 100644 index 00000000..5490adce --- /dev/null +++ b/internal/app/wwctl/container/copy/root.go @@ -0,0 +1,33 @@ +package copy + +import ( + "github.com/hpcng/warewulf/internal/pkg/container" + "github.com/spf13/cobra" +) + +var ( + baseCmd = &cobra.Command{ + DisableFlagsInUseLine: true, + Use: "cp CONTAINER NEW_NAME", + Short: "Copy an existing container", + Long: "This command will duplicate an imported container image.", + RunE: CobraRunE, + Args: cobra.MinimumNArgs(2), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + list, _ := container.ListSources() + return list, cobra.ShellCompDirectiveNoFileComp + }, + } +) + +func init() { + // Nothing to do here +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/container/root.go b/internal/app/wwctl/container/root.go index 363f82b5..6ffcf279 100644 --- a/internal/app/wwctl/container/root.go +++ b/internal/app/wwctl/container/root.go @@ -2,6 +2,7 @@ package container import ( "github.com/hpcng/warewulf/internal/app/wwctl/container/build" + "github.com/hpcng/warewulf/internal/app/wwctl/container/copy" "github.com/hpcng/warewulf/internal/app/wwctl/container/delete" "github.com/hpcng/warewulf/internal/app/wwctl/container/exec" "github.com/hpcng/warewulf/internal/app/wwctl/container/imprt" @@ -33,6 +34,7 @@ func init() { baseCmd.AddCommand(delete.GetCommand()) baseCmd.AddCommand(show.GetCommand()) baseCmd.AddCommand(syncuser.GetCommand()) + baseCmd.AddCommand(copy.GetCommand()) } diff --git a/internal/pkg/api/routes/v1/routes.proto b/internal/pkg/api/routes/v1/routes.proto index 83b844c5..e83389f4 100644 --- a/internal/pkg/api/routes/v1/routes.proto +++ b/internal/pkg/api/routes/v1/routes.proto @@ -32,6 +32,13 @@ message ContainerDeleteParameter { repeated string containerNames = 1; } +// ContainerCopyParameter contains 2 inputs : first one for the source container name and second one for the duplicated container name. +message ContainerCopyParameter { + repeated string containerSourceName = 1; + repeated string containerDestName = 1; + +} + // ContainerImportParameter has all input for importing a container. message ContainerImportParameter{ string source = 1; // container source uri diff --git a/internal/pkg/api/routes/wwapiv1/routes.pb.go b/internal/pkg/api/routes/wwapiv1/routes.pb.go index f13faca9..a3d34f7d 100644 --- a/internal/pkg/api/routes/wwapiv1/routes.pb.go +++ b/internal/pkg/api/routes/wwapiv1/routes.pb.go @@ -251,6 +251,16 @@ func (x *ContainerDeleteParameter) GetContainerNames() []string { return nil } +type ContainerCopyParameter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ContainerSource string `protobuf:"bytes,1,rep,name=ContainerSource,proto3" json:"ContainerSource,omitempty"` + ContainerDestination string `protobuf:"bytes,1,rep,name=ContainerDestination,proto3" json:"ContainerDestination,omitempty"` +} + + // ContainerImportParameter has all input for importing a container. type ContainerImportParameter struct { state protoimpl.MessageState diff --git a/internal/pkg/container/build.go b/internal/pkg/container/build.go index cd3e45d9..e9392d82 100644 --- a/internal/pkg/container/build.go +++ b/internal/pkg/container/build.go @@ -37,7 +37,7 @@ func Build(name string, buildForce bool) error { } err := util.BuildFsImage( - "VNFS container " + name, + "VNFS container "+name, rootfsPath, imagePath, []string{"*"}, diff --git a/internal/pkg/container/util.go b/internal/pkg/container/util.go index 48211710..67ff9c48 100644 --- a/internal/pkg/container/util.go +++ b/internal/pkg/container/util.go @@ -48,14 +48,22 @@ func ListSources() ([]string, error) { return ret, nil } -func ValidSource(name string) bool { - fullPath := RootFsDir(name) +func DoesContainerExists(name string) bool { + fullPath := ImageFile(name) + return util.IsFile(fullPath) +} +func DoesSourceExist(name string) bool { + fullPath := RootFsDir(name) + return util.IsDir(fullPath) +} + +func ValidSource(name string) bool { if !ValidName(name) { return false } - if !util.IsDir(fullPath) { + if !DoesSourceExist(name) { wwlog.Verbose("Location is not a VNFS source directory: %s", name) return false } @@ -73,6 +81,23 @@ func DeleteSource(name string) error { return os.RemoveAll(fullPath) } +func Duplicate(name string, destination string) error { + fullPathImageSource := RootFsDir(name) + + wwlog.Info("Copying sources...") + err := ImportDirectory(fullPathImageSource, destination) + + if err != nil { + return err + } + wwlog.Info("Building container: %s", destination) + err = Build(destination, true) + if err != nil { + return err + } + return nil +} + /* Delete the image of a container */ From f2392f6e374c5b6e3b133bacba5b6aeaff82e506 Mon Sep 17 00:00:00 2001 From: Arnaud Lecomte Date: Mon, 28 Aug 2023 08:39:15 +0200 Subject: [PATCH 72/75] Updating documentation & API endpoint for container copy --- API.md | 1 + CHANGELOG.md | 3 + CONTRIBUTORS.md | 1 + Makefile | 4 +- Variables.mk | 2 + go.mod | 2 +- internal/app/api/wwapid/wwapid.go | 11 +- internal/pkg/api/container/container.go | 25 + internal/pkg/api/routes/v1/routes.proto | 11 +- internal/pkg/api/routes/wwapiv1/routes.pb.go | 971 ++++++++++-------- .../pkg/api/routes/wwapiv1/routes.pb.gw.go | 95 +- .../pkg/api/routes/wwapiv1/routes_grpc.pb.go | 147 +-- userdocs/contents/containers.rst | 10 + userdocs/contents/provisioning.rst | 2 + 14 files changed, 754 insertions(+), 531 deletions(-) diff --git a/API.md b/API.md index d5e03737..9a8e8b0d 100644 --- a/API.md +++ b/API.md @@ -19,6 +19,7 @@ wwctl container delete wwctl container import wwctl container list wwctl container show +wwctl container cp Some notes on the files: diff --git a/CHANGELOG.md b/CHANGELOG.md index 22fe4306..d5e3beda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New documentation for the hostlist syntax. #611 - New documentation for development environment (Vagrant) +- Ability to duplicate an image with `wwctl container cp` or the API +- New documentation for container duplication procedure ### Fixed +- Fix hard CPU architecture on proto's installation in the Makefile - More aggressive `make clean`. - Replace deprecated `io.utils` functions with new `os` functions. - The correct header is now displayed when `-al` flags are specified to overlay diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index c3dcbeb1..9b394cae 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -26,3 +26,4 @@ * Brian Phan * Jeffrey Frey @jtfrey * Xu Yang(Jason Yang) @JasonYangShadow +* Arnaud LECOMTE diff --git a/Makefile b/Makefile index 0bbe2a59..424e106b 100644 --- a/Makefile +++ b/Makefile @@ -9,14 +9,14 @@ build: lint test vet all .PHONY: setup_tools setup_tools: $(GO_TOOLS_BIN) $(GOLANGCI_LINT) $(PROTOC_GEN_GO) $(PROTOC_GEN_GO_GRPC) -$(GO_TOOLS_BIN): +$(GO_TOOLS_BIN): GOBIN="$(PWD)/$(TOOLS_BIN)" go install -mod=vendor $(GO_TOOLS) $(GOLANGCI_LINT): curl -qq -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TOOLS_BIN) $(GOLANGCI_LINT_VERSION) $(PROTOC): - cd $(PWD)/$(TOOLS_DIR) && curl -LO $(PROTOC_URL) && unzip protoc-24.0-linux-aarch_64.zip + cd $(PWD)/$(TOOLS_DIR) && curl -LO $(PROTOC_URL) && unzip protoc-24.0-linux-$(ARCHITECTURE_CPU).zip $(PROTOC_GEN_GRPC_GATEWAY): curl -L $(PROTOC_GEN_GRPC_GATEWAY_URL) -o $(PROTOC_GEN_GRPC_GATEWAY) diff --git a/Variables.mk b/Variables.mk index 9e650460..51be6796 100644 --- a/Variables.mk +++ b/Variables.mk @@ -97,6 +97,8 @@ PROTOC_GEN_GRPC_GATEWAY_URL := https://github.com/grpc-ecosystem/grpc-gateway/re # helper functions godeps=$(shell go list -deps -f '{{if not .Standard}}{{ $$dep := . }}{{range .GoFiles}}{{$$dep.Dir}}/{{.}} {{end}}{{end}}' $(1) | sed "s%${PWD}/%%g") +ARCHITECTURE_CPU=$(shell lscpu | grep 'Architecture' | cut -d':' -f2 | xargs) + # use GOPROXY for older git clients and speed up downloads GOPROXY ?= https://proxy.golang.org export GOPROXY diff --git a/go.mod b/go.mod index c766128a..ad9c083d 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/creasty/defaults v1.7.0 github.com/fatih/color v1.15.0 github.com/golang/glog v1.0.0 - github.com/golang/protobuf v1.5.2 github.com/google/uuid v1.3.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 github.com/manifoldco/promptui v0.9.0 @@ -56,6 +55,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.3 // indirect github.com/gorilla/mux v1.7.4 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect diff --git a/internal/app/api/wwapid/wwapid.go b/internal/app/api/wwapid/wwapid.go index 1d5419eb..311addc6 100644 --- a/internal/app/api/wwapid/wwapid.go +++ b/internal/app/api/wwapid/wwapid.go @@ -158,21 +158,14 @@ func (s *apiServer) ContainerBuild(ctx context.Context, request *wwapiv1.Contain } // ContainerCopy duplicates a container. -func (s *apiServer) ContainerCopy(ctx context.Context, request *wwapiv1.ContainerCopyParameter) (response *wwapiv1.ContainerListResponse, err error) { +func (s *apiServer) ContainerCopy(ctx context.Context, request *wwapiv1.ContainerCopyParameter) (response *emptypb.Empty, err error) { // Parameter checks. if request == nil { return response, status.Errorf(codes.InvalidArgument, "nil request") } - if request.ContainerSource == "" { - return response, status.Errorf(codes.InvalidArgument, "nil request.ContainerSource") - } - - if request.ContainerDestination == "" { - return response, status.Errorf(codes.InvalidArgument, "nil request.ContainerDest") - } - + err = container.ContainerCopy(request) return } diff --git a/internal/pkg/api/container/container.go b/internal/pkg/api/container/container.go index 6c427664..2539ff50 100644 --- a/internal/pkg/api/container/container.go +++ b/internal/pkg/api/container/container.go @@ -18,6 +18,31 @@ import ( "github.com/pkg/errors" ) +func ContainerCopy(cbp *wwapiv1.ContainerCopyParameter) (err error) { + if cbp == nil { + return fmt.Errorf("ContainerCopyParameter is nil") + } + + if !container.DoesSourceExist(cbp.ContainerSource) { + return fmt.Errorf("Container %s does not exists.", cbp.ContainerSource) + } + + if !container.ValidName(cbp.ContainerDestination) { + return fmt.Errorf("Container name contains illegal characters : %s", cbp.ContainerDestination) + } + + if container.DoesSourceExist(cbp.ContainerDestination) { + return fmt.Errorf("An other container with the name %s already exists", cbp.ContainerDestination) + } + + err = container.Duplicate(cbp.ContainerSource, cbp.ContainerDestination) + if err != nil { + return fmt.Errorf("could not duplicate image: %s", err.Error()) + } + + return fmt.Errorf("Container %s has been succesfully duplicated as %s", cbp.ContainerSource, cbp.ContainerDestination) +} + func ContainerBuild(cbp *wwapiv1.ContainerBuildParameter) (err error) { if cbp == nil { return fmt.Errorf("ContainerBuildParameter is nil") diff --git a/internal/pkg/api/routes/v1/routes.proto b/internal/pkg/api/routes/v1/routes.proto index e83389f4..8684b5e8 100644 --- a/internal/pkg/api/routes/v1/routes.proto +++ b/internal/pkg/api/routes/v1/routes.proto @@ -34,8 +34,8 @@ message ContainerDeleteParameter { // ContainerCopyParameter contains 2 inputs : first one for the source container name and second one for the duplicated container name. message ContainerCopyParameter { - repeated string containerSourceName = 1; - repeated string containerDestName = 1; + string containerSource = 1; + string containerDestination = 2; } @@ -246,6 +246,13 @@ service WWApi { }; } + + rpc ContainerCopy(ContainerCopyParameter) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/v1/containercopy" + body: "*" + }; + } // ContainerImport imports a container to Warewulf. rpc ContainerImport(ContainerImportParameter) returns (ContainerListResponse) { option(google.api.http) = { diff --git a/internal/pkg/api/routes/wwapiv1/routes.pb.go b/internal/pkg/api/routes/wwapiv1/routes.pb.go index a3d34f7d..4e25603f 100644 --- a/internal/pkg/api/routes/wwapiv1/routes.pb.go +++ b/internal/pkg/api/routes/wwapiv1/routes.pb.go @@ -4,16 +4,16 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.1 +// protoc v4.24.0 // source: routes.proto package wwapiv1 import ( - empty "github.com/golang/protobuf/ptypes/empty" _ "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" ) @@ -80,7 +80,7 @@ func (x GetNodeList_ListType) Number() protoreflect.EnumNumber { // Deprecated: Use GetNodeList_ListType.Descriptor instead. func (GetNodeList_ListType) EnumDescriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{14, 0} + return file_routes_proto_rawDescGZIP(), []int{15, 0} } type NodeDBHash struct { @@ -251,15 +251,61 @@ func (x *ContainerDeleteParameter) GetContainerNames() []string { return nil } +// ContainerCopyParameter contains 2 inputs : first one for the source container name and second one for the duplicated container name. type ContainerCopyParameter struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ContainerSource string `protobuf:"bytes,1,rep,name=ContainerSource,proto3" json:"ContainerSource,omitempty"` - ContainerDestination string `protobuf:"bytes,1,rep,name=ContainerDestination,proto3" json:"ContainerDestination,omitempty"` + ContainerSource string `protobuf:"bytes,1,opt,name=containerSource,proto3" json:"containerSource,omitempty"` + ContainerDestination string `protobuf:"bytes,2,opt,name=containerDestination,proto3" json:"containerDestination,omitempty"` } +func (x *ContainerCopyParameter) Reset() { + *x = ContainerCopyParameter{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContainerCopyParameter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContainerCopyParameter) ProtoMessage() {} + +func (x *ContainerCopyParameter) 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 ContainerCopyParameter.ProtoReflect.Descriptor instead. +func (*ContainerCopyParameter) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{3} +} + +func (x *ContainerCopyParameter) GetContainerSource() string { + if x != nil { + return x.ContainerSource + } + return "" +} + +func (x *ContainerCopyParameter) GetContainerDestination() string { + if x != nil { + return x.ContainerDestination + } + return "" +} // ContainerImportParameter has all input for importing a container. type ContainerImportParameter struct { @@ -279,7 +325,7 @@ type ContainerImportParameter struct { func (x *ContainerImportParameter) Reset() { *x = ContainerImportParameter{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[3] + mi := &file_routes_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -292,7 +338,7 @@ func (x *ContainerImportParameter) String() string { func (*ContainerImportParameter) ProtoMessage() {} func (x *ContainerImportParameter) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[3] + mi := &file_routes_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -305,7 +351,7 @@ func (x *ContainerImportParameter) ProtoReflect() protoreflect.Message { // Deprecated: Use ContainerImportParameter.ProtoReflect.Descriptor instead. func (*ContainerImportParameter) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{3} + return file_routes_proto_rawDescGZIP(), []int{4} } func (x *ContainerImportParameter) GetSource() string { @@ -375,7 +421,7 @@ type ContainerInfo struct { func (x *ContainerInfo) Reset() { *x = ContainerInfo{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[4] + mi := &file_routes_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -388,7 +434,7 @@ func (x *ContainerInfo) String() string { func (*ContainerInfo) ProtoMessage() {} func (x *ContainerInfo) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[4] + mi := &file_routes_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -401,7 +447,7 @@ func (x *ContainerInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ContainerInfo.ProtoReflect.Descriptor instead. func (*ContainerInfo) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{4} + return file_routes_proto_rawDescGZIP(), []int{5} } func (x *ContainerInfo) GetName() string { @@ -458,7 +504,7 @@ type ContainerListResponse struct { func (x *ContainerListResponse) Reset() { *x = ContainerListResponse{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[5] + mi := &file_routes_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -471,7 +517,7 @@ func (x *ContainerListResponse) String() string { func (*ContainerListResponse) ProtoMessage() {} func (x *ContainerListResponse) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[5] + mi := &file_routes_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -484,7 +530,7 @@ func (x *ContainerListResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ContainerListResponse.ProtoReflect.Descriptor instead. func (*ContainerListResponse) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{5} + return file_routes_proto_rawDescGZIP(), []int{6} } func (x *ContainerListResponse) GetContainers() []*ContainerInfo { @@ -506,7 +552,7 @@ type ContainerShowParameter struct { func (x *ContainerShowParameter) Reset() { *x = ContainerShowParameter{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[6] + mi := &file_routes_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -519,7 +565,7 @@ func (x *ContainerShowParameter) String() string { func (*ContainerShowParameter) ProtoMessage() {} func (x *ContainerShowParameter) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[6] + mi := &file_routes_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -532,7 +578,7 @@ func (x *ContainerShowParameter) ProtoReflect() protoreflect.Message { // Deprecated: Use ContainerShowParameter.ProtoReflect.Descriptor instead. func (*ContainerShowParameter) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{6} + return file_routes_proto_rawDescGZIP(), []int{7} } func (x *ContainerShowParameter) GetContainerName() string { @@ -557,7 +603,7 @@ type ContainerShowResponse struct { func (x *ContainerShowResponse) Reset() { *x = ContainerShowResponse{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[7] + mi := &file_routes_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -570,7 +616,7 @@ func (x *ContainerShowResponse) String() string { func (*ContainerShowResponse) ProtoMessage() {} func (x *ContainerShowResponse) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[7] + mi := &file_routes_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -583,7 +629,7 @@ func (x *ContainerShowResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ContainerShowResponse.ProtoReflect.Descriptor instead. func (*ContainerShowResponse) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{7} + return file_routes_proto_rawDescGZIP(), []int{8} } func (x *ContainerShowResponse) GetName() string { @@ -626,7 +672,7 @@ type ContainerSyncUserParameter struct { func (x *ContainerSyncUserParameter) Reset() { *x = ContainerSyncUserParameter{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[8] + mi := &file_routes_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -639,7 +685,7 @@ func (x *ContainerSyncUserParameter) String() string { func (*ContainerSyncUserParameter) ProtoMessage() {} func (x *ContainerSyncUserParameter) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[8] + mi := &file_routes_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -652,7 +698,7 @@ func (x *ContainerSyncUserParameter) ProtoReflect() protoreflect.Message { // Deprecated: Use ContainerSyncUserParameter.ProtoReflect.Descriptor instead. func (*ContainerSyncUserParameter) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{8} + return file_routes_proto_rawDescGZIP(), []int{9} } func (x *ContainerSyncUserParameter) GetContainerName() string { @@ -674,7 +720,7 @@ type NodeNames struct { func (x *NodeNames) Reset() { *x = NodeNames{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[9] + mi := &file_routes_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -687,7 +733,7 @@ func (x *NodeNames) String() string { func (*NodeNames) ProtoMessage() {} func (x *NodeNames) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[9] + mi := &file_routes_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -700,7 +746,7 @@ func (x *NodeNames) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeNames.ProtoReflect.Descriptor instead. func (*NodeNames) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{9} + return file_routes_proto_rawDescGZIP(), []int{10} } func (x *NodeNames) GetNodeNames() []string { @@ -724,7 +770,7 @@ type NodeField struct { func (x *NodeField) Reset() { *x = NodeField{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[10] + mi := &file_routes_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -737,7 +783,7 @@ func (x *NodeField) String() string { func (*NodeField) ProtoMessage() {} func (x *NodeField) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[10] + mi := &file_routes_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -750,7 +796,7 @@ func (x *NodeField) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeField.ProtoReflect.Descriptor instead. func (*NodeField) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{10} + return file_routes_proto_rawDescGZIP(), []int{11} } func (x *NodeField) GetSource() string { @@ -787,7 +833,7 @@ type NetDev struct { func (x *NetDev) Reset() { *x = NetDev{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[11] + mi := &file_routes_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -800,7 +846,7 @@ func (x *NetDev) String() string { func (*NetDev) ProtoMessage() {} func (x *NetDev) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[11] + mi := &file_routes_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -813,7 +859,7 @@ func (x *NetDev) ProtoReflect() protoreflect.Message { // Deprecated: Use NetDev.ProtoReflect.Descriptor instead. func (*NetDev) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{11} + return file_routes_proto_rawDescGZIP(), []int{12} } func (x *NetDev) GetField() map[string]*NodeField { @@ -845,7 +891,7 @@ type NodeInfo struct { func (x *NodeInfo) Reset() { *x = NodeInfo{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[12] + mi := &file_routes_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -858,7 +904,7 @@ func (x *NodeInfo) String() string { func (*NodeInfo) ProtoMessage() {} func (x *NodeInfo) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[12] + mi := &file_routes_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -871,7 +917,7 @@ func (x *NodeInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeInfo.ProtoReflect.Descriptor instead. func (*NodeInfo) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{12} + return file_routes_proto_rawDescGZIP(), []int{13} } func (x *NodeInfo) GetFields() map[string]*NodeField { @@ -914,7 +960,7 @@ type NodeListResponse struct { func (x *NodeListResponse) Reset() { *x = NodeListResponse{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[13] + mi := &file_routes_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -927,7 +973,7 @@ func (x *NodeListResponse) String() string { func (*NodeListResponse) ProtoMessage() {} func (x *NodeListResponse) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[13] + mi := &file_routes_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -940,7 +986,7 @@ func (x *NodeListResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeListResponse.ProtoReflect.Descriptor instead. func (*NodeListResponse) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{13} + return file_routes_proto_rawDescGZIP(), []int{14} } func (x *NodeListResponse) GetNodes() []*NodeInfo { @@ -963,7 +1009,7 @@ type GetNodeList struct { func (x *GetNodeList) Reset() { *x = GetNodeList{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[14] + mi := &file_routes_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -976,7 +1022,7 @@ func (x *GetNodeList) String() string { func (*GetNodeList) ProtoMessage() {} func (x *GetNodeList) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[14] + mi := &file_routes_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -989,7 +1035,7 @@ func (x *GetNodeList) ProtoReflect() protoreflect.Message { // Deprecated: Use GetNodeList.ProtoReflect.Descriptor instead. func (*GetNodeList) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{14} + return file_routes_proto_rawDescGZIP(), []int{15} } func (x *GetNodeList) GetType() GetNodeList_ListType { @@ -1018,7 +1064,7 @@ type NodeList struct { func (x *NodeList) Reset() { *x = NodeList{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[15] + mi := &file_routes_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1031,7 +1077,7 @@ func (x *NodeList) String() string { func (*NodeList) ProtoMessage() {} func (x *NodeList) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[15] + mi := &file_routes_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1044,7 +1090,7 @@ func (x *NodeList) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeList.ProtoReflect.Descriptor instead. func (*NodeList) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{15} + return file_routes_proto_rawDescGZIP(), []int{16} } func (x *NodeList) GetOutput() []string { @@ -1068,7 +1114,7 @@ type GetProfileList struct { func (x *GetProfileList) Reset() { *x = GetProfileList{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[16] + mi := &file_routes_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1081,7 +1127,7 @@ func (x *GetProfileList) String() string { func (*GetProfileList) ProtoMessage() {} func (x *GetProfileList) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[16] + mi := &file_routes_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1094,7 +1140,7 @@ func (x *GetProfileList) ProtoReflect() protoreflect.Message { // Deprecated: Use GetProfileList.ProtoReflect.Descriptor instead. func (*GetProfileList) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{16} + return file_routes_proto_rawDescGZIP(), []int{17} } func (x *GetProfileList) GetShowAll() bool { @@ -1130,7 +1176,7 @@ type ProfileList struct { func (x *ProfileList) Reset() { *x = ProfileList{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[17] + mi := &file_routes_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1143,7 +1189,7 @@ func (x *ProfileList) String() string { func (*ProfileList) ProtoMessage() {} func (x *ProfileList) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[17] + mi := &file_routes_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1156,7 +1202,7 @@ func (x *ProfileList) ProtoReflect() protoreflect.Message { // Deprecated: Use ProfileList.ProtoReflect.Descriptor instead. func (*ProfileList) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{17} + return file_routes_proto_rawDescGZIP(), []int{18} } func (x *ProfileList) GetOutput() []string { @@ -1183,7 +1229,7 @@ type NodeAddParameter struct { func (x *NodeAddParameter) Reset() { *x = NodeAddParameter{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[18] + mi := &file_routes_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1196,7 +1242,7 @@ func (x *NodeAddParameter) String() string { func (*NodeAddParameter) ProtoMessage() {} func (x *NodeAddParameter) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[18] + mi := &file_routes_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1209,7 +1255,7 @@ func (x *NodeAddParameter) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeAddParameter.ProtoReflect.Descriptor instead. func (*NodeAddParameter) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{18} + return file_routes_proto_rawDescGZIP(), []int{19} } func (x *NodeAddParameter) GetNodeConfYaml() string { @@ -1254,7 +1300,7 @@ type NodeYaml struct { func (x *NodeYaml) Reset() { *x = NodeYaml{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[19] + mi := &file_routes_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1267,7 +1313,7 @@ func (x *NodeYaml) String() string { func (*NodeYaml) ProtoMessage() {} func (x *NodeYaml) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[19] + mi := &file_routes_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1280,7 +1326,7 @@ func (x *NodeYaml) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeYaml.ProtoReflect.Descriptor instead. func (*NodeYaml) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{19} + return file_routes_proto_rawDescGZIP(), []int{20} } func (x *NodeYaml) GetNodeConfMapYaml() string { @@ -1314,7 +1360,7 @@ type NodeDeleteParameter struct { func (x *NodeDeleteParameter) Reset() { *x = NodeDeleteParameter{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[20] + mi := &file_routes_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1327,7 +1373,7 @@ func (x *NodeDeleteParameter) String() string { func (*NodeDeleteParameter) ProtoMessage() {} func (x *NodeDeleteParameter) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[20] + mi := &file_routes_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1340,7 +1386,7 @@ func (x *NodeDeleteParameter) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeDeleteParameter.ProtoReflect.Descriptor instead. func (*NodeDeleteParameter) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{20} + return file_routes_proto_rawDescGZIP(), []int{21} } func (x *NodeDeleteParameter) GetForce() bool { @@ -1384,7 +1430,7 @@ type NodeSetParameter struct { func (x *NodeSetParameter) Reset() { *x = NodeSetParameter{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[21] + mi := &file_routes_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1397,7 +1443,7 @@ func (x *NodeSetParameter) String() string { func (*NodeSetParameter) ProtoMessage() {} func (x *NodeSetParameter) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[21] + mi := &file_routes_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1410,7 +1456,7 @@ func (x *NodeSetParameter) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeSetParameter.ProtoReflect.Descriptor instead. func (*NodeSetParameter) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{21} + return file_routes_proto_rawDescGZIP(), []int{22} } func (x *NodeSetParameter) GetNodeConfYaml() string { @@ -1489,7 +1535,7 @@ type ProfileSetParameter struct { func (x *ProfileSetParameter) Reset() { *x = ProfileSetParameter{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[22] + mi := &file_routes_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1502,7 +1548,7 @@ func (x *ProfileSetParameter) String() string { func (*ProfileSetParameter) ProtoMessage() {} func (x *ProfileSetParameter) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[22] + mi := &file_routes_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1515,7 +1561,7 @@ func (x *ProfileSetParameter) ProtoReflect() protoreflect.Message { // Deprecated: Use ProfileSetParameter.ProtoReflect.Descriptor instead. func (*ProfileSetParameter) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{22} + return file_routes_proto_rawDescGZIP(), []int{23} } func (x *ProfileSetParameter) GetNodeConfYaml() string { @@ -1590,7 +1636,7 @@ type NodeStatus struct { func (x *NodeStatus) Reset() { *x = NodeStatus{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[23] + mi := &file_routes_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1603,7 +1649,7 @@ func (x *NodeStatus) String() string { func (*NodeStatus) ProtoMessage() {} func (x *NodeStatus) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[23] + mi := &file_routes_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1616,7 +1662,7 @@ func (x *NodeStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeStatus.ProtoReflect.Descriptor instead. func (*NodeStatus) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{23} + return file_routes_proto_rawDescGZIP(), []int{24} } func (x *NodeStatus) GetNodeName() string { @@ -1666,7 +1712,7 @@ type NodeStatusResponse struct { func (x *NodeStatusResponse) Reset() { *x = NodeStatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[24] + mi := &file_routes_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1679,7 +1725,7 @@ func (x *NodeStatusResponse) String() string { func (*NodeStatusResponse) ProtoMessage() {} func (x *NodeStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[24] + mi := &file_routes_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1692,7 +1738,7 @@ func (x *NodeStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeStatusResponse.ProtoReflect.Descriptor instead. func (*NodeStatusResponse) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{24} + return file_routes_proto_rawDescGZIP(), []int{25} } func (x *NodeStatusResponse) GetNodeStatus() []*NodeStatus { @@ -1716,7 +1762,7 @@ type VersionResponse struct { func (x *VersionResponse) Reset() { *x = VersionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[25] + mi := &file_routes_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1729,7 +1775,7 @@ func (x *VersionResponse) String() string { func (*VersionResponse) ProtoMessage() {} func (x *VersionResponse) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[25] + mi := &file_routes_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1742,7 +1788,7 @@ func (x *VersionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VersionResponse.ProtoReflect.Descriptor instead. func (*VersionResponse) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{25} + return file_routes_proto_rawDescGZIP(), []int{26} } func (x *VersionResponse) GetApiPrefix() string { @@ -1778,7 +1824,7 @@ type CanWriteConfig struct { func (x *CanWriteConfig) Reset() { *x = CanWriteConfig{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[26] + mi := &file_routes_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1791,7 +1837,7 @@ func (x *CanWriteConfig) String() string { func (*CanWriteConfig) ProtoMessage() {} func (x *CanWriteConfig) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[26] + mi := &file_routes_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1804,7 +1850,7 @@ func (x *CanWriteConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use CanWriteConfig.ProtoReflect.Descriptor instead. func (*CanWriteConfig) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{26} + return file_routes_proto_rawDescGZIP(), []int{27} } func (x *CanWriteConfig) GetCanWriteConfig() bool { @@ -1837,286 +1883,300 @@ var file_routes_proto_rawDesc = []byte{ 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, 0xb5, 0x01, 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, 0x12, 0x1e, 0x0a, 0x0a, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, - 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 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, 0x88, 0x02, - 0x0a, 0x06, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x12, 0x31, 0x0a, 0x05, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, 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, 0x4d, 0x0a, 0x0a, 0x46, - 0x69, 0x65, 0x6c, 0x64, 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, 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, 0x9b, 0x04, 0x0a, 0x08, 0x4e, 0x6f, 0x64, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x36, 0x0a, 0x06, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x39, 0x0a, - 0x07, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, + 0x76, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x70, 0x79, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x12, 0x32, 0x0a, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x14, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0xb5, 0x01, 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, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x61, + 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x44, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, + 0x7a, 0x65, 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, 0x88, 0x02, 0x0a, 0x06, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, + 0x12, 0x31, 0x0a, 0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x44, 0x65, + 0x76, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x46, 0x69, + 0x65, 0x6c, 0x64, 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, 0x4d, 0x0a, 0x0a, 0x46, 0x69, 0x65, 0x6c, 0x64, 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, 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, 0x9b, 0x04, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x36, 0x0a, + 0x06, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x46, + 0x69, 0x65, 0x6c, 0x64, 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, 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, 0x4e, 0x0a, 0x0b, - 0x46, 0x69, 0x65, 0x6c, 0x64, 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, 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, 0xa6, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, - 0x4c, 0x69, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x6f, 0x64, 0x65, - 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x4d, - 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x69, - 0x6d, 0x70, 0x6c, 0x65, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x70, 0x6d, 0x69, 0x10, 0x01, - 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x10, 0x02, 0x12, 0x08, 0x0a, - 0x04, 0x4c, 0x6f, 0x6e, 0x67, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x6c, 0x6c, 0x10, 0x04, - 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x75, 0x6c, 0x6c, 0x41, 0x6c, 0x6c, 0x10, 0x05, 0x22, 0x22, 0x0a, - 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x4f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x22, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4c, - 0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x12, 0x20, 0x0a, - 0x0b, 0x53, 0x68, 0x6f, 0x77, 0x46, 0x75, 0x6c, 0x6c, 0x41, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0b, 0x53, 0x68, 0x6f, 0x77, 0x46, 0x75, 0x6c, 0x6c, 0x41, 0x6c, 0x6c, 0x12, - 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x25, 0x0a, 0x0b, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x4f, 0x75, 0x74, 0x70, - 0x75, 0x74, 0x22, 0x7e, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, - 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, - 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x68, 0x61, 0x73, 0x68, 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, 0x22, 0x48, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x28, - 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x4d, 0x61, 0x70, 0x59, 0x61, 0x6d, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, - 0x66, 0x4d, 0x61, 0x70, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0x5d, 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, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0xa0, 0x02, 0x0a, 0x10, - 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x65, 0x74, 0x64, - 0x65, 0x76, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x73, 0x6b, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, - 0x73, 0x6b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x70, 0x61, 0x72, 0x74, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x66, 0x69, - 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1a, - 0x0a, 0x08, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x08, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, - 0x72, 0x63, 0x65, 0x18, 0x07, 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, 0x08, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xaf, - 0x02, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, - 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x65, - 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1e, - 0x0a, 0x0a, 0x64, 0x69, 0x73, 0x6b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, 0x73, 0x6b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x28, - 0x0a, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x65, - 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x50, 0x72, - 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x22, 0x0a, 0x0c, - 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 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, - 0x22, 0x38, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x63, 0x61, 0x6e, 0x57, - 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, + 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, 0x4e, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 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, 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, 0xa6, 0x01, 0x0a, + 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x77, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, + 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x4d, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x10, 0x00, 0x12, 0x08, + 0x0a, 0x04, 0x49, 0x70, 0x6d, 0x69, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x6f, 0x6e, 0x67, 0x10, 0x03, 0x12, + 0x07, 0x0a, 0x03, 0x41, 0x6c, 0x6c, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x75, 0x6c, 0x6c, + 0x41, 0x6c, 0x6c, 0x10, 0x05, 0x22, 0x22, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x53, + 0x68, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x68, + 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x53, 0x68, 0x6f, 0x77, 0x46, 0x75, 0x6c, + 0x6c, 0x41, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x53, 0x68, 0x6f, 0x77, + 0x46, 0x75, 0x6c, 0x6c, 0x41, 0x6c, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x22, 0x25, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0x7e, 0x0a, 0x10, 0x4e, 0x6f, + 0x64, 0x65, 0x41, 0x64, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x22, + 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, + 0x6d, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 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, 0x22, 0x48, 0x0a, 0x08, 0x4e, 0x6f, + 0x64, 0x65, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x28, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, + 0x6e, 0x66, 0x4d, 0x61, 0x70, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x4d, 0x61, 0x70, 0x59, 0x61, 0x6d, 0x6c, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x68, 0x61, 0x73, 0x68, 0x22, 0x5d, 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, 0x12, + 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, + 0x61, 0x73, 0x68, 0x22, 0xa0, 0x02, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x74, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, + 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x22, 0x0a, 0x0c, + 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x73, 0x6b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, 0x73, 0x6b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x12, 0x28, 0x0a, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, + 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, + 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x07, 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, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x64, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xaf, 0x02, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x53, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x22, + 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, + 0x6d, 0x6c, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x73, 0x6b, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, 0x73, 0x6b, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x12, 0x2a, 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, + 0x61, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, + 0x6f, 0x72, 0x63, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 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, 0x22, 0x38, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x57, + 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x61, + 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0e, 0x63, 0x61, 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x32, 0x8f, 0x09, 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, 0x67, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x20, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x70, + 0x79, 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, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x63, 0x6f, 0x70, 0x79, 0x3a, 0x01, 0x2a, + 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, 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, + 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 ( @@ -2132,85 +2192,88 @@ func file_routes_proto_rawDescGZIP() []byte { } var file_routes_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_routes_proto_msgTypes = make([]protoimpl.MessageInfo, 33) +var file_routes_proto_msgTypes = make([]protoimpl.MessageInfo, 34) var file_routes_proto_goTypes = []interface{}{ (GetNodeList_ListType)(0), // 0: wwapi.v1.GetNodeList.ListType (*NodeDBHash)(nil), // 1: wwapi.v1.NodeDBHash (*ContainerBuildParameter)(nil), // 2: wwapi.v1.ContainerBuildParameter (*ContainerDeleteParameter)(nil), // 3: wwapi.v1.ContainerDeleteParameter - (*ContainerImportParameter)(nil), // 4: wwapi.v1.ContainerImportParameter - (*ContainerInfo)(nil), // 5: wwapi.v1.ContainerInfo - (*ContainerListResponse)(nil), // 6: wwapi.v1.ContainerListResponse - (*ContainerShowParameter)(nil), // 7: wwapi.v1.ContainerShowParameter - (*ContainerShowResponse)(nil), // 8: wwapi.v1.ContainerShowResponse - (*ContainerSyncUserParameter)(nil), // 9: wwapi.v1.ContainerSyncUserParameter - (*NodeNames)(nil), // 10: wwapi.v1.NodeNames - (*NodeField)(nil), // 11: wwapi.v1.NodeField - (*NetDev)(nil), // 12: wwapi.v1.NetDev - (*NodeInfo)(nil), // 13: wwapi.v1.NodeInfo - (*NodeListResponse)(nil), // 14: wwapi.v1.NodeListResponse - (*GetNodeList)(nil), // 15: wwapi.v1.GetNodeList - (*NodeList)(nil), // 16: wwapi.v1.NodeList - (*GetProfileList)(nil), // 17: wwapi.v1.GetProfileList - (*ProfileList)(nil), // 18: wwapi.v1.ProfileList - (*NodeAddParameter)(nil), // 19: wwapi.v1.NodeAddParameter - (*NodeYaml)(nil), // 20: wwapi.v1.NodeYaml - (*NodeDeleteParameter)(nil), // 21: wwapi.v1.NodeDeleteParameter - (*NodeSetParameter)(nil), // 22: wwapi.v1.NodeSetParameter - (*ProfileSetParameter)(nil), // 23: wwapi.v1.ProfileSetParameter - (*NodeStatus)(nil), // 24: wwapi.v1.NodeStatus - (*NodeStatusResponse)(nil), // 25: wwapi.v1.NodeStatusResponse - (*VersionResponse)(nil), // 26: wwapi.v1.VersionResponse - (*CanWriteConfig)(nil), // 27: wwapi.v1.CanWriteConfig - nil, // 28: wwapi.v1.NetDev.FieldEntry - nil, // 29: wwapi.v1.NetDev.TagsEntry - nil, // 30: wwapi.v1.NodeInfo.FieldsEntry - nil, // 31: wwapi.v1.NodeInfo.NetDevsEntry - nil, // 32: wwapi.v1.NodeInfo.TagsEntry - nil, // 33: wwapi.v1.NodeInfo.KeysEntry - (*empty.Empty)(nil), // 34: google.protobuf.Empty + (*ContainerCopyParameter)(nil), // 4: wwapi.v1.ContainerCopyParameter + (*ContainerImportParameter)(nil), // 5: wwapi.v1.ContainerImportParameter + (*ContainerInfo)(nil), // 6: wwapi.v1.ContainerInfo + (*ContainerListResponse)(nil), // 7: wwapi.v1.ContainerListResponse + (*ContainerShowParameter)(nil), // 8: wwapi.v1.ContainerShowParameter + (*ContainerShowResponse)(nil), // 9: wwapi.v1.ContainerShowResponse + (*ContainerSyncUserParameter)(nil), // 10: wwapi.v1.ContainerSyncUserParameter + (*NodeNames)(nil), // 11: wwapi.v1.NodeNames + (*NodeField)(nil), // 12: wwapi.v1.NodeField + (*NetDev)(nil), // 13: wwapi.v1.NetDev + (*NodeInfo)(nil), // 14: wwapi.v1.NodeInfo + (*NodeListResponse)(nil), // 15: wwapi.v1.NodeListResponse + (*GetNodeList)(nil), // 16: wwapi.v1.GetNodeList + (*NodeList)(nil), // 17: wwapi.v1.NodeList + (*GetProfileList)(nil), // 18: wwapi.v1.GetProfileList + (*ProfileList)(nil), // 19: wwapi.v1.ProfileList + (*NodeAddParameter)(nil), // 20: wwapi.v1.NodeAddParameter + (*NodeYaml)(nil), // 21: wwapi.v1.NodeYaml + (*NodeDeleteParameter)(nil), // 22: wwapi.v1.NodeDeleteParameter + (*NodeSetParameter)(nil), // 23: wwapi.v1.NodeSetParameter + (*ProfileSetParameter)(nil), // 24: wwapi.v1.ProfileSetParameter + (*NodeStatus)(nil), // 25: wwapi.v1.NodeStatus + (*NodeStatusResponse)(nil), // 26: wwapi.v1.NodeStatusResponse + (*VersionResponse)(nil), // 27: wwapi.v1.VersionResponse + (*CanWriteConfig)(nil), // 28: wwapi.v1.CanWriteConfig + nil, // 29: wwapi.v1.NetDev.FieldEntry + nil, // 30: wwapi.v1.NetDev.TagsEntry + nil, // 31: wwapi.v1.NodeInfo.FieldsEntry + nil, // 32: wwapi.v1.NodeInfo.NetDevsEntry + nil, // 33: wwapi.v1.NodeInfo.TagsEntry + nil, // 34: wwapi.v1.NodeInfo.KeysEntry + (*emptypb.Empty)(nil), // 35: google.protobuf.Empty } var file_routes_proto_depIdxs = []int32{ - 5, // 0: wwapi.v1.ContainerListResponse.containers:type_name -> wwapi.v1.ContainerInfo - 28, // 1: wwapi.v1.NetDev.Field:type_name -> wwapi.v1.NetDev.FieldEntry - 29, // 2: wwapi.v1.NetDev.Tags:type_name -> wwapi.v1.NetDev.TagsEntry - 30, // 3: wwapi.v1.NodeInfo.Fields:type_name -> wwapi.v1.NodeInfo.FieldsEntry - 31, // 4: wwapi.v1.NodeInfo.NetDevs:type_name -> wwapi.v1.NodeInfo.NetDevsEntry - 32, // 5: wwapi.v1.NodeInfo.Tags:type_name -> wwapi.v1.NodeInfo.TagsEntry - 33, // 6: wwapi.v1.NodeInfo.Keys:type_name -> wwapi.v1.NodeInfo.KeysEntry - 13, // 7: wwapi.v1.NodeListResponse.nodes:type_name -> wwapi.v1.NodeInfo + 6, // 0: wwapi.v1.ContainerListResponse.containers:type_name -> wwapi.v1.ContainerInfo + 29, // 1: wwapi.v1.NetDev.Field:type_name -> wwapi.v1.NetDev.FieldEntry + 30, // 2: wwapi.v1.NetDev.Tags:type_name -> wwapi.v1.NetDev.TagsEntry + 31, // 3: wwapi.v1.NodeInfo.Fields:type_name -> wwapi.v1.NodeInfo.FieldsEntry + 32, // 4: wwapi.v1.NodeInfo.NetDevs:type_name -> wwapi.v1.NodeInfo.NetDevsEntry + 33, // 5: wwapi.v1.NodeInfo.Tags:type_name -> wwapi.v1.NodeInfo.TagsEntry + 34, // 6: wwapi.v1.NodeInfo.Keys:type_name -> wwapi.v1.NodeInfo.KeysEntry + 14, // 7: wwapi.v1.NodeListResponse.nodes:type_name -> wwapi.v1.NodeInfo 0, // 8: wwapi.v1.GetNodeList.type:type_name -> wwapi.v1.GetNodeList.ListType - 24, // 9: wwapi.v1.NodeStatusResponse.nodeStatus:type_name -> wwapi.v1.NodeStatus - 11, // 10: wwapi.v1.NetDev.FieldEntry.value:type_name -> wwapi.v1.NodeField - 11, // 11: wwapi.v1.NetDev.TagsEntry.value:type_name -> wwapi.v1.NodeField - 11, // 12: wwapi.v1.NodeInfo.FieldsEntry.value:type_name -> wwapi.v1.NodeField - 12, // 13: wwapi.v1.NodeInfo.NetDevsEntry.value:type_name -> wwapi.v1.NetDev - 11, // 14: wwapi.v1.NodeInfo.TagsEntry.value:type_name -> wwapi.v1.NodeField - 11, // 15: wwapi.v1.NodeInfo.KeysEntry.value:type_name -> wwapi.v1.NodeField + 25, // 9: wwapi.v1.NodeStatusResponse.nodeStatus:type_name -> wwapi.v1.NodeStatus + 12, // 10: wwapi.v1.NetDev.FieldEntry.value:type_name -> wwapi.v1.NodeField + 12, // 11: wwapi.v1.NetDev.TagsEntry.value:type_name -> wwapi.v1.NodeField + 12, // 12: wwapi.v1.NodeInfo.FieldsEntry.value:type_name -> wwapi.v1.NodeField + 13, // 13: wwapi.v1.NodeInfo.NetDevsEntry.value:type_name -> wwapi.v1.NetDev + 12, // 14: wwapi.v1.NodeInfo.TagsEntry.value:type_name -> wwapi.v1.NodeField + 12, // 15: wwapi.v1.NodeInfo.KeysEntry.value:type_name -> wwapi.v1.NodeField 2, // 16: wwapi.v1.WWApi.ContainerBuild:input_type -> wwapi.v1.ContainerBuildParameter 3, // 17: wwapi.v1.WWApi.ContainerDelete:input_type -> wwapi.v1.ContainerDeleteParameter - 4, // 18: wwapi.v1.WWApi.ContainerImport:input_type -> wwapi.v1.ContainerImportParameter - 34, // 19: wwapi.v1.WWApi.ContainerList:input_type -> google.protobuf.Empty - 7, // 20: wwapi.v1.WWApi.ContainerShow:input_type -> wwapi.v1.ContainerShowParameter - 19, // 21: wwapi.v1.WWApi.NodeAdd:input_type -> wwapi.v1.NodeAddParameter - 21, // 22: wwapi.v1.WWApi.NodeDelete:input_type -> wwapi.v1.NodeDeleteParameter - 10, // 23: wwapi.v1.WWApi.NodeList:input_type -> wwapi.v1.NodeNames - 22, // 24: wwapi.v1.WWApi.NodeSet:input_type -> wwapi.v1.NodeSetParameter - 10, // 25: wwapi.v1.WWApi.NodeStatus:input_type -> wwapi.v1.NodeNames - 34, // 26: wwapi.v1.WWApi.Version:input_type -> google.protobuf.Empty - 6, // 27: wwapi.v1.WWApi.ContainerBuild:output_type -> wwapi.v1.ContainerListResponse - 34, // 28: wwapi.v1.WWApi.ContainerDelete:output_type -> google.protobuf.Empty - 6, // 29: wwapi.v1.WWApi.ContainerImport:output_type -> wwapi.v1.ContainerListResponse - 6, // 30: wwapi.v1.WWApi.ContainerList:output_type -> wwapi.v1.ContainerListResponse - 8, // 31: wwapi.v1.WWApi.ContainerShow:output_type -> wwapi.v1.ContainerShowResponse - 14, // 32: wwapi.v1.WWApi.NodeAdd:output_type -> wwapi.v1.NodeListResponse - 34, // 33: wwapi.v1.WWApi.NodeDelete:output_type -> google.protobuf.Empty - 14, // 34: wwapi.v1.WWApi.NodeList:output_type -> wwapi.v1.NodeListResponse - 14, // 35: wwapi.v1.WWApi.NodeSet:output_type -> wwapi.v1.NodeListResponse - 25, // 36: wwapi.v1.WWApi.NodeStatus:output_type -> wwapi.v1.NodeStatusResponse - 26, // 37: wwapi.v1.WWApi.Version:output_type -> wwapi.v1.VersionResponse - 27, // [27:38] is the sub-list for method output_type - 16, // [16:27] is the sub-list for method input_type + 4, // 18: wwapi.v1.WWApi.ContainerCopy:input_type -> wwapi.v1.ContainerCopyParameter + 5, // 19: wwapi.v1.WWApi.ContainerImport:input_type -> wwapi.v1.ContainerImportParameter + 35, // 20: wwapi.v1.WWApi.ContainerList:input_type -> google.protobuf.Empty + 8, // 21: wwapi.v1.WWApi.ContainerShow:input_type -> wwapi.v1.ContainerShowParameter + 20, // 22: wwapi.v1.WWApi.NodeAdd:input_type -> wwapi.v1.NodeAddParameter + 22, // 23: wwapi.v1.WWApi.NodeDelete:input_type -> wwapi.v1.NodeDeleteParameter + 11, // 24: wwapi.v1.WWApi.NodeList:input_type -> wwapi.v1.NodeNames + 23, // 25: wwapi.v1.WWApi.NodeSet:input_type -> wwapi.v1.NodeSetParameter + 11, // 26: wwapi.v1.WWApi.NodeStatus:input_type -> wwapi.v1.NodeNames + 35, // 27: wwapi.v1.WWApi.Version:input_type -> google.protobuf.Empty + 7, // 28: wwapi.v1.WWApi.ContainerBuild:output_type -> wwapi.v1.ContainerListResponse + 35, // 29: wwapi.v1.WWApi.ContainerDelete:output_type -> google.protobuf.Empty + 35, // 30: wwapi.v1.WWApi.ContainerCopy:output_type -> google.protobuf.Empty + 7, // 31: wwapi.v1.WWApi.ContainerImport:output_type -> wwapi.v1.ContainerListResponse + 7, // 32: wwapi.v1.WWApi.ContainerList:output_type -> wwapi.v1.ContainerListResponse + 9, // 33: wwapi.v1.WWApi.ContainerShow:output_type -> wwapi.v1.ContainerShowResponse + 15, // 34: wwapi.v1.WWApi.NodeAdd:output_type -> wwapi.v1.NodeListResponse + 35, // 35: wwapi.v1.WWApi.NodeDelete:output_type -> google.protobuf.Empty + 15, // 36: wwapi.v1.WWApi.NodeList:output_type -> wwapi.v1.NodeListResponse + 15, // 37: wwapi.v1.WWApi.NodeSet:output_type -> wwapi.v1.NodeListResponse + 26, // 38: wwapi.v1.WWApi.NodeStatus:output_type -> wwapi.v1.NodeStatusResponse + 27, // 39: wwapi.v1.WWApi.Version:output_type -> wwapi.v1.VersionResponse + 28, // [28:40] is the sub-list for method output_type + 16, // [16:28] is the sub-list for method input_type 16, // [16:16] is the sub-list for extension type_name 16, // [16:16] is the sub-list for extension extendee 0, // [0:16] is the sub-list for field type_name @@ -2259,7 +2322,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ContainerImportParameter); i { + switch v := v.(*ContainerCopyParameter); i { case 0: return &v.state case 1: @@ -2271,7 +2334,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ContainerInfo); i { + switch v := v.(*ContainerImportParameter); i { case 0: return &v.state case 1: @@ -2283,7 +2346,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ContainerListResponse); i { + switch v := v.(*ContainerInfo); i { case 0: return &v.state case 1: @@ -2295,7 +2358,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ContainerShowParameter); i { + switch v := v.(*ContainerListResponse); i { case 0: return &v.state case 1: @@ -2307,7 +2370,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ContainerShowResponse); i { + switch v := v.(*ContainerShowParameter); i { case 0: return &v.state case 1: @@ -2319,7 +2382,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ContainerSyncUserParameter); i { + switch v := v.(*ContainerShowResponse); i { case 0: return &v.state case 1: @@ -2331,7 +2394,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeNames); i { + switch v := v.(*ContainerSyncUserParameter); i { case 0: return &v.state case 1: @@ -2343,7 +2406,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeField); i { + switch v := v.(*NodeNames); i { case 0: return &v.state case 1: @@ -2355,7 +2418,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetDev); i { + switch v := v.(*NodeField); i { case 0: return &v.state case 1: @@ -2367,7 +2430,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeInfo); i { + switch v := v.(*NetDev); i { case 0: return &v.state case 1: @@ -2379,7 +2442,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeListResponse); i { + switch v := v.(*NodeInfo); i { case 0: return &v.state case 1: @@ -2391,7 +2454,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetNodeList); i { + switch v := v.(*NodeListResponse); i { case 0: return &v.state case 1: @@ -2403,7 +2466,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeList); i { + switch v := v.(*GetNodeList); i { case 0: return &v.state case 1: @@ -2415,7 +2478,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetProfileList); i { + switch v := v.(*NodeList); i { case 0: return &v.state case 1: @@ -2427,7 +2490,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProfileList); i { + switch v := v.(*GetProfileList); i { case 0: return &v.state case 1: @@ -2439,7 +2502,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeAddParameter); i { + switch v := v.(*ProfileList); i { case 0: return &v.state case 1: @@ -2451,7 +2514,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeYaml); i { + switch v := v.(*NodeAddParameter); i { case 0: return &v.state case 1: @@ -2463,7 +2526,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeDeleteParameter); i { + switch v := v.(*NodeYaml); i { case 0: return &v.state case 1: @@ -2475,7 +2538,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeSetParameter); i { + switch v := v.(*NodeDeleteParameter); i { case 0: return &v.state case 1: @@ -2487,7 +2550,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProfileSetParameter); i { + switch v := v.(*NodeSetParameter); i { case 0: return &v.state case 1: @@ -2499,7 +2562,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeStatus); i { + switch v := v.(*ProfileSetParameter); i { case 0: return &v.state case 1: @@ -2511,7 +2574,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeStatusResponse); i { + switch v := v.(*NodeStatus); i { case 0: return &v.state case 1: @@ -2523,7 +2586,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VersionResponse); i { + switch v := v.(*NodeStatusResponse); i { case 0: return &v.state case 1: @@ -2535,6 +2598,18 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[26].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 + } + } + file_routes_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CanWriteConfig); i { case 0: return &v.state @@ -2553,7 +2628,7 @@ func file_routes_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_routes_proto_rawDesc, NumEnums: 1, - NumMessages: 33, + NumMessages: 34, NumExtensions: 0, NumServices: 1, }, diff --git a/internal/pkg/api/routes/wwapiv1/routes.pb.gw.go b/internal/pkg/api/routes/wwapiv1/routes.pb.gw.go index 1165a2e3..fb71a9d9 100644 --- a/internal/pkg/api/routes/wwapiv1/routes.pb.gw.go +++ b/internal/pkg/api/routes/wwapiv1/routes.pb.gw.go @@ -13,7 +13,6 @@ import ( "io" "net/http" - "github.com/golang/protobuf/ptypes/empty" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" "google.golang.org/grpc" @@ -22,6 +21,7 @@ import ( "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 @@ -102,6 +102,40 @@ func local_request_WWApi_ContainerDelete_0(ctx context.Context, marshaler runtim } +func request_WWApi_ContainerCopy_0(ctx context.Context, marshaler runtime.Marshaler, client WWApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ContainerCopyParameter + 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.ContainerCopy(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_WWApi_ContainerCopy_0(ctx context.Context, marshaler runtime.Marshaler, server WWApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ContainerCopyParameter + 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.ContainerCopy(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 @@ -137,7 +171,7 @@ func local_request_WWApi_ContainerImport_0(ctx context.Context, marshaler runtim } 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 empty.Empty + var protoReq emptypb.Empty var metadata runtime.ServerMetadata msg, err := client.ContainerList(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -146,7 +180,7 @@ func request_WWApi_ContainerList_0(ctx context.Context, marshaler runtime.Marsha } 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 empty.Empty + var protoReq emptypb.Empty var metadata runtime.ServerMetadata msg, err := server.ContainerList(ctx, &protoReq) @@ -367,7 +401,7 @@ func local_request_WWApi_NodeStatus_0(ctx context.Context, marshaler runtime.Mar } 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 empty.Empty + var protoReq emptypb.Empty var metadata runtime.ServerMetadata msg, err := client.Version(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -376,7 +410,7 @@ func request_WWApi_Version_0(ctx context.Context, marshaler runtime.Marshaler, c } 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 empty.Empty + var protoReq emptypb.Empty var metadata runtime.ServerMetadata msg, err := server.Version(ctx, &protoReq) @@ -440,6 +474,31 @@ func RegisterWWApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("POST", pattern_WWApi_ContainerCopy_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerCopy", runtime.WithHTTPPathPattern("/v1/containercopy")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_WWApi_ContainerCopy_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_ContainerCopy_0(annotatedContext, 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() @@ -750,6 +809,28 @@ func RegisterWWApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("POST", pattern_WWApi_ContainerCopy_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/wwapi.v1.WWApi/ContainerCopy", runtime.WithHTTPPathPattern("/v1/containercopy")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_WWApi_ContainerCopy_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_WWApi_ContainerCopy_0(annotatedContext, 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() @@ -956,6 +1037,8 @@ var ( pattern_WWApi_ContainerDelete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "container"}, "")) + pattern_WWApi_ContainerCopy_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "containercopy"}, "")) + 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"}, "")) @@ -980,6 +1063,8 @@ var ( forward_WWApi_ContainerDelete_0 = runtime.ForwardResponseMessage + forward_WWApi_ContainerCopy_0 = runtime.ForwardResponseMessage + forward_WWApi_ContainerImport_0 = runtime.ForwardResponseMessage forward_WWApi_ContainerList_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 index 65a3bb33..f361197a 100644 --- a/internal/pkg/api/routes/wwapiv1/routes_grpc.pb.go +++ b/internal/pkg/api/routes/wwapiv1/routes_grpc.pb.go @@ -1,20 +1,17 @@ -// Routes for the wwapi (WareWulf API). -// TODO: Try protoc-gen-doc for generating documentation. - // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 -// - protoc v3.21.1 +// - protoc-gen-go-grpc v1.2.0 +// - protoc v4.24.0 // source: routes.proto package wwapiv1 import ( context "context" - empty "github.com/golang/protobuf/ptypes/empty" 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 @@ -22,20 +19,6 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 -const ( - WWApi_ContainerBuild_FullMethodName = "/wwapi.v1.WWApi/ContainerBuild" - WWApi_ContainerDelete_FullMethodName = "/wwapi.v1.WWApi/ContainerDelete" - WWApi_ContainerImport_FullMethodName = "/wwapi.v1.WWApi/ContainerImport" - WWApi_ContainerList_FullMethodName = "/wwapi.v1.WWApi/ContainerList" - WWApi_ContainerShow_FullMethodName = "/wwapi.v1.WWApi/ContainerShow" - WWApi_NodeAdd_FullMethodName = "/wwapi.v1.WWApi/NodeAdd" - WWApi_NodeDelete_FullMethodName = "/wwapi.v1.WWApi/NodeDelete" - WWApi_NodeList_FullMethodName = "/wwapi.v1.WWApi/NodeList" - WWApi_NodeSet_FullMethodName = "/wwapi.v1.WWApi/NodeSet" - WWApi_NodeStatus_FullMethodName = "/wwapi.v1.WWApi/NodeStatus" - WWApi_Version_FullMethodName = "/wwapi.v1.WWApi/Version" -) - // 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. @@ -43,18 +26,19 @@ 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) (*empty.Empty, error) + ContainerDelete(ctx context.Context, in *ContainerDeleteParameter, opts ...grpc.CallOption) (*emptypb.Empty, error) + ContainerCopy(ctx context.Context, in *ContainerCopyParameter, 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 *empty.Empty, opts ...grpc.CallOption) (*ContainerListResponse, error) + 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) (*empty.Empty, error) + 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. @@ -64,7 +48,7 @@ type WWApiClient interface { 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 *empty.Empty, opts ...grpc.CallOption) (*VersionResponse, error) + Version(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*VersionResponse, error) } type wWApiClient struct { @@ -77,16 +61,25 @@ func NewWWApiClient(cc grpc.ClientConnInterface) WWApiClient { func (c *wWApiClient) ContainerBuild(ctx context.Context, in *ContainerBuildParameter, opts ...grpc.CallOption) (*ContainerListResponse, error) { out := new(ContainerListResponse) - err := c.cc.Invoke(ctx, WWApi_ContainerBuild_FullMethodName, in, out, opts...) + 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) (*empty.Empty, error) { - out := new(empty.Empty) - err := c.cc.Invoke(ctx, WWApi_ContainerDelete_FullMethodName, in, out, opts...) +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) ContainerCopy(ctx context.Context, in *ContainerCopyParameter, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/ContainerCopy", in, out, opts...) if err != nil { return nil, err } @@ -95,16 +88,16 @@ func (c *wWApiClient) ContainerDelete(ctx context.Context, in *ContainerDeletePa func (c *wWApiClient) ContainerImport(ctx context.Context, in *ContainerImportParameter, opts ...grpc.CallOption) (*ContainerListResponse, error) { out := new(ContainerListResponse) - err := c.cc.Invoke(ctx, WWApi_ContainerImport_FullMethodName, in, out, opts...) + 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 *empty.Empty, opts ...grpc.CallOption) (*ContainerListResponse, error) { +func (c *wWApiClient) ContainerList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ContainerListResponse, error) { out := new(ContainerListResponse) - err := c.cc.Invoke(ctx, WWApi_ContainerList_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/ContainerList", in, out, opts...) if err != nil { return nil, err } @@ -113,7 +106,7 @@ func (c *wWApiClient) ContainerList(ctx context.Context, in *empty.Empty, opts . func (c *wWApiClient) ContainerShow(ctx context.Context, in *ContainerShowParameter, opts ...grpc.CallOption) (*ContainerShowResponse, error) { out := new(ContainerShowResponse) - err := c.cc.Invoke(ctx, WWApi_ContainerShow_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/ContainerShow", in, out, opts...) if err != nil { return nil, err } @@ -122,16 +115,16 @@ func (c *wWApiClient) ContainerShow(ctx context.Context, in *ContainerShowParame func (c *wWApiClient) NodeAdd(ctx context.Context, in *NodeAddParameter, opts ...grpc.CallOption) (*NodeListResponse, error) { out := new(NodeListResponse) - err := c.cc.Invoke(ctx, WWApi_NodeAdd_FullMethodName, in, out, opts...) + 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) (*empty.Empty, error) { - out := new(empty.Empty) - err := c.cc.Invoke(ctx, WWApi_NodeDelete_FullMethodName, in, out, opts...) +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 } @@ -140,7 +133,7 @@ func (c *wWApiClient) NodeDelete(ctx context.Context, in *NodeDeleteParameter, o func (c *wWApiClient) NodeList(ctx context.Context, in *NodeNames, opts ...grpc.CallOption) (*NodeListResponse, error) { out := new(NodeListResponse) - err := c.cc.Invoke(ctx, WWApi_NodeList_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/NodeList", in, out, opts...) if err != nil { return nil, err } @@ -149,7 +142,7 @@ func (c *wWApiClient) NodeList(ctx context.Context, in *NodeNames, opts ...grpc. func (c *wWApiClient) NodeSet(ctx context.Context, in *NodeSetParameter, opts ...grpc.CallOption) (*NodeListResponse, error) { out := new(NodeListResponse) - err := c.cc.Invoke(ctx, WWApi_NodeSet_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/NodeSet", in, out, opts...) if err != nil { return nil, err } @@ -158,16 +151,16 @@ func (c *wWApiClient) NodeSet(ctx context.Context, in *NodeSetParameter, opts .. func (c *wWApiClient) NodeStatus(ctx context.Context, in *NodeNames, opts ...grpc.CallOption) (*NodeStatusResponse, error) { out := new(NodeStatusResponse) - err := c.cc.Invoke(ctx, WWApi_NodeStatus_FullMethodName, in, out, opts...) + 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 *empty.Empty, opts ...grpc.CallOption) (*VersionResponse, error) { +func (c *wWApiClient) Version(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*VersionResponse, error) { out := new(VersionResponse) - err := c.cc.Invoke(ctx, WWApi_Version_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/wwapi.v1.WWApi/Version", in, out, opts...) if err != nil { return nil, err } @@ -181,18 +174,19 @@ 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) (*empty.Empty, error) + ContainerDelete(context.Context, *ContainerDeleteParameter) (*emptypb.Empty, error) + ContainerCopy(context.Context, *ContainerCopyParameter) (*emptypb.Empty, error) // ContainerImport imports a container to Warewulf. ContainerImport(context.Context, *ContainerImportParameter) (*ContainerListResponse, error) // ContainerList lists ContainerInfo for each container. - ContainerList(context.Context, *empty.Empty) (*ContainerListResponse, error) + 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) (*empty.Empty, error) + 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. @@ -202,7 +196,7 @@ type WWApiServer interface { 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, *empty.Empty) (*VersionResponse, error) + Version(context.Context, *emptypb.Empty) (*VersionResponse, error) mustEmbedUnimplementedWWApiServer() } @@ -213,13 +207,16 @@ 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) (*empty.Empty, error) { +func (UnimplementedWWApiServer) ContainerDelete(context.Context, *ContainerDeleteParameter) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method ContainerDelete not implemented") } +func (UnimplementedWWApiServer) ContainerCopy(context.Context, *ContainerCopyParameter) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method ContainerCopy 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, *empty.Empty) (*ContainerListResponse, error) { +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) { @@ -228,7 +225,7 @@ func (UnimplementedWWApiServer) ContainerShow(context.Context, *ContainerShowPar func (UnimplementedWWApiServer) NodeAdd(context.Context, *NodeAddParameter) (*NodeListResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method NodeAdd not implemented") } -func (UnimplementedWWApiServer) NodeDelete(context.Context, *NodeDeleteParameter) (*empty.Empty, error) { +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) { @@ -240,7 +237,7 @@ func (UnimplementedWWApiServer) NodeSet(context.Context, *NodeSetParameter) (*No func (UnimplementedWWApiServer) NodeStatus(context.Context, *NodeNames) (*NodeStatusResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method NodeStatus not implemented") } -func (UnimplementedWWApiServer) Version(context.Context, *empty.Empty) (*VersionResponse, error) { +func (UnimplementedWWApiServer) Version(context.Context, *emptypb.Empty) (*VersionResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Version not implemented") } func (UnimplementedWWApiServer) mustEmbedUnimplementedWWApiServer() {} @@ -266,7 +263,7 @@ func _WWApi_ContainerBuild_Handler(srv interface{}, ctx context.Context, dec fun } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: WWApi_ContainerBuild_FullMethodName, + FullMethod: "/wwapi.v1.WWApi/ContainerBuild", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).ContainerBuild(ctx, req.(*ContainerBuildParameter)) @@ -284,7 +281,7 @@ func _WWApi_ContainerDelete_Handler(srv interface{}, ctx context.Context, dec fu } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: WWApi_ContainerDelete_FullMethodName, + FullMethod: "/wwapi.v1.WWApi/ContainerDelete", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).ContainerDelete(ctx, req.(*ContainerDeleteParameter)) @@ -292,6 +289,24 @@ func _WWApi_ContainerDelete_Handler(srv interface{}, ctx context.Context, dec fu return interceptor(ctx, in, info, handler) } +func _WWApi_ContainerCopy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ContainerCopyParameter) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WWApiServer).ContainerCopy(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/wwapi.v1.WWApi/ContainerCopy", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WWApiServer).ContainerCopy(ctx, req.(*ContainerCopyParameter)) + } + 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 { @@ -302,7 +317,7 @@ func _WWApi_ContainerImport_Handler(srv interface{}, ctx context.Context, dec fu } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: WWApi_ContainerImport_FullMethodName, + FullMethod: "/wwapi.v1.WWApi/ContainerImport", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).ContainerImport(ctx, req.(*ContainerImportParameter)) @@ -311,7 +326,7 @@ func _WWApi_ContainerImport_Handler(srv interface{}, ctx context.Context, dec fu } func _WWApi_ContainerList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(empty.Empty) + in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } @@ -320,10 +335,10 @@ func _WWApi_ContainerList_Handler(srv interface{}, ctx context.Context, dec func } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: WWApi_ContainerList_FullMethodName, + FullMethod: "/wwapi.v1.WWApi/ContainerList", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(WWApiServer).ContainerList(ctx, req.(*empty.Empty)) + return srv.(WWApiServer).ContainerList(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } @@ -338,7 +353,7 @@ func _WWApi_ContainerShow_Handler(srv interface{}, ctx context.Context, dec func } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: WWApi_ContainerShow_FullMethodName, + FullMethod: "/wwapi.v1.WWApi/ContainerShow", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).ContainerShow(ctx, req.(*ContainerShowParameter)) @@ -356,7 +371,7 @@ func _WWApi_NodeAdd_Handler(srv interface{}, ctx context.Context, dec func(inter } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: WWApi_NodeAdd_FullMethodName, + FullMethod: "/wwapi.v1.WWApi/NodeAdd", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).NodeAdd(ctx, req.(*NodeAddParameter)) @@ -374,7 +389,7 @@ func _WWApi_NodeDelete_Handler(srv interface{}, ctx context.Context, dec func(in } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: WWApi_NodeDelete_FullMethodName, + FullMethod: "/wwapi.v1.WWApi/NodeDelete", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).NodeDelete(ctx, req.(*NodeDeleteParameter)) @@ -392,7 +407,7 @@ func _WWApi_NodeList_Handler(srv interface{}, ctx context.Context, dec func(inte } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: WWApi_NodeList_FullMethodName, + FullMethod: "/wwapi.v1.WWApi/NodeList", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).NodeList(ctx, req.(*NodeNames)) @@ -410,7 +425,7 @@ func _WWApi_NodeSet_Handler(srv interface{}, ctx context.Context, dec func(inter } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: WWApi_NodeSet_FullMethodName, + FullMethod: "/wwapi.v1.WWApi/NodeSet", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).NodeSet(ctx, req.(*NodeSetParameter)) @@ -428,7 +443,7 @@ func _WWApi_NodeStatus_Handler(srv interface{}, ctx context.Context, dec func(in } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: WWApi_NodeStatus_FullMethodName, + FullMethod: "/wwapi.v1.WWApi/NodeStatus", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WWApiServer).NodeStatus(ctx, req.(*NodeNames)) @@ -437,7 +452,7 @@ func _WWApi_NodeStatus_Handler(srv interface{}, ctx context.Context, dec func(in } func _WWApi_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(empty.Empty) + in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } @@ -446,10 +461,10 @@ func _WWApi_Version_Handler(srv interface{}, ctx context.Context, dec func(inter } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: WWApi_Version_FullMethodName, + FullMethod: "/wwapi.v1.WWApi/Version", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(WWApiServer).Version(ctx, req.(*empty.Empty)) + return srv.(WWApiServer).Version(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } @@ -469,6 +484,10 @@ var WWApi_ServiceDesc = grpc.ServiceDesc{ MethodName: "ContainerDelete", Handler: _WWApi_ContainerDelete_Handler, }, + { + MethodName: "ContainerCopy", + Handler: _WWApi_ContainerCopy_Handler, + }, { MethodName: "ContainerImport", Handler: _WWApi_ContainerImport_Handler, diff --git a/userdocs/contents/containers.rst b/userdocs/contents/containers.rst index 412378af..73c3f890 100644 --- a/userdocs/contents/containers.rst +++ b/userdocs/contents/containers.rst @@ -349,3 +349,13 @@ issues in most circumstances: If you are still getting "Not enough memory" or "No space left on device" errors, try disabling any "memory hole" features or updating your system BIOS or firmware. + +Duplicating a container +============================ +It is possible to duplicate an installed image by using : + +.. code-block:: console + + # wwctl container copy CONTAINER_NAME DUPLICATED_CONTAINER_NAME + +This kind of duplication can be useful if you are looking for canary tests. \ No newline at end of file diff --git a/userdocs/contents/provisioning.rst b/userdocs/contents/provisioning.rst index 5a451669..c1ba2c14 100644 --- a/userdocs/contents/provisioning.rst +++ b/userdocs/contents/provisioning.rst @@ -76,6 +76,7 @@ Node status ======================== During the whole provisioning process of your nodes, you can check their status through the following command : + .. code-block:: console # wwctl node status @@ -85,6 +86,7 @@ through the following command : For each node, there is 4 different stages : + * **IPXE** * **KERNEL** * **SYSTEM_OVERLAY** From aeb7793fba53452ad99af93b2d7def01a5e8901f Mon Sep 17 00:00:00 2001 From: Arnaud LECOMTE Date: Tue, 5 Sep 2023 14:37:29 +0200 Subject: [PATCH 73/75] Switching cp to copy --- API.md | 2 +- CHANGELOG.md | 2 +- internal/app/wwctl/container/copy/main.go | 2 +- internal/app/wwctl/container/copy/root.go | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/API.md b/API.md index 9a8e8b0d..2b94cf88 100644 --- a/API.md +++ b/API.md @@ -19,7 +19,7 @@ wwctl container delete wwctl container import wwctl container list wwctl container show -wwctl container cp +wwctl container copy Some notes on the files: diff --git a/CHANGELOG.md b/CHANGELOG.md index d5e3beda..b7c0f6e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New documentation for the hostlist syntax. #611 - New documentation for development environment (Vagrant) -- Ability to duplicate an image with `wwctl container cp` or the API +- Ability to duplicate an image with `wwctl container copy` or the API - New documentation for container duplication procedure ### Fixed diff --git a/internal/app/wwctl/container/copy/main.go b/internal/app/wwctl/container/copy/main.go index f3499f78..5ecc6e3a 100644 --- a/internal/app/wwctl/container/copy/main.go +++ b/internal/app/wwctl/container/copy/main.go @@ -12,7 +12,7 @@ import ( func CobraRunE(cmd *cobra.Command, args []string) (err error) { if len(args) > 2 { - wwlog.Warn("cp only requires 2 arguments but you provided %d arguments. Hence, they will be ignored.", len(args)) + wwlog.Warn("copy only requires 2 arguments but you provided %d arguments. Hence, they will be ignored.", len(args)) } cdp := &wwapiv1.ContainerCopyParameter{ diff --git a/internal/app/wwctl/container/copy/root.go b/internal/app/wwctl/container/copy/root.go index 5490adce..fad0dbaa 100644 --- a/internal/app/wwctl/container/copy/root.go +++ b/internal/app/wwctl/container/copy/root.go @@ -8,7 +8,8 @@ import ( var ( baseCmd = &cobra.Command{ DisableFlagsInUseLine: true, - Use: "cp CONTAINER NEW_NAME", + Use: "copy CONTAINER NEW_NAME", + Aliases: []string{"cp"}, Short: "Copy an existing container", Long: "This command will duplicate an imported container image.", RunE: CobraRunE, From 0364f1c9ca649214563e41dfa15f1cc5c9108bda Mon Sep 17 00:00:00 2001 From: Matt Jolly Date: Thu, 7 Sep 2023 18:52:34 +1000 Subject: [PATCH 74/75] add error handling to dynamic network configuration --- CHANGELOG.md | 1 + CONTRIBUTORS.md | 1 + internal/pkg/config/root.go | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc0e55f1..db644f2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add EL9 Quickstart guide to index.rst - Container file gids are now updated properly during syncuser. #840 - Fix build for API. +- Add error handling to `wwctl`'s dynamic IP configuration attempt. ### Changed diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 9b394cae..45127882 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -27,3 +27,4 @@ * Jeffrey Frey @jtfrey * Xu Yang(Jason Yang) @JasonYangShadow * Arnaud LECOMTE +* Matt Jolly diff --git a/internal/pkg/config/root.go b/internal/pkg/config/root.go index 67c4447a..3e641e6a 100644 --- a/internal/pkg/config/root.go +++ b/internal/pkg/config/root.go @@ -119,7 +119,10 @@ func (conf *RootConf) SetDynamicDefaults() (err error) { if conf.Ipaddr == "" { wwlog.Verbose("Configuration has no valid network, going to dynamic values") - conn, _ := net.Dial("udp", "8.8.8.8:80") + conn, err := net.Dial("udp", "8.8.8.8:80") + if err != nil { + return errors.Wrap(err, "Couldn't establish a connection to remote") + } defer conn.Close() ipaddr = conn.LocalAddr().(*net.UDPAddr).IP mask = ipaddr.DefaultMask() From bb0c37763b9a754e645515edf8725439d3f5d04a Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Thu, 7 Sep 2023 12:09:23 +0200 Subject: [PATCH 75/75] Revert "add error handling to dynamic network configuration" --- CHANGELOG.md | 1 - CONTRIBUTORS.md | 1 - internal/pkg/config/root.go | 5 +---- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db644f2b..bc0e55f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add EL9 Quickstart guide to index.rst - Container file gids are now updated properly during syncuser. #840 - Fix build for API. -- Add error handling to `wwctl`'s dynamic IP configuration attempt. ### Changed diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 45127882..9b394cae 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -27,4 +27,3 @@ * Jeffrey Frey @jtfrey * Xu Yang(Jason Yang) @JasonYangShadow * Arnaud LECOMTE -* Matt Jolly diff --git a/internal/pkg/config/root.go b/internal/pkg/config/root.go index 3e641e6a..67c4447a 100644 --- a/internal/pkg/config/root.go +++ b/internal/pkg/config/root.go @@ -119,10 +119,7 @@ func (conf *RootConf) SetDynamicDefaults() (err error) { if conf.Ipaddr == "" { wwlog.Verbose("Configuration has no valid network, going to dynamic values") - conn, err := net.Dial("udp", "8.8.8.8:80") - if err != nil { - return errors.Wrap(err, "Couldn't establish a connection to remote") - } + conn, _ := net.Dial("udp", "8.8.8.8:80") defer conn.Close() ipaddr = conn.LocalAddr().(*net.UDPAddr).IP mask = ipaddr.DefaultMask()