From 695dc99c1c821a42c3a7ba0c6ab28742ce87aaac Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Wed, 30 Jan 2019 15:59:33 -0500 Subject: [PATCH] Add pivot.service as a host API Add a new systemd service that simply executes `pivot`. But note that we don't have a `pivot.path` unit to auto-trigger it: this needs to be triggered explicitly by whatever drives pivot. Making it a service and standizing on a path in `/etc` means that we can trigger a pivot on boot from Ignition (patch to preset enable the service is pending), as well as from the MCD by directly starting the unit. Related: https://github.com/openshift/machine-config-operator/issues/314 --- .gitignore | 2 ++ Makefile | 8 ++++++- README.md | 13 +++++++++++ cmd/root.go | 49 ++++++++++++++++++++++++++++------------ pivot.spec | 11 +++++---- systemd/pivot.service.in | 12 ++++++++++ utils/fs.go | 18 +++++++++++++++ 7 files changed, 94 insertions(+), 19 deletions(-) create mode 100644 systemd/pivot.service.in create mode 100644 utils/fs.go diff --git a/.gitignore b/.gitignore index 4c556fb..87dcbd6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ pivot *.out # editor .vscode + +systemd/pivot.service diff --git a/Makefile b/Makefile index b5f1cbb..1460788 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ LDFLAGS := -X main.version=${VERSION} -X main.commitHash=${COMMIT_HASH} -X main. PREFIX ?= /usr CONFIG_DIR ?= /etc BIN_DIR ?= ${PREFIX}/bin +SYSTEMD_UNIT_DIR ?= ${PREFIX}/lib/systemd/system .PHONY: help build clean deps install lint static test @@ -32,11 +33,14 @@ help: changelog: git log --format="- %s" `git tag | tail -n 1`..HEAD +systemd/pivot.service: systemd/pivot.service.in + sed "s,@@PIVOT_BINARY_PATH@@,${BIN_DIR}/pivot,g" < systemd/pivot.service.in > systemd/pivot.service + pivot: Gopkg.* *.go cmd/*.go utils/*.go types/*.go go build -ldflags '${LDFLAGS}' -o pivot main.go strip pivot -build: pivot +build: pivot systemd/pivot.service static: clean CGO_ENABLED=0 go build -ldflags '${LDFLAGS} -w -extldflags "-static"' -a -o pivot main.go @@ -51,6 +55,8 @@ deps: install: build install -d ${DESTDIR}${BIN_DIR} install --mode 755 pivot ${DESTDIR}${BIN_DIR}/pivot + install -d ${DESTDIR}${SYSTEMD_UNIT_DIR} + install --mode 664 systemd/pivot.service ${DESTDIR}${SYSTEMD_UNIT_DIR} lint: go get -u github.com/golang/lint/golint diff --git a/README.md b/README.md index 93c6a76..a99adfe 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,19 @@ Though normally, one wants to use digests rather than tags, e.g.: pivot -r $REGISTRY/os@sha256:fdf70521df4ed1dc135d81fd3c4608574aeca45dc22d1b4e38d16630e9d6f1a7 ``` +It also comes with a systemd unit to provide a "host API". For example: + +``` +mkdir -p /etc/pivot +echo $REGISTRY/os:latest > /etc/pivot/image-pullspec +touch /run/pivot-reboot-needed +systemctl start pivot +``` + +This will start `pivot`, which will read the file and execute the pivot. +If the pivot is completed, the file will be deleted. The expected way to +make use of this is to create the necessary files from Ignition. + See --- - [openshift/os](https://github.com/openshift/os/) diff --git a/cmd/root.go b/cmd/root.go index e03ed58..eb45431 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,9 +2,9 @@ package cmd import ( "encoding/json" - "errors" "flag" "fmt" + "io/ioutil" "os" "strings" @@ -21,22 +21,20 @@ var reboot bool var container string var exit_77 bool -// the number of times to retry commands that pull data from the network -const numRetriesNetCommands = 5 +const ( + // the number of times to retry commands that pull data from the network + numRetriesNetCommands = 5 + etcPivotFile = "/etc/pivot/image-pullspec" + runPivotRebootFile = "/run/pivot/reboot-needed" +) // RootCmd houses the cobra config for the main command var RootCmd = &cobra.Command{ - Use: "pivot [FLAGS] ", + Use: "pivot [FLAGS] [IMAGE_PULLSPEC]", DisableFlagsInUseLine: true, Short: "Allows moving from one OSTree deployment to another", - // Long: ``, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("An image name must be provided") - } - return nil - }, - Run: Execute, + Args: cobra.MaximumNArgs(1), + Run: Execute, } // init executes upon import @@ -150,9 +148,32 @@ func pullAndRebase(container string) (imgid string, changed bool) { // Execute runs the command func Execute(cmd *cobra.Command, args []string) { - container := args[0] + var fromFile bool + var container string + if len(args) > 0 { + container = args[0] + fromFile = false + } else { + glog.Infof("Using image pullspec from %s", etcPivotFile) + data, err := ioutil.ReadFile(etcPivotFile) + if err != nil { + glog.Fatalf("Failed to read from %s: %v", etcPivotFile, err) + } + container = strings.TrimSpace(string(data)) + fromFile = true + } + imgid, changed := pullAndRebase(container) + // Delete the file now that we successfully rebased + if fromFile { + if err := os.Remove(etcPivotFile); err != nil { + if !os.IsNotExist(err) { + glog.Fatal("Failed to delete %s: %v", etcPivotFile, err) + } + } + } + // By default, delete the image. if !keep { // Related: https://github.com/containers/libpod/issues/2234 @@ -164,7 +185,7 @@ func Execute(cmd *cobra.Command, args []string) { if exit_77 { os.Exit(77) } - } else if reboot { + } else if reboot || utils.FileExists(runPivotRebootFile) { // Reboot the machine if asked to do so utils.Run("systemctl", "reboot") } diff --git a/pivot.spec b/pivot.spec index d7f96ad..a87f84a 100644 --- a/pivot.spec +++ b/pivot.spec @@ -1,8 +1,8 @@ %define debug_package %{nil} Name: pivot -Version: 0.0.2 -Release: 0.1%{?dist} +Version: 0.0.3 +Release: 1%{?dist} Summary: allows moving from one OSTree deployment to another License: ASL 2.0 @@ -21,7 +21,7 @@ deployment to another with minimal effort. %prep %autosetup -n %{name}-%{version} mkdir -p src/github.com/openshift/%{name}/ -cp -rf cmd Gopkg.lock Gopkg.toml LICENSE main.go Makefile pivot.spec README.md types utils vendor VERSION src/github.com/openshift/%{name} +cp -rf cmd Gopkg.lock Gopkg.toml LICENSE main.go Makefile pivot.spec README.md types utils vendor VERSION systemd src/github.com/openshift/%{name} %build export GOPATH=`pwd` @@ -36,9 +36,12 @@ make install DESTDIR=%{buildroot} %license LICENSE %doc README.md %{_bindir}/%{name} - +%{_prefix}/lib/systemd/system/pivot.* %changelog +* Tue Feb 05 2019 Jonathan Lebon - 0.0.3-1 +- Add systemd service unit + * Wed Nov 14 2018 Steve Milner - 0.0.2-0.1 - Makefile: Add changelog target - cmd/root: Print pivoting msg before pull diff --git a/systemd/pivot.service.in b/systemd/pivot.service.in new file mode 100644 index 0000000..fbc1409 --- /dev/null +++ b/systemd/pivot.service.in @@ -0,0 +1,12 @@ +[Unit] +Description=Pivot Tool +ConditionPathExists=/etc/pivot/image-pullspec +After=ignition-firstboot-complete.service +Before=kubelet.service + +[Service] +Type=simple +ExecStart=@@PIVOT_BINARY_PATH@@ + +[Install] +WantedBy=multi-user.target diff --git a/utils/fs.go b/utils/fs.go new file mode 100644 index 0000000..2ced330 --- /dev/null +++ b/utils/fs.go @@ -0,0 +1,18 @@ +package utils + +import ( + "os" + + "github.com/golang/glog" +) + +// FileExists checks if the file exists, gracefully handling ENOENT. +func FileExists(path string) bool { + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + return false + } + glog.Fatalf("Failed to stat %s: %v", path, err) + } + return true +}