diff --git a/cmd/freebsd-update.conf.go b/cmd/freebsd-update.conf.go new file mode 100644 index 0000000..080764a --- /dev/null +++ b/cmd/freebsd-update.conf.go @@ -0,0 +1,85 @@ +package cmd + +const ( + fbsdUpdateConfig = ` + # $FreeBSD$ + + # Trusted keyprint. Changing this is a Bad Idea unless you've received + # a PGP-signed email from telling you to + # change it and explaining why. + KeyPrint 800651ef4b4c71c27e60786d7b487188970f4b4169cc055784e21eb71d410cc5 + + # Server or server pool from which to fetch updates. You can change + # this to point at a specific server if you want, but in most cases + # using a "nearby" server won't provide a measurable improvement in + # performance. + ServerName update.FreeBSD.org + + # Components of the base system which should be kept updated. + Components world + + # Example for updating the userland and the kernel source code only: + # Components src/base src/sys world + + # Paths which start with anything matching an entry in an IgnorePaths + # statement will be ignored. + IgnorePaths + + # Paths which start with anything matching an entry in an IDSIgnorePaths + # statement will be ignored by "freebsd-update IDS". + IDSIgnorePaths /usr/share/man/cat + IDSIgnorePaths /usr/share/man/whatis + IDSIgnorePaths /var/db/locate.database + IDSIgnorePaths /var/log + + # Paths which start with anything matching an entry in an UpdateIfUnmodified + # statement will only be updated if the contents of the file have not been + # modified by the user (unless changes are merged; see below). + UpdateIfUnmodified /etc/ /var/ /root/ /.cshrc /.profile + + # When upgrading to a new FreeBSD release, files which match MergeChanges + # will have any local changes merged into the version from the new release. + MergeChanges /etc/ + + ### Default configuration options: + + # Directory in which to store downloaded updates and temporary + # files used by FreeBSD Update. + WorkDir /iocage/freebsd-update + + # Destination to send output of "freebsd-update cron" if an error + # occurs or updates have been downloaded. + # MailTo root + + # Is FreeBSD Update allowed to create new files? + # AllowAdd yes + + # Is FreeBSD Update allowed to delete files? + # AllowDelete yes + + # If the user has modified file ownership, permissions, or flags, should + # FreeBSD Update retain this modified metadata when installing a new version + # of that file? + # KeepModifiedMetadata yes + + # When upgrading between releases, should the list of Components be + # read strictly (StrictComponents yes) or merely as a list of components + # which *might* be installed of which FreeBSD Update should figure out + # which actually are installed and upgrade those (StrictComponents no)? + StrictComponents yes + + # When installing a new kernel perform a backup of the old one first + # so it is possible to boot the old kernel in case of problems. + BackupKernel no + + # If BackupKernel is enabled, the backup kernel is saved to this + # directory. + # BackupKernelDir /boot/kernel.old + + # When backing up a kernel also back up debug symbol files? + BackupKernelSymbolFiles no + + # Create a new boot environment when installing patches + CreateBootEnv no + ` +) diff --git a/cmd/root.go b/cmd/root.go index 229739c..c86b305 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -15,7 +15,7 @@ import ( ) const ( - gVersion = "0.35" + gVersion = "0.36a" // TODO : Get from $jail_zpool/defaults.json MIN_DYN_DEVFS_RULESET = 1000 @@ -55,6 +55,7 @@ var ( gFetchRelease string gFetchIntoDS string gFetchFrom string + gUpgradeRelease string gMdevfs sync.Mutex @@ -139,9 +140,7 @@ ex: gocage list srv-db srv-web`, WriteConfigToDisk("", false, false) }, } - - - + restartCmd = &cobra.Command{ Use: "restart", Short: "restart jail", @@ -153,7 +152,7 @@ ex: gocage list srv-db srv-web`, WriteConfigToDisk("", false, false) }, } - + shellCmd = &cobra.Command { Use: "console", Short: "Execute shell on jail", @@ -163,7 +162,7 @@ ex: gocage list srv-db srv-web`, ShellJail(args) }, } - + setCmd = &cobra.Command{ Use: "set", Short: "Set a jail property", @@ -291,7 +290,7 @@ You can specify multiple datastores.`, ListDatastores(args, true) }, } - + fetchCmd = &cobra.Command{ Use: "fetch", Short: "Fetch FreeBSD release to local datastore", @@ -302,18 +301,27 @@ You can specify multiple datastores.`, } else { extractRelease(gFetchRelease, gFetchIntoDS) } - }, + }, } - - UpdateCmd = &cobra.Command{ + + updateCmd = &cobra.Command{ Use: "update", - Short: "Update FreeBSD release", - Run: func(cmd *cobra.Command, args []string) { - ListJails(args, false) - UpdateJail(args) - }, + Short: "Update FreeBSD release", + Run: func(cmd *cobra.Command, args []string) { + ListJails(args, false) + UpdateJail(args) + }, } - + + upgradeCmd = &cobra.Command{ + Use: "upgrade", + Short: "Upgrade FreeBSD release", + Run: func(cmd *cobra.Command, args []string) { + ListJails(args, false) + UpgradeJail(args) + }, + } + testCmd = &cobra.Command{ Use: "test", Short: "temporary command to test some code snippet", @@ -373,6 +381,8 @@ func init() { fetchCmd.MarkFlagRequired("release") fetchCmd.MarkFlagRequired("datastore") + upgradeCmd.Flags().StringVarP(&gUpgradeRelease, "release", "r", "", "Release to upgrade to (e.g.: \"13.1-RELEASE\"") + upgradeCmd.MarkFlagRequired("release") // Now declare commands rootCmd.AddCommand(versionCmd) @@ -389,7 +399,8 @@ func init() { rootCmd.AddCommand(migrateCmd) rootCmd.AddCommand(datastoreCmd) rootCmd.AddCommand(fetchCmd) - rootCmd.AddCommand(UpdateCmd) + rootCmd.AddCommand(updateCmd) + rootCmd.AddCommand(upgradeCmd) rootCmd.AddCommand(testCmd) @@ -522,6 +533,8 @@ func WriteConfigToDisk(jailName string, changeauto bool, forceWrite bool) { //fmt.Printf("DEBUG: Will write config to disk, with content:\n") //fmt.Printf(string(marshaled)) + fmt.Printf("DEBUG: Will write config to disk, Config.Release=%s\n", jc.Release) + fmt.Printf("DEBUG: Will write config to disk, Config.Last_started=%s\n", jc.Last_started) if os.WriteFile(j.ConfigPath, []byte(marshaled), 0644); err != nil { fmt.Printf("Error writing config file %s: %v\n", j.ConfigPath, err) diff --git a/cmd/update.go b/cmd/update.go index 71670f1..1c1e9b1 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -7,117 +7,41 @@ import ( "time" ) -const ( - fbsdUpdateConfig = ` - # $FreeBSD: releng/12.2/usr.sbin/freebsd-update/freebsd-update.conf 337338 2018-08-04 22:25:41Z brd $ - - # Trusted keyprint. Changing this is a Bad Idea unless you've received - # a PGP-signed email from telling you to - # change it and explaining why. - KeyPrint 800651ef4b4c71c27e60786d7b487188970f4b4169cc055784e21eb71d410cc5 - - # Server or server pool from which to fetch updates. You can change - # this to point at a specific server if you want, but in most cases - # using a "nearby" server won't provide a measurable improvement in - # performance. - ServerName update.FreeBSD.org - - # Components of the base system which should be kept updated. - Components world - - # Example for updating the userland and the kernel source code only: - # Components src/base src/sys world - - # Paths which start with anything matching an entry in an IgnorePaths - # statement will be ignored. - IgnorePaths - - # Paths which start with anything matching an entry in an IDSIgnorePaths - # statement will be ignored by "freebsd-update IDS". - IDSIgnorePaths /usr/share/man/cat - IDSIgnorePaths /usr/share/man/whatis - IDSIgnorePaths /var/db/locate.database - IDSIgnorePaths /var/log - - # Paths which start with anything matching an entry in an UpdateIfUnmodified - # statement will only be updated if the contents of the file have not been - # modified by the user (unless changes are merged; see below). - UpdateIfUnmodified /etc/ /var/ /root/ /.cshrc /.profile - - # When upgrading to a new FreeBSD release, files which match MergeChanges - # will have any local changes merged into the version from the new release. - MergeChanges /etc/ - - ### Default configuration options: - - # Directory in which to store downloaded updates and temporary - # files used by FreeBSD Update. - # WorkDir /var/db/freebsd-update - - # Destination to send output of "freebsd-update cron" if an error - # occurs or updates have been downloaded. - # MailTo root - - # Is FreeBSD Update allowed to create new files? - # AllowAdd yes - - # Is FreeBSD Update allowed to delete files? - # AllowDelete yes - - # If the user has modified file ownership, permissions, or flags, should - # FreeBSD Update retain this modified metadata when installing a new version - # of that file? - # KeepModifiedMetadata yes - - # When upgrading between releases, should the list of Components be - # read strictly (StrictComponents yes) or merely as a list of components - # which *might* be installed of which FreeBSD Update should figure out - # which actually are installed and upgrade those (StrictComponents no)? - # StrictComponents no - - # When installing a new kernel perform a backup of the old one first - # so it is possible to boot the old kernel in case of problems. - # BackupKernel yes - - # If BackupKernel is enabled, the backup kernel is saved to this - # directory. - # BackupKernelDir /boot/kernel.old - - # When backing up a kernel also back up debug symbol files? - # BackupKernelSymbolFiles no - - # Create a new boot environment when installing patches - # CreateBootEnv yes - ` -) // Internal usage only func updateJail(jail *Jail) error { - // Create default config as temporary file cfgFile, err := os.CreateTemp("", "gocage-jail-update-") if err != nil { return err } - + cfgFile.Write([]byte(fbsdUpdateConfig)) - + defer cfgFile.Close() - //defer os.Remove(cfgFile.Name()) - + defer os.Remove(cfgFile.Name()) + + // Folder containing update/uipgrade temporary files. Common so we save bandwith when upgrading multiple jails + // TODO: Variabilize /iocage/freebsd-update + _, err = os.Stat("/iocage/freebsd-update") + if os.IsNotExist(err) { + if err := os.Mkdir("/iocage/freebsd-update", 0755); err != nil { + return err + } + } + cmd := fmt.Sprintf("/usr/sbin/freebsd-update --not-running-from-cron -f %s -b %s --currently-running %s fetch install", cfgFile.Name(), jail.RootPath, jail.Config.Release) - - fmt.Printf("DEBUG: Prepare to execute \"%s\"\n", cmd) + + //fmt.Printf("DEBUG: Prepare to execute \"%s\"\n", cmd) err = executeCommandWithOutputToStdout(cmd) if err != nil { return err } - - // Get and write new release into config.json - - + + // TODO : Get and write new release into config.json + return nil } diff --git a/cmd/upgrade.go b/cmd/upgrade.go new file mode 100644 index 0000000..0a9dff8 --- /dev/null +++ b/cmd/upgrade.go @@ -0,0 +1,124 @@ +package cmd + + + +import ( + "os" + "fmt" + //"log" + "time" + "strings" +) + +// Internal usage only +func upgradeJail(jail *Jail, version string) error { + // Create default config as temporary file + cfgFile, err := os.CreateTemp("", "gocage-jail-upgrade-") + if err != nil { + return err + } + + cfgFile.Write([]byte(fbsdUpdateConfig)) + + defer cfgFile.Close() + defer os.Remove(cfgFile.Name()) + + // Folder containing update/uipgrade temporary files. Common so we save bandwith when upgrading multiple jails + // TODO: Variabilize /iocage/freebsd-update + _, err = os.Stat("/iocage/freebsd-update") + if os.IsNotExist(err) { + if err := os.Mkdir("/iocage/freebsd-update", 0755); err != nil { + return err + } + } + + // Get current version. Won't work on stopped jail. + fbsdvers, err := executeCommandInJail(jail, "/bin/freebsd-version") + if err != nil { + fmt.Printf("ERROR executeCommandInJail: %s\n", err.Error()) + return err + } + fbsdvers = strings.TrimRight(fbsdvers, "\n") + //fbsdvers := jail.Config.Release + + cmd := fmt.Sprintf("/usr/sbin/freebsd-update -f %s -b %s --currently-running %s -r %s upgrade", + cfgFile.Name(), jail.RootPath, fbsdvers, version) + + //fmt.Printf("DEBUG: Prepare to execute \"%s\"\n", cmd) + + // Need to give user control, bc there could be merge edit needs + err = executeCommandWithStdinStdoutStderr(cmd) + if err != nil { + return err + } + + cmd = fmt.Sprintf("/usr/sbin/freebsd-update -f %s -b %s --currently-running %s -r %s install", + cfgFile.Name(), jail.RootPath, fbsdvers, version) + + //fmt.Printf("DEBUG: Prepare to execute \"%s\"\n", cmd) + + err = executeCommandWithStdinStdoutStderr(cmd) + if err != nil { + return err + } + + cmd = fmt.Sprintf("/usr/sbin/freebsd-update -f %s -b %s --currently-running %s -r %s install", + cfgFile.Name(), jail.RootPath, fbsdvers, version) + + //fmt.Printf("DEBUG: Prepare to execute \"%s\"\n", cmd) + + err = executeCommandWithStdinStdoutStderr(cmd) + if err != nil { + return err + } + + cmd = fmt.Sprintf("/usr/local/sbin/pkg-static -j %d install -q -f -y pkg", jail.JID) + err = executeCommandWithStdinStdoutStderr(cmd) + if err != nil { + return err + } + + // Get and write new release into config.json + + return nil +} + +func UpgradeJail(args []string) { + // Current jail were stopping + var cj *Jail + var err error + + for _, a := range args { + // Check if jail exist and is distinctly named + cj, err = getJailFromArray(a, gJails) + if err != nil { + fmt.Printf("Error getting jail: %s\n", err) + continue + } + + if cj.Running == false { + fmt.Printf("Error: jail must be running for upgrade.\n") + return + } + + fmt.Printf(" > Snapshot jail %s\n", cj.Name) + // Set snapshot name + dt := time.Now() + curDate := fmt.Sprintf("%s", dt.Format("2006-01-02_15-04-05")) + gSnapshotName = fmt.Sprintf("goc_upgrade_%s_%s", cj.Config.Release, curDate) + err := createJailSnapshot(*cj) + if err != nil { + fmt.Printf(" > Snapshot jail %s: ERROR: %s\n", cj.Name, err.Error()) + return + } + fmt.Printf(" > Snapshot jail %s: OK\n", cj.Name) + + fmt.Printf(" > Upgrade jail %s to %s\n", cj.Name, gUpgradeRelease) + err = upgradeJail(cj, gUpgradeRelease) + if err != nil { + fmt.Printf("ERROR: %s\n", err.Error()) + } else { + fmt.Printf(" > Upgrade jail %s: OK\n", cj.Name) + } + } +}