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 }