From 6ac0537f07634d1781a988a642f8270c0393f650 Mon Sep 17 00:00:00 2001 From: Ashlesha Atrey Date: Tue, 10 Sep 2019 09:48:35 -0700 Subject: [PATCH] Implemented mixer reset command It reverts the mix to a good state in case of a build failure or in case the user wants to roll back to a previous version. By default, the command will bring back the mix to the same state it have when it last build a successful mix. This value can be overridden if a `--to` flag is provided with a version number. The command will not be destructive unless a `--clean` flag is provided specified. If so, mixer will delete all files associated with versions that are bigger than the one provided. If not, only the values of the state files will change, but the files will be kept. Signed-off-by: Ashlesha Atrey --- builder/builder.go | 64 ++++++++------ mixer/cmd/reset.go | 202 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+), 27 deletions(-) create mode 100644 mixer/cmd/reset.go diff --git a/builder/builder.go b/builder/builder.go index b7ec5ba74..02fd16e6d 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -369,34 +369,9 @@ func (b *Builder) BuildUpdate(params UpdateParameters) error { return nil } - fmt.Printf("Setting latest version to %s\n", b.MixVer) - latestVerFilePath := filepath.Join(b.Config.Builder.ServerStateDir, "www", "version", "latest_version") - err = ioutil.WriteFile(latestVerFilePath, []byte(b.MixVer), 0644) - if err != nil { - return errors.Wrapf(err, "couldn't update the latest_version file") - } - - // sign the latest_version file - if !params.SkipSigning { - fmt.Println("Signing latest_version file.") - err = b.signFile(latestVerFilePath) - if err != nil { - return errors.Wrapf(err, "couldn't sign the latest_version file") - } - } - err = ioutil.WriteFile(filepath.Join(formatDir, "latest"), []byte(b.MixVer), 0644) + err = b.UpdateLatest(params.SkipSigning) if err != nil { - return errors.Wrapf(err, "couldn't update the latest version") - } - - // sign the latest file in place based on the Mixed format - // read from builder.conf. - if !params.SkipSigning { - fmt.Println("Signing latest file.") - err = b.signFile(filepath.Join(formatDir, "latest")) - if err != nil { - return errors.Wrapf(err, "couldn't sign the latest file") - } + return err } err = ioutil.WriteFile(filepath.Join(b.Config.Builder.ServerStateDir, "image", "LAST_VER"), []byte(b.MixVer), 0644) @@ -762,3 +737,38 @@ func (b *Builder) BuildDeltaManifestsPreviousVersions(prev, to uint32) error { return nil } + +// UpdateLatest is use to update and sign the latest and latest_version file +func (b *Builder) UpdateLatest(skipSigning bool) error { + formatDir := filepath.Join(b.Config.Builder.ServerStateDir, "www", "version", "format"+b.State.Mix.Format) + fmt.Printf("Setting latest version to %s\n", b.MixVer) + latestVerFilePath := filepath.Join(b.Config.Builder.ServerStateDir, "www", "version", "latest_version") + err := ioutil.WriteFile(latestVerFilePath, []byte(b.MixVer), 0644) + if err != nil { + return errors.Wrapf(err, "couldn't update the latest_version file") + } + + // sign the latest_version file + if !skipSigning { + fmt.Println("Signing latest_version file.") + err = b.signFile(latestVerFilePath) + if err != nil { + return errors.Wrapf(err, "couldn't sign the latest_version file") + } + } + err = ioutil.WriteFile(filepath.Join(formatDir, "latest"), []byte(b.MixVer), 0644) + if err != nil { + return errors.Wrapf(err, "couldn't update the latest version") + } + + // sign the latest file in place based on the Mixed format + // read from builder.conf. + if !skipSigning { + fmt.Println("Signing latest file.") + err = b.signFile(filepath.Join(formatDir, "latest")) + if err != nil { + return errors.Wrapf(err, "couldn't sign the latest file") + } + } + return nil +} diff --git a/mixer/cmd/reset.go b/mixer/cmd/reset.go new file mode 100644 index 000000000..9e47bc3a9 --- /dev/null +++ b/mixer/cmd/reset.go @@ -0,0 +1,202 @@ +// Copyright © 2018 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "io/ioutil" + "log" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/clearlinux/mixer-tools/builder" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var resetCmd = &cobra.Command{ + Use: "reset", + Short: "Revert the mix to a good state", + Long: `Revert the mix to a good state in case of a build + failure or in case the user wants to roll back to a previous + version`, + Run: runReset, +} + +type resetFlags struct { + toVersion int32 + clean bool +} + +var resetCmdFlags resetFlags + +func init() { + RootCmd.AddCommand(resetCmd) + + resetCmd.Flags().Int32Var(&resetCmdFlags.toVersion, "to", -1, "Reset to a specific mix version, default = PREVIOUS_MIX_VERSION") + resetCmd.Flags().BoolVar(&resetCmdFlags.clean, "clean", false, "Deletes all files with versions bigger than the one provided") +} + +func runReset(cmd *cobra.Command, args []string) { + b, err := builder.NewFromConfig(configFile) + if err != nil { + fail(err) + } + + // assuming mixer.state file has the correct info + stateMixPreviousMixVersionUint32, err := parseUint32(b.State.Mix.PreviousMixVer) + if err != nil { + fail(err) + } + + // if toVersion provided by the user, replace the default previous version + if resetCmdFlags.toVersion >= 0 && uint32(resetCmdFlags.toVersion) < stateMixPreviousMixVersionUint32 { + b.State.Mix.PreviousMixVer = strconv.Itoa(int(resetCmdFlags.toVersion)) + stateMixPreviousMixVersionUint32 = uint32(resetCmdFlags.toVersion) + } + + // Make sure FORMAT in mixer.state has the same value as update/www//format + var format []byte + filename := filepath.Join(b.Config.Builder.ServerStateDir, "www", b.State.Mix.PreviousMixVer, "format") + if format, err = ioutil.ReadFile(filename); err != nil { + fail(err) + } else { + if strings.TrimSpace(string(format)) != b.State.Mix.Format { + b.State.Mix.Format = strings.TrimSpace(string(format)) + } + } + + stateMixPreviousMixFormatUint32, err := parseUint32(b.State.Mix.Format) + if err != nil { + fail(err) + } + // Change upstreamVersion file content to the previous version + var lastStableMixUpstreamVersion []byte + filename = filepath.Join(b.Config.Builder.ServerStateDir, "www", b.State.Mix.PreviousMixVer, "upstreamver") + if lastStableMixUpstreamVersion, err = ioutil.ReadFile(filename); err != nil { + fail(err) + } else { + if strings.TrimSpace(string(lastStableMixUpstreamVersion)) != b.UpstreamVer { + // Set the upstream version to the previous format's latest version + b.UpstreamVer = strings.TrimSpace(string(lastStableMixUpstreamVersion)) + b.UpstreamVerUint32, err = parseUint32(b.UpstreamVer) + if err != nil { + fail(errors.Wrapf(err, "Couldn't parse upstream version")) + } + vFile := filepath.Join(b.Config.Builder.VersionPath, b.UpstreamVerFile) + if err = ioutil.WriteFile(vFile, []byte(b.UpstreamVer), 0644); err != nil { + fail(err) + } + } + } + + // Change mixVersion to point to the last good build + if err := ioutil.WriteFile(filepath.Join(b.Config.Builder.VersionPath, b.MixVerFile), []byte(b.State.Mix.PreviousMixVer), 0644); err != nil { + fail(err) + } + b.MixVer = b.State.Mix.PreviousMixVer + b.MixVerUint32, err = parseUint32(b.MixVer) + if err != nil { + fail(errors.Wrapf(err, "Couldn't parse mixer version")) + } + + // Make sure update/image/LAST_VER points to the last good build + if err = ioutil.WriteFile(filepath.Join(b.Config.Builder.ServerStateDir, "image", "LAST_VER"), []byte(b.State.Mix.PreviousMixVer), 0644); err != nil { + failf("Couldn't update LAST_VER file: %s", err) + } + + // update the state.mix file + err = b.State.Save() + if err != nil { + failf("Couldn't update mixer.state file: %s", err) + } + + // update www/version/latest_version file and check if sig file exists, if it does, regenerate the signature + // update www/version/formatXX/latest file and update the content, if signature exits , regenerate the signature + var skipSign bool + latestVerSigFilePath := filepath.Join(b.Config.Builder.ServerStateDir, "www", "version", "latest_version.sig") + if format, err = ioutil.ReadFile(latestVerSigFilePath); err != nil { + skipSign = true + } + err = b.UpdateLatest(skipSign) + if err != nil { + fail(err) + } + + // if clean flag set + if resetCmdFlags.clean { + // Remove any folder inside update/image for versions above prevMixVer + files, err := ioutil.ReadDir(b.Config.Builder.ServerStateDir + "/image") + if err != nil { + log.Fatal(err) + } + + for _, f := range files { + dirNameInt, err := parseUint32(f.Name()) + if err != nil { + continue + } + if dirNameInt > stateMixPreviousMixVersionUint32 { + err := os.RemoveAll(b.Config.Builder.ServerStateDir + "/image/" + f.Name()) + if err != nil { + continue + } + } + } + + // Remove any folder inside update/www for versions above prevMixVer + files, err = ioutil.ReadDir(b.Config.Builder.ServerStateDir + "/www") + if err != nil { + log.Fatal(err) + } + + for _, f := range files { + dirNameInt, err := parseUint32(f.Name()) + if err != nil { + continue + } + if dirNameInt > stateMixPreviousMixVersionUint32 { + err := os.RemoveAll(b.Config.Builder.ServerStateDir + "/www/" + f.Name()) + if err != nil { + continue + } + } + } + + // Remove any folder inside update/www/version for formats above prevStableFormats + files, err = ioutil.ReadDir(b.Config.Builder.ServerStateDir + "/www/version") + if err != nil { + log.Fatal(err) + } + + for _, f := range files { + dirName := strings.SplitAfter(f.Name(), "format") + if len(dirName) == 2 { + dirNameInt, err := parseUint32(dirName[1]) + if err != nil { + continue + } + + if dirNameInt > stateMixPreviousMixFormatUint32 { + err := os.RemoveAll(b.Config.Builder.ServerStateDir + "/www/version/" + f.Name()) + if err != nil { + continue + } + } + } + } + } +}