2016-08-23 15:03:41 +00:00
/ *
Copyright 2016 The Kubernetes Authors All rights reserved .
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 provision
import (
"bytes"
"fmt"
"path"
2017-07-10 18:50:21 +00:00
"path/filepath"
2019-03-27 09:05:20 +00:00
"strings"
2016-10-17 09:00:43 +00:00
"text/template"
2016-11-16 19:50:08 +00:00
"time"
2016-08-23 15:03:41 +00:00
"github.com/docker/machine/libmachine/auth"
2017-07-10 18:50:21 +00:00
"github.com/docker/machine/libmachine/cert"
2016-08-23 15:03:41 +00:00
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/engine"
"github.com/docker/machine/libmachine/log"
2017-07-10 18:50:21 +00:00
"github.com/docker/machine/libmachine/mcnutils"
2016-08-23 15:03:41 +00:00
"github.com/docker/machine/libmachine/provision"
"github.com/docker/machine/libmachine/provision/pkgaction"
2017-07-10 18:50:21 +00:00
"github.com/docker/machine/libmachine/provision/serviceaction"
2016-08-23 15:03:41 +00:00
"github.com/docker/machine/libmachine/swarm"
2017-07-10 18:50:21 +00:00
"github.com/pkg/errors"
"k8s.io/minikube/pkg/minikube/assets"
2019-07-04 23:31:07 +00:00
"k8s.io/minikube/pkg/minikube/command"
2018-12-07 20:08:59 +00:00
"k8s.io/minikube/pkg/minikube/config"
2017-07-10 18:50:21 +00:00
"k8s.io/minikube/pkg/minikube/sshutil"
2016-11-16 19:50:08 +00:00
"k8s.io/minikube/pkg/util"
2019-08-14 06:48:30 +00:00
"k8s.io/minikube/pkg/util/retry"
2016-08-23 15:03:41 +00:00
)
2019-03-16 13:12:18 +00:00
// BuildrootProvisioner provisions the custom system based on Buildroot
2016-08-23 15:03:41 +00:00
type BuildrootProvisioner struct {
provision . SystemdProvisioner
}
2019-03-27 09:05:20 +00:00
// for escaping systemd template specifiers (e.g. '%i'), which are not supported by minikube
var systemdSpecifierEscaper = strings . NewReplacer ( "%" , "%%" )
2016-08-23 15:03:41 +00:00
func init ( ) {
provision . Register ( "Buildroot" , & provision . RegisteredProvisioner {
New : NewBuildrootProvisioner ,
} )
}
2019-03-16 13:12:18 +00:00
// NewBuildrootProvisioner creates a new BuildrootProvisioner
2016-08-23 15:03:41 +00:00
func NewBuildrootProvisioner ( d drivers . Driver ) provision . Provisioner {
return & BuildrootProvisioner {
provision . NewSystemdProvisioner ( "buildroot" , d ) ,
}
}
func ( p * BuildrootProvisioner ) String ( ) string {
return "buildroot"
}
2019-07-14 14:02:37 +00:00
// CompatibleWithHost checks if provisioner is compatible with host
func ( p * BuildrootProvisioner ) CompatibleWithHost ( ) bool {
return p . OsReleaseInfo . ID == "buildroot"
}
2019-03-27 09:05:20 +00:00
// escapeSystemdDirectives escapes special characters in the input variables used to create the
// systemd unit file, which would otherwise be interpreted as systemd directives. An example
// are template specifiers (e.g. '%i') which are predefined variables that get evaluated dynamically
// (see systemd man pages for more info). This is not supported by minikube, thus needs to be escaped.
func escapeSystemdDirectives ( engineConfigContext * provision . EngineConfigContext ) {
// escape '%' in Environment option so that it does not evaluate into a template specifier
engineConfigContext . EngineOptions . Env = util . ReplaceChars ( engineConfigContext . EngineOptions . Env , systemdSpecifierEscaper )
// input might contain whitespaces, wrap it in quotes
engineConfigContext . EngineOptions . Env = util . ConcatStrings ( engineConfigContext . EngineOptions . Env , "\"" , "\"" )
}
2019-03-16 13:12:18 +00:00
// GenerateDockerOptions generates the *provision.DockerOptions for this provisioner
2016-08-23 15:03:41 +00:00
func ( p * BuildrootProvisioner ) GenerateDockerOptions ( dockerPort int ) ( * provision . DockerOptions , error ) {
var engineCfg bytes . Buffer
2019-10-18 20:00:07 +00:00
drvLabel := fmt . Sprintf ( "provider=%s" , p . Driver . DriverName ( ) )
p . EngineOptions . Labels = append ( p . EngineOptions . Labels , drvLabel )
2016-08-23 15:03:41 +00:00
2019-08-14 19:18:38 +00:00
noPivot := true
// Using pivot_root is not supported on fstype rootfs
if fstype , err := rootFileSystemType ( p ) ; err == nil {
log . Debugf ( "root file system type: %s" , fstype )
noPivot = fstype == "rootfs"
}
2016-08-23 15:03:41 +00:00
engineConfigTmpl := ` [ Unit ]
Description = Docker Application Container Engine
Documentation = https : //docs.docker.com
2018-11-21 21:39:48 +00:00
After = network . target minikube - automount . service docker . socket
Requires = minikube - automount . service docker . socket
2016-08-23 15:03:41 +00:00
[ Service ]
Type = notify
2019-08-14 19:18:38 +00:00
`
if noPivot {
2019-08-24 14:16:11 +00:00
log . Warn ( "Using fundamentally insecure --no-pivot option" )
2019-08-14 19:18:38 +00:00
engineConfigTmpl += `
2016-08-23 15:03:41 +00:00
# DOCKER_RAMDISK disables pivot_root in Docker , using MS_MOVE instead .
Environment = DOCKER_RAMDISK = yes
2019-08-14 19:18:38 +00:00
`
}
engineConfigTmpl += `
2016-10-27 21:50:36 +00:00
{ { range . EngineOptions . Env } } Environment = { { . } }
{ { end } }
2017-04-05 04:40:05 +00:00
2017-04-10 02:38:45 +00:00
# This file is a systemd drop - in unit that inherits from the base dockerd configuration .
# The base configuration already specifies an ' ExecStart = ... ' command . The first directive
# here is to clear out that command inherited from the base configuration . Without this ,
# the command from the base configuration and the command specified here are treated as
# a sequence of commands , which is not the desired behavior , nor is it valid -- systemd
# will catch this invalid input and refuse to start the service with an error like :
# Service has more than one ExecStart = setting , which is only allowed for Type = oneshot services .
2019-10-29 21:21:53 +00:00
# NOTE : default - ulimit = nofile is set to an arbitrary number for consistency with other
# container runtimes . If left unlimited , it may result in OOM issues with MySQL .
2017-04-05 04:40:05 +00:00
ExecStart =
2019-10-29 14:56:31 +00:00
ExecStart = / usr / bin / dockerd - H tcp : //0.0.0.0:{{.DockerPort}} -H unix:///var/run/docker.sock --default-ulimit=nofile=1048576:1048576 --tlsverify --tlscacert {{.AuthOptions.CaCertRemotePath}} --tlscert {{.AuthOptions.ServerCertRemotePath}} --tlskey {{.AuthOptions.ServerKeyRemotePath}} {{ range .EngineOptions.Labels }}--label {{.}} {{ end }}{{ range .EngineOptions.InsecureRegistry }}--insecure-registry {{.}} {{ end }}{{ range .EngineOptions.RegistryMirror }}--registry-mirror {{.}} {{ end }}{{ range .EngineOptions.ArbitraryFlags }}--{{.}} {{ end }}
2016-08-23 15:03:41 +00:00
ExecReload = / bin / kill - s HUP $ MAINPID
# Having non - zero Limit * s causes performance problems due to accounting overhead
# in the kernel . We recommend using cgroups to do container - local accounting .
2019-10-29 05:33:18 +00:00
LimitNOFILE = infinity
2016-08-23 15:03:41 +00:00
LimitNPROC = infinity
LimitCORE = infinity
# Uncomment TasksMax if your systemd version supports it .
# Only systemd 226 and above support this version .
TasksMax = infinity
TimeoutStartSec = 0
# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate = yes
# kill only the docker process , not all processes in the cgroup
KillMode = process
[ Install ]
WantedBy = multi - user . target
`
t , err := template . New ( "engineConfig" ) . Parse ( engineConfigTmpl )
if err != nil {
return nil , err
}
engineConfigContext := provision . EngineConfigContext {
DockerPort : dockerPort ,
AuthOptions : p . AuthOptions ,
EngineOptions : p . EngineOptions ,
}
2019-03-27 09:05:20 +00:00
escapeSystemdDirectives ( & engineConfigContext )
2017-10-16 19:17:57 +00:00
if err := t . Execute ( & engineCfg , engineConfigContext ) ; err != nil {
return nil , err
}
2016-08-23 15:03:41 +00:00
return & provision . DockerOptions {
EngineOptions : engineCfg . String ( ) ,
2017-07-10 18:50:21 +00:00
EngineOptionsPath : "/lib/systemd/system/docker.service" ,
2016-08-23 15:03:41 +00:00
} , nil
}
2019-08-14 19:18:38 +00:00
func rootFileSystemType ( p * BuildrootProvisioner ) ( string , error ) {
fs , err := p . SSHCommand ( "df --output=fstype / | tail -n 1" )
if err != nil {
return "" , err
}
return strings . TrimSpace ( fs ) , nil
}
2019-03-16 13:12:18 +00:00
// Package installs a package
2016-08-23 15:03:41 +00:00
func ( p * BuildrootProvisioner ) Package ( name string , action pkgaction . PackageAction ) error {
return nil
}
2019-03-16 13:12:18 +00:00
// Provision does the provisioning
2016-08-23 15:03:41 +00:00
func ( p * BuildrootProvisioner ) Provision ( swarmOptions swarm . Options , authOptions auth . Options , engineOptions engine . Options ) error {
p . SwarmOptions = swarmOptions
p . AuthOptions = authOptions
p . EngineOptions = engineOptions
log . Debugf ( "setting hostname %q" , p . Driver . GetMachineName ( ) )
if err := p . SetHostname ( p . Driver . GetMachineName ( ) ) ; err != nil {
return err
}
p . AuthOptions = setRemoteAuthOptions ( p )
log . Debugf ( "set auth options %+v" , p . AuthOptions )
log . Debugf ( "setting up certificates" )
2019-08-14 06:48:30 +00:00
configAuth := func ( ) error {
2017-07-10 18:50:21 +00:00
if err := configureAuth ( p ) ; err != nil {
2019-08-14 06:48:30 +00:00
return & retry . RetriableError { Err : err }
2016-11-16 19:50:08 +00:00
}
return nil
}
2019-08-14 06:48:30 +00:00
err := retry . Expo ( configAuth , time . Second , 2 * time . Minute )
2016-11-16 19:50:08 +00:00
if err != nil {
log . Debugf ( "Error configuring auth during provisioning %v" , err )
2016-08-23 15:03:41 +00:00
return err
}
2017-10-16 19:17:57 +00:00
log . Debugf ( "setting minikube options for container-runtime" )
if err := setMinikubeOptions ( p ) ; err != nil {
log . Debugf ( "Error setting container-runtime options during provisioning %v" , err )
return err
}
2016-08-23 15:03:41 +00:00
return nil
}
func setRemoteAuthOptions ( p provision . Provisioner ) auth . Options {
dockerDir := p . GetDockerOptionsDir ( )
authOptions := p . GetAuthOptions ( )
// due to windows clients, we cannot use filepath.Join as the paths
// will be mucked on the linux hosts
authOptions . CaCertRemotePath = path . Join ( dockerDir , "ca.pem" )
authOptions . ServerCertRemotePath = path . Join ( dockerDir , "server.pem" )
authOptions . ServerKeyRemotePath = path . Join ( dockerDir , "server-key.pem" )
return authOptions
}
2017-07-10 18:50:21 +00:00
2017-10-16 19:17:57 +00:00
func setMinikubeOptions ( p * BuildrootProvisioner ) error {
// pass through --insecure-registry
var (
crioOptsTmpl = `
CRIO_MINIKUBE_OPTIONS = ' { { range . EngineOptions . InsecureRegistry } } -- insecure - registry { { . } } { { end } } '
`
crioOptsPath = "/etc/sysconfig/crio.minikube"
)
t , err := template . New ( "crioOpts" ) . Parse ( crioOptsTmpl )
if err != nil {
return err
}
var crioOptsBuf bytes . Buffer
if err := t . Execute ( & crioOptsBuf , p ) ; err != nil {
return err
}
2018-02-15 17:00:14 +00:00
2017-10-16 19:17:57 +00:00
if _ , err = p . SSHCommand ( fmt . Sprintf ( "sudo mkdir -p %s && printf %%s \"%s\" | sudo tee %s" , path . Dir ( crioOptsPath ) , crioOptsBuf . String ( ) , crioOptsPath ) ) ; err != nil {
return err
}
2018-02-15 17:00:14 +00:00
// This is unlikely to cause issues unless the user has explicitly requested CRIO, so just log a warning.
2017-10-16 19:17:57 +00:00
if err := p . Service ( "crio" , serviceaction . Restart ) ; err != nil {
2018-09-28 23:05:27 +00:00
log . Warn ( "Unable to restart crio service. Error: %v" , err )
2017-10-16 19:17:57 +00:00
}
return nil
}
2017-07-10 18:50:21 +00:00
func configureAuth ( p * BuildrootProvisioner ) error {
driver := p . GetDriver ( )
machineName := driver . GetMachineName ( )
authOptions := p . GetAuthOptions ( )
org := mcnutils . GetUsername ( ) + "." + machineName
bits := 2048
ip , err := driver . GetIP ( )
if err != nil {
return errors . Wrap ( err , "error getting ip during provisioning" )
}
2019-03-16 17:59:00 +00:00
err = copyHostCerts ( authOptions )
if err != nil {
return err
2017-07-10 18:50:21 +00:00
}
// The Host IP is always added to the certificate's SANs list
hosts := append ( authOptions . ServerCertSANs , ip , "localhost" )
log . Debugf ( "generating server cert: %s ca-key=%s private-key=%s org=%s san=%s" ,
authOptions . ServerCertPath ,
authOptions . CaCertPath ,
authOptions . CaPrivateKeyPath ,
org ,
hosts ,
)
err = cert . GenerateCert ( & cert . Options {
Hosts : hosts ,
CertFile : authOptions . ServerCertPath ,
KeyFile : authOptions . ServerKeyPath ,
CAFile : authOptions . CaCertPath ,
CAKeyFile : authOptions . CaPrivateKeyPath ,
Org : org ,
Bits : bits ,
} )
if err != nil {
2018-09-28 23:05:27 +00:00
return fmt . Errorf ( "error generating server cert: %v" , err )
2017-07-10 18:50:21 +00:00
}
2019-03-16 17:59:00 +00:00
err = copyRemoteCerts ( authOptions , driver )
2017-07-10 18:50:21 +00:00
if err != nil {
2019-03-16 17:59:00 +00:00
return err
2017-07-10 18:50:21 +00:00
}
2019-12-09 22:46:44 +00:00
config , err := config . Load ( p . Driver . GetMachineName ( ) )
2018-12-07 20:08:59 +00:00
if err != nil {
return errors . Wrap ( err , "getting cluster config" )
}
2017-07-10 18:50:21 +00:00
dockerCfg , err := p . GenerateDockerOptions ( engine . DefaultPort )
if err != nil {
return errors . Wrap ( err , "generating docker options" )
}
log . Info ( "Setting Docker configuration on the remote daemon..." )
if _ , err = p . SSHCommand ( fmt . Sprintf ( "sudo mkdir -p %s && printf %%s \"%s\" | sudo tee %s" , path . Dir ( dockerCfg . EngineOptionsPath ) , dockerCfg . EngineOptions , dockerCfg . EngineOptionsPath ) ) ; err != nil {
return err
}
2019-11-12 01:52:15 +00:00
if config . ContainerRuntime == "" {
2018-01-08 21:19:24 +00:00
2019-01-20 14:22:37 +00:00
if err := p . Service ( "docker" , serviceaction . Enable ) ; err != nil {
return err
}
if err := p . Service ( "docker" , serviceaction . Restart ) ; err != nil {
return err
}
2017-07-10 18:50:21 +00:00
}
return nil
}
2019-03-16 17:59:00 +00:00
func copyHostCerts ( authOptions auth . Options ) error {
2019-07-04 23:31:07 +00:00
execRunner := & command . ExecRunner { }
2019-03-16 17:59:00 +00:00
hostCerts := map [ string ] string {
authOptions . CaCertPath : path . Join ( authOptions . StorePath , "ca.pem" ) ,
authOptions . ClientCertPath : path . Join ( authOptions . StorePath , "cert.pem" ) ,
authOptions . ClientKeyPath : path . Join ( authOptions . StorePath , "key.pem" ) ,
}
for src , dst := range hostCerts {
f , err := assets . NewFileAsset ( src , path . Dir ( dst ) , filepath . Base ( dst ) , "0777" )
if err != nil {
return errors . Wrapf ( err , "open cert file: %s" , src )
}
if err := execRunner . Copy ( f ) ; err != nil {
return errors . Wrapf ( err , "transferring file: %+v" , f )
}
}
return nil
}
func copyRemoteCerts ( authOptions auth . Options , driver drivers . Driver ) error {
remoteCerts := map [ string ] string {
authOptions . CaCertPath : authOptions . CaCertRemotePath ,
authOptions . ServerCertPath : authOptions . ServerCertRemotePath ,
authOptions . ServerKeyPath : authOptions . ServerKeyRemotePath ,
}
sshClient , err := sshutil . NewSSHClient ( driver )
if err != nil {
return errors . Wrap ( err , "provisioning: error getting ssh client" )
}
2019-07-04 23:31:07 +00:00
sshRunner := command . NewSSHRunner ( sshClient )
2019-03-16 17:59:00 +00:00
for src , dst := range remoteCerts {
f , err := assets . NewFileAsset ( src , path . Dir ( dst ) , filepath . Base ( dst ) , "0640" )
if err != nil {
return errors . Wrapf ( err , "error copying %s to %s" , src , dst )
}
if err := sshRunner . Copy ( f ) ; err != nil {
return errors . Wrapf ( err , "transferring file to machine %v" , f )
}
}
return nil
}