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

     1  // Copyright 2015 CoreOS, Inc.
     2  // Copyright 2011 The Go Authors.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package sdk
    17  
    18  import (
    19  	"bytes"
    20  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  	"text/template"
    24  
    25  	"github.com/coreos/pkg/capnslog"
    26  
    27  	"github.com/coreos/mantle/system/exec"
    28  	"github.com/coreos/mantle/system/user"
    29  	"github.com/coreos/mantle/util"
    30  )
    31  
    32  // Must run inside the SDK chroot, easiest to just assemble a script to do it
    33  const (
    34  	safePath   = "PATH=/usr/sbin:/usr/bin:/sbin:/bin"
    35  	sudoPrompt = "sudo password for %p: "
    36  	script     = `#!/bin/bash
    37  set -e
    38  
    39  # make sure user/group database files exist
    40  touch /etc/{group,gshadow,passwd,shadow}
    41  chmod 0640 /etc/{gshadow,shadow}
    42  
    43  # add group if it doesn't exist already
    44  if ! getent group {{printf "%q" .Groupname}} >/dev/null; then
    45  	echo Adding group {{printf "%q" .Groupname}}
    46  	groupadd -o -g {{.Gid}} {{printf "%q" .Groupname}}
    47  fi
    48  
    49  # add user if it doesn't exist already
    50  if ! getent passwd {{printf "%q" .Username}} >/dev/null; then
    51  	echo Adding user {{printf "%q" .Username}}
    52  	useradd -o -g {{.Gid}} -u {{.Uid}} -s /bin/bash -m \
    53  		-c {{printf "%q" .Name}} {{printf "%q" .Username}}
    54  fi
    55  
    56  for g in kvm portage sudo; do
    57  	# copy system group from /usr to /etc if needed
    58  	if getent -s usrfiles group "$g" >/dev/null && \
    59  	   ! getent -s files group "$g" >/dev/null; then
    60  		getent -s usrfiles group "$g" >> /etc/group
    61  	fi
    62  	gpasswd -a {{printf "%q" .Username}} "$g"
    63  done
    64  
    65  echo Setting up sudoers
    66  cat >/etc/sudoers.d/90_env_keep <<EOF
    67  Defaults env_keep += "\
    68  COREOS_BUILD_ID COREOS_OFFICIAL \
    69  EMAIL GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME \
    70  GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME \
    71  GIT_PROXY_COMMAND GIT_SSH RSYNC_PROXY \
    72  GNUPGHOME GPG_AGENT_INFO SSH_AUTH_SOCK \
    73  BOTO_PATH GOOGLE_APPLICATION_CREDENTIALS \
    74  USE FEATURES PORTAGE_USERNAME \
    75  all_proxy ftp_proxy http_proxy https_proxy no_proxy"
    76  EOF
    77  chmod 0440 /etc/sudoers.d/90_env_keep
    78  
    79  echo Setting default enviornment variables
    80  cat >/etc/env.d/90portage_username <<EOF
    81  PORTAGE_USERNAME={{printf "%q" .Username}}
    82  EOF
    83  # needlessly noisy since portage isn't set up yet
    84  env-update &>/dev/null
    85  
    86  echo Setting up home directory
    87  HOME=/home/{{printf "%q" .Username}}
    88  
    89  # Create ~/trunk symlink
    90  ln -sfT /mnt/host/source "$HOME"/trunk
    91  
    92  rm -f "$HOME"/.bash{_logout,_profile,rc}
    93  cat >"$HOME"/.bash_logout <<EOF
    94  # .bash_logout
    95  
    96  # This file is sourced when a login shell terminates.
    97  EOF
    98  
    99  cat >"$HOME"/.bash_profile <<EOF
   100  # .bash_profile
   101  
   102  # This file is sourced by bash for login shells.  The following line
   103  # runs your .bashrc and is recommended by the bash info pages.
   104  [[ -f ~/.bashrc ]] && . ~/.bashrc
   105  
   106  # Automatically change to scripts directory.
   107  cd ${CHROOT_CWD:-~/trunk/src/scripts}
   108  EOF
   109  
   110  cat >"$HOME"/.bashrc << 'EOF'
   111  # .bashrc
   112  
   113  # This file is sourced by all *interactive* bash shells on startup,
   114  # including some apparently interactive shells such as scp and rcp
   115  # that can't tolerate any output.  So make sure this doesn't display
   116  # anything or bad things will happen !
   117  
   118  # Test for an interactive shell.  There is no need to set anything
   119  # past this point for scp and rcp, and it's important to refrain from
   120  # outputting anything in those cases.
   121  if [[ $- != *i* ]] ; then
   122  	# Shell is non-interactive.  Be done now!
   123  	return
   124  fi
   125  
   126  # Enable bash completion for build scripts.
   127  source ~/trunk/src/scripts/bash_completion
   128  
   129  # Put your fun stuff here.
   130  EOF
   131  
   132  chown -R {{.Uid}}:{{.Gid}} "$HOME"
   133  
   134  # Checked in src/scripts/common.sh
   135  touch /etc/debian_chroot
   136  `
   137  )
   138  
   139  var scriptTemplate = template.Must(template.New("script").Parse(script))
   140  
   141  func Setup(name string) error {
   142  	chroot := filepath.Join(RepoRoot(), name)
   143  	u, err := user.Current()
   144  	if err != nil {
   145  		return err
   146  	}
   147  
   148  	var sc bytes.Buffer
   149  	if err := scriptTemplate.Execute(&sc, u); err != nil {
   150  		return err
   151  	}
   152  
   153  	plog.Info("Configuring SDK chroot")
   154  	sh := exec.Command(
   155  		"sudo", "-p", sudoPrompt,
   156  		"chroot", chroot,
   157  		"/usr/bin/env", "-i",
   158  		"/bin/bash", "--login")
   159  	sh.Stdin = &sc
   160  	sh.Stderr = os.Stderr
   161  	if plog.LevelAt(capnslog.INFO) {
   162  		out, err := sh.StdoutPipe()
   163  		if err != nil {
   164  			return err
   165  		}
   166  		go util.LogFrom(capnslog.INFO, out)
   167  	}
   168  	if plog.LevelAt(capnslog.DEBUG) {
   169  		sh.Args = append(sh.Args, "-x")
   170  	}
   171  	return sh.Run()
   172  }
   173  
   174  func extract(tar, dir string) error {
   175  	in, err := os.Open(tar)
   176  	if err != nil {
   177  		return err
   178  	}
   179  	defer in.Close()
   180  
   181  	unzipper, err := exec.LookPath("lbzcat")
   182  	if err != nil {
   183  		unzipper = "bzcat"
   184  	}
   185  
   186  	unzip := exec.Command(unzipper)
   187  	unzip.Stdin = in
   188  	unzip.Stderr = os.Stderr
   189  	unzipped, err := unzip.StdoutPipe()
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	untar := exec.Command("sudo", "-p", sudoPrompt,
   195  		"tar", "--numeric-owner", "-x")
   196  	untar.Dir = dir
   197  	untar.Stdin = unzipped
   198  	untar.Stderr = os.Stderr
   199  
   200  	if err := unzip.Start(); err != nil {
   201  		return err
   202  	}
   203  
   204  	if err := untar.Start(); err != nil {
   205  		unzip.Kill()
   206  		return err
   207  	}
   208  
   209  	if err := untar.Wait(); err != nil {
   210  		unzip.Kill()
   211  		return err
   212  	}
   213  
   214  	if err := unzip.Wait(); err != nil {
   215  		return err
   216  	}
   217  
   218  	return nil
   219  }
   220  
   221  func Unpack(version, name string) error {
   222  	chroot := filepath.Join(RepoRoot(), name)
   223  	if _, err := os.Stat(chroot); !os.IsNotExist(err) {
   224  		if err == nil {
   225  			err = fmt.Errorf("Path already exists: %s", chroot)
   226  		}
   227  		return err
   228  	}
   229  
   230  	plog.Noticef("Unpacking SDK into %s", chroot)
   231  	if err := os.MkdirAll(chroot, 0777); err != nil {
   232  		return err
   233  	}
   234  
   235  	tar := filepath.Join(RepoCache(), "sdks", TarballName(version))
   236  	plog.Infof("Using %s", tar)
   237  	if err := extract(tar, chroot); err != nil {
   238  		plog.Errorf("Extracting %s to %s failed: %v", tar, chroot, err)
   239  		return err
   240  	}
   241  	plog.Notice("Unpacked")
   242  
   243  	return nil
   244  }
   245  
   246  func Delete(name string) error {
   247  	chroot := filepath.Join(RepoRoot(), name)
   248  	if _, err := os.Stat(chroot); err != nil {
   249  		if os.IsNotExist(err) {
   250  			plog.Infof("Path does not exist: %s", chroot)
   251  			return nil
   252  		}
   253  		return err
   254  	}
   255  
   256  	plog.Noticef("Removing SDK at %s", chroot)
   257  	rm := exec.Command("sudo", "-p", sudoPrompt, "rm", "-rf", chroot)
   258  	rm.Stderr = os.Stderr
   259  	if err := rm.Run(); err != nil {
   260  		return err
   261  	}
   262  	plog.Notice("Removed")
   263  
   264  	return nil
   265  }