github.com/coreos/mantle@v0.13.0/cmd/cork/create.go (about)

     1  // Copyright 2015 CoreOS, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package main
    16  
    17  import (
    18  	"os"
    19  	"path/filepath"
    20  
    21  	"github.com/coreos/go-semver/semver"
    22  	"github.com/spf13/cobra"
    23  	"github.com/spf13/pflag"
    24  
    25  	"github.com/coreos/mantle/sdk"
    26  	"github.com/coreos/mantle/sdk/repo"
    27  )
    28  
    29  const (
    30  	coreosManifestURL = "https://github.com/coreos/manifest.git"
    31  )
    32  
    33  var (
    34  	// everything uses this flag
    35  	chrootFlags *pflag.FlagSet
    36  	chrootName  string
    37  
    38  	// creation flags
    39  	creationFlags  *pflag.FlagSet
    40  	sdkVersion     string
    41  	manifestURL    string
    42  	manifestName   string
    43  	manifestBranch string
    44  	repoVerify     bool
    45  	sigVerify      bool
    46  
    47  	// only for `create` command
    48  	allowReplace bool
    49  
    50  	// only for `enter` command
    51  	bindGpgAgent bool
    52  
    53  	// for create/update/enter
    54  	useHostDNS bool
    55  
    56  	// only for `update` command
    57  	allowCreate      bool
    58  	forceSync        bool
    59  	downgradeInPlace bool
    60  	downgradeReplace bool
    61  	newVersion       string
    62  
    63  	verifyKeyFile string
    64  
    65  	setupCmd = &cobra.Command{
    66  		Use:   "setup",
    67  		Short: "Setup the system for SDK use",
    68  		Run:   runSetup,
    69  	}
    70  	createCmd = &cobra.Command{
    71  		Use:   "create",
    72  		Short: "Download and unpack the SDK",
    73  		Run:   runCreate,
    74  	}
    75  	enterCmd = &cobra.Command{
    76  		Use:   "enter [-- command]",
    77  		Short: "Enter the SDK chroot, optionally running a command",
    78  		Run:   runEnter,
    79  	}
    80  	deleteCmd = &cobra.Command{
    81  		Use:   "delete",
    82  		Short: "Delete the SDK chroot",
    83  		Run:   runDelete,
    84  	}
    85  	updateCmd = &cobra.Command{
    86  		Use:   "update",
    87  		Short: "Update the SDK chroot and source tree",
    88  		Run:   runUpdate,
    89  	}
    90  	verifyCmd = &cobra.Command{
    91  		Use:   "verify",
    92  		Short: "Check repo tree and release manifest match",
    93  		Run:   runVerify,
    94  	}
    95  )
    96  
    97  func init() {
    98  	// the names and error handling of these flag sets are meaningless,
    99  	// the flag sets are only used to group common options together.
   100  	chrootFlags = pflag.NewFlagSet("chroot", pflag.ExitOnError)
   101  	chrootFlags.StringVar(&chrootName,
   102  		"chroot", "chroot", "SDK chroot directory name")
   103  	chrootFlags.BoolVar(&useHostDNS,
   104  		"use-host-dns", false, "Use the host's /etc/resolv.conf instead of 8.8.8.8 and 8.8.4.4")
   105  
   106  	creationFlags = pflag.NewFlagSet("creation", pflag.ExitOnError)
   107  	creationFlags.StringVar(&sdkVersion,
   108  		"sdk-version", "", "SDK version. Defaults to the SDK version in version.txt")
   109  	creationFlags.StringVar(&manifestURL,
   110  		"manifest-url", coreosManifestURL, "Manifest git repo location")
   111  	creationFlags.StringVar(&manifestBranch,
   112  		"manifest-branch", "master", "Manifest git repo branch")
   113  	creationFlags.StringVar(&manifestName,
   114  		"manifest-name", "default.xml", "Manifest file name")
   115  	creationFlags.BoolVar(&repoVerify,
   116  		"verify", false, "Check repo tree and release manifest match")
   117  	creationFlags.StringVar(&verifyKeyFile,
   118  		"verify-key", "", "PGP public key to be used in verifing download signatures.  Defaults to CoreOS Buildbot (0412 7D0B FABE C887 1FFB  2CCE 50E0 8855 93D2 DCB4)")
   119  	creationFlags.BoolVar(&sigVerify,
   120  		"verify-signature", false, "Verify the manifest Git tag with GPG")
   121  
   122  	root.AddCommand(setupCmd)
   123  
   124  	createCmd.Flags().AddFlagSet(chrootFlags)
   125  	createCmd.Flags().AddFlagSet(creationFlags)
   126  	createCmd.Flags().BoolVar(&allowReplace,
   127  		"replace", false, "Replace an existing SDK chroot")
   128  	root.AddCommand(createCmd)
   129  
   130  	enterCmd.Flags().AddFlagSet(chrootFlags)
   131  	enterCmd.Flags().BoolVar(&bindGpgAgent,
   132  		"bind-gpg-agent", true, "bind mount the gpg agent socket directory")
   133  	root.AddCommand(enterCmd)
   134  
   135  	deleteCmd.Flags().AddFlagSet(chrootFlags)
   136  	root.AddCommand(deleteCmd)
   137  
   138  	updateCmd.Flags().AddFlagSet(chrootFlags)
   139  	updateCmd.Flags().AddFlagSet(creationFlags)
   140  	updateCmd.Flags().BoolVar(&allowCreate,
   141  		"create", false, "Create the SDK chroot if missing")
   142  	updateCmd.Flags().BoolVar(&forceSync,
   143  		"force-sync", false, "Overrwrite stale .git directories if needed")
   144  	updateCmd.Flags().BoolVar(&downgradeInPlace,
   145  		"downgrade-in-place", false,
   146  		"Allow in-place downgrades of SDK chroot")
   147  	updateCmd.Flags().BoolVar(&downgradeReplace,
   148  		"downgrade-replace", false,
   149  		"Replace SDK chroot instead of downgrading")
   150  	updateCmd.Flags().StringVar(&newVersion,
   151  		"new-version", "", "Hint at the new version. Defaults to the version in version.txt")
   152  	root.AddCommand(updateCmd)
   153  
   154  	root.AddCommand(verifyCmd)
   155  }
   156  
   157  func runSetup(cmd *cobra.Command, args []string) {
   158  	if len(args) != 0 {
   159  		plog.Fatal("No args accepted")
   160  	}
   161  
   162  	if err := installQemuBinfmt(); err != nil {
   163  		plog.Fatal("Install QEMU binfmt failed: ", err)
   164  	}
   165  }
   166  
   167  func installQemuBinfmt() error {
   168  	const dest = "/etc/binfmt.d/qemu-aarch64.conf"
   169  	const data = ":qemu-aarch64:M::\\x7fELF\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\xb7:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/usr/bin/qemu-aarch64-static:"
   170  
   171  	// Only install if file does not exist
   172  	if _, err := os.Stat(dest); err == nil {
   173  		return nil
   174  	}
   175  
   176  	dir := filepath.Dir(dest)
   177  	if err := os.MkdirAll(dir, 0755); err != nil {
   178  		return err
   179  	}
   180  
   181  	file, err := os.Create(dest)
   182  	if err != nil {
   183  		return err
   184  	}
   185  
   186  	if _, err := file.WriteString(data); err != nil {
   187  		return err
   188  	}
   189  
   190  	if err := file.Close(); err != nil {
   191  		os.Remove(dest)
   192  		return err
   193  	}
   194  
   195  	return nil
   196  }
   197  
   198  func runCreate(cmd *cobra.Command, args []string) {
   199  	if len(args) != 0 {
   200  		plog.Fatal("No args accepted")
   201  	}
   202  
   203  	if sdkVersion == "" {
   204  		plog.Noticef("Detecting SDK version")
   205  
   206  		getRemoteVersions := sdk.VersionsFromRemoteRepo
   207  		if sigVerify {
   208  			getRemoteVersions = sdk.VersionsFromSignedRemoteRepo
   209  		}
   210  
   211  		if ver, err := sdk.VersionsFromManifest(); err == nil {
   212  			sdkVersion = ver.SDKVersion
   213  			plog.Noticef("Found SDK version %s from local repo", sdkVersion)
   214  		} else if ver, err := getRemoteVersions(manifestURL, manifestBranch); err == nil {
   215  			sdkVersion = ver.SDKVersion
   216  			plog.Noticef("Found SDK version %s from remote repo", sdkVersion)
   217  		} else {
   218  			plog.Fatalf("Reading from remote repo failed: %v", err)
   219  		}
   220  	}
   221  
   222  	unpackChroot(allowReplace)
   223  	updateRepo()
   224  }
   225  
   226  func unpackChroot(replace bool) {
   227  	plog.Noticef("Downloading SDK version %s", sdkVersion)
   228  	if err := sdk.DownloadSDK(sdkVersion, verifyKeyFile); err != nil {
   229  		plog.Fatalf("Download failed: %v", err)
   230  	}
   231  
   232  	if replace {
   233  		if err := sdk.Delete(chrootName); err != nil {
   234  			plog.Fatalf("Replace failed: %v", err)
   235  		}
   236  	}
   237  
   238  	if err := sdk.Unpack(sdkVersion, chrootName); err != nil {
   239  		plog.Fatalf("Create failed: %v", err)
   240  	}
   241  
   242  	if err := sdk.Setup(chrootName); err != nil {
   243  		plog.Fatalf("Create failed: %v", err)
   244  	}
   245  }
   246  
   247  func updateRepo() {
   248  	if err := sdk.RepoInit(chrootName, manifestURL, manifestBranch, manifestName, useHostDNS); err != nil {
   249  		plog.Fatalf("repo init failed: %v", err)
   250  	}
   251  
   252  	if sigVerify {
   253  		if err := sdk.RepoVerifyTag(manifestBranch); err != nil {
   254  			plog.Fatalf("repo tag verification failed: %v", err)
   255  		}
   256  	}
   257  
   258  	if err := sdk.RepoSync(chrootName, forceSync, useHostDNS); err != nil {
   259  		plog.Fatalf("repo sync failed: %v", err)
   260  	}
   261  
   262  	if repoVerify {
   263  		if err := repo.VerifySync(manifestName); err != nil {
   264  			plog.Fatalf("Verify failed: %v", err)
   265  		}
   266  	}
   267  }
   268  
   269  func runEnter(cmd *cobra.Command, args []string) {
   270  	err := sdk.Enter(chrootName, bindGpgAgent, useHostDNS, args...)
   271  	if err != nil && len(args) != 0 {
   272  		plog.Fatalf("Running %v failed: %v", args, err)
   273  	}
   274  }
   275  
   276  func runDelete(cmd *cobra.Command, args []string) {
   277  	if len(args) != 0 {
   278  		plog.Fatal("No args accepted")
   279  	}
   280  
   281  	if err := sdk.Delete(chrootName); err != nil {
   282  		plog.Fatalf("Delete failed: %v", err)
   283  	}
   284  }
   285  
   286  func verLessThan(a, b string) bool {
   287  	aver, err := semver.NewVersion(a)
   288  	if err != nil {
   289  		plog.Fatal(err)
   290  	}
   291  	bver, err := semver.NewVersion(b)
   292  	if err != nil {
   293  		plog.Fatal(err)
   294  	}
   295  	return aver.LessThan(*bver)
   296  }
   297  
   298  func runUpdate(cmd *cobra.Command, args []string) {
   299  	const updateChroot = "/mnt/host/source/src/scripts/update_chroot"
   300  	updateCommand := append([]string{updateChroot}, args...)
   301  
   302  	// avoid downgrade strategy ambiguity
   303  	if downgradeInPlace && downgradeReplace {
   304  		plog.Fatal("Conflicting downgrade options")
   305  	}
   306  
   307  	if sdkVersion == "" || newVersion == "" {
   308  		plog.Notice("Detecting versions in remote repo")
   309  		getRemoteVersions := sdk.VersionsFromRemoteRepo
   310  		if sigVerify {
   311  			getRemoteVersions = sdk.VersionsFromSignedRemoteRepo
   312  		}
   313  		ver, err := getRemoteVersions(manifestURL, manifestBranch)
   314  		if err != nil {
   315  			plog.Fatalf("Reading from remote repo failed: %v", err)
   316  		}
   317  
   318  		if newVersion == "" {
   319  			newVersion = ver.Version
   320  		}
   321  
   322  		if sdkVersion == "" {
   323  			sdkVersion = ver.SDKVersion
   324  		}
   325  	}
   326  
   327  	plog.Infof("New version %s", newVersion)
   328  	plog.Infof("SDK version %s", sdkVersion)
   329  
   330  	plog.Info("Checking version of local chroot")
   331  	chroot := filepath.Join(sdk.RepoRoot(), chrootName)
   332  	old, err := sdk.OSRelease(chroot)
   333  	if err != nil {
   334  		if allowCreate && os.IsNotExist(err) {
   335  			unpackChroot(false)
   336  		} else {
   337  			plog.Fatal(err)
   338  		}
   339  	} else if verLessThan(newVersion, old.Version) {
   340  		plog.Noticef("Downgrade from %s to %s required!",
   341  			old.Version, newVersion)
   342  		if downgradeReplace {
   343  			unpackChroot(true)
   344  		} else if downgradeInPlace {
   345  			plog.Infof("Attempting to downgrade existing chroot.")
   346  		} else {
   347  			plog.Fatalf("Refusing to downgrade.")
   348  		}
   349  	}
   350  
   351  	updateRepo()
   352  
   353  	if err := sdk.Enter(chrootName, false, false, updateCommand...); err != nil {
   354  		plog.Fatalf("update_chroot failed: %v", err)
   355  	}
   356  }
   357  
   358  func runVerify(cmd *cobra.Command, args []string) {
   359  	if len(args) != 0 {
   360  		plog.Fatal("No args accepted")
   361  	}
   362  
   363  	if err := repo.VerifySync(""); err != nil {
   364  		plog.Fatalf("Verify failed: %v", err)
   365  	}
   366  }