diff --git a/pkg/kubelet/rkt/config.go b/pkg/kubelet/rkt/config.go new file mode 100644 index 00000000000..33f59a54a8c --- /dev/null +++ b/pkg/kubelet/rkt/config.go @@ -0,0 +1,50 @@ +/* +Copyright 2015 Google Inc. 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 rkt + +import "fmt" + +// Config stores the global configuration for the rkt runtime. +// Run 'rkt' for more details. +type Config struct { + // The debug flag for rkt. + Debug bool + // The rkt data directory. + Dir string + // This flag controls whether we skip image or key verification. + InsecureSkipVerify bool + // The local config directory. + LocalConfigDir string +} + +// buildGlobalOptions returns an array of global command line options. +func (c *Config) buildGlobalOptions() []string { + var result []string + if c == nil { + return result + } + + result = append(result, fmt.Sprintf("--debug=%v", c.Debug)) + result = append(result, fmt.Sprintf("--insecure-skip-verify=%v", c.InsecureSkipVerify)) + if c.LocalConfigDir != "" { + result = append(result, fmt.Sprintf("--local-config=%s", c.LocalConfigDir)) + } + if c.Dir != "" { + result = append(result, fmt.Sprintf("--dir=%s", c.Dir)) + } + return result +} diff --git a/pkg/kubelet/rkt/rkt.go b/pkg/kubelet/rkt/rkt.go new file mode 100644 index 00000000000..2a4ac3e2589 --- /dev/null +++ b/pkg/kubelet/rkt/rkt.go @@ -0,0 +1,144 @@ +/* +Copyright 2015 Google Inc. 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 rkt + +import ( + "fmt" + "os/exec" + "strings" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/credentialprovider" + "github.com/coreos/go-systemd/dbus" + "github.com/golang/glog" +) + +const ( + acversion = "0.5.1" + rktMinimumVersion = "0.5.4" + systemdMinimumVersion = "215" + + systemdServiceDir = "/run/systemd/system" + rktDataDir = "/var/lib/rkt" + rktLocalConfigDir = "/etc/rkt" + rktMetadataServiceFile = "rkt-metadata.service" + rktMetadataSocketFile = "rkt-metadata.socket" + + kubernetesUnitPrefix = "k8s" + unitKubernetesSection = "X-Kubernetes" + unitPodName = "POD" + unitRktID = "RktID" + + dockerPrefix = "docker://" +) + +const ( + rktBinName = "rkt" + + Embryo = "embryo" + Preparing = "preparing" + AbortedPrepare = "aborted prepare" + Prepared = "prepared" + Running = "running" + Deleting = "deleting" // This covers pod.isExitedDeleting and pod.isDeleting. + Exited = "exited" // This covers pod.isExited and pod.isExitedGarbage. + Garbage = "garbage" +) + +const ( + dockerAuthTemplate = `{"rktKind":"dockerAuth","rktVersion":"v1","registries":[%q],"credentials":{"user":%q,"password":%q}}` +) + +// Runtime implements the ContainerRuntime for rkt. The implementation +// uses systemd, so in order to run this runtime, systemd must be installed +// on the machine. +type Runtime struct { + systemd *dbus.Conn + absPath string + config *Config + // TODO(yifan): Refactor this to be generic keyring. + dockerKeyring credentialprovider.DockerKeyring +} + +// New creates the rkt container runtime which implements the container runtime interface. +// It will test if the rkt binary is in the $PATH, and whether we can get the +// version of it. If so, creates the rkt container runtime, otherwise returns an error. +func New(config *Config) (*Runtime, error) { + systemdVersion, err := getSystemdVersion() + if err != nil { + return nil, err + } + result, err := systemdVersion.Compare(systemdMinimumVersion) + if err != nil { + return nil, err + } + if result < 0 { + return nil, fmt.Errorf("rkt: systemd version is too old, requires at least %v", systemdMinimumVersion) + } + + systemd, err := dbus.New() + if err != nil { + glog.Errorf("rkt: Cannot connect to dbus: %v", err) + return nil, err + } + + // Test if rkt binary is in $PATH. + absPath, err := exec.LookPath(rktBinName) + if err != nil { + glog.Errorf("rkt: Cannot find rkt binary: %v", err) + return nil, err + } + + rkt := &Runtime{ + systemd: systemd, + absPath: absPath, + config: config, + dockerKeyring: credentialprovider.NewDockerKeyring(), + } + + // Test the rkt version. + version, err := rkt.Version() + if err != nil { + return nil, err + } + result, err = version.Compare(rktMinimumVersion) + if err != nil { + return nil, err + } + if result < 0 { + return nil, fmt.Errorf("rkt: Version is too old, requires at least %v", rktMinimumVersion) + } + return rkt, nil +} + +func (r *Runtime) buildCommand(args ...string) *exec.Cmd { + cmd := exec.Command(rktBinName) + cmd.Args = append(cmd.Args, r.config.buildGlobalOptions()...) + cmd.Args = append(cmd.Args, args...) + return cmd +} + +// runCommand invokes rkt binary with arguments and returns the result +// from stdout in a list of strings. +func (r *Runtime) runCommand(args ...string) ([]string, error) { + glog.V(4).Info("rkt: Run command:", args) + + output, err := r.buildCommand(args...).Output() + if err != nil { + return nil, err + } + return strings.Split(strings.TrimSpace(string(output)), "\n"), nil +} diff --git a/pkg/kubelet/rkt/version.go b/pkg/kubelet/rkt/version.go new file mode 100644 index 00000000000..a8636852f1c --- /dev/null +++ b/pkg/kubelet/rkt/version.go @@ -0,0 +1,149 @@ +/* +Copyright 2015 Google Inc. 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 rkt + +import ( + "fmt" + "os/exec" + "strconv" + "strings" + + kubecontainer "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/container" + "github.com/golang/glog" +) + +type rktVersion []int + +func parseVersion(input string) (rktVersion, error) { + tail := strings.Index(input, "+") + if tail > 0 { + input = input[:tail] + } + var result rktVersion + tuples := strings.Split(input, ".") + for _, t := range tuples { + n, err := strconv.Atoi(t) + if err != nil { + return nil, err + } + result = append(result, n) + } + return result, nil +} + +func (r rktVersion) Compare(other string) (int, error) { + v, err := parseVersion(other) + if err != nil { + return -1, err + } + + for i := range r { + if i > len(v)-1 { + return 1, nil + } + if r[i] < v[i] { + return -1, nil + } + if r[i] > v[i] { + return 1, nil + } + } + + // When loop ends, len(r) is <= len(v). + if len(r) < len(v) { + return -1, nil + } + return 0, nil +} + +func (r rktVersion) String() string { + var version []string + for _, v := range r { + version = append(version, fmt.Sprintf("%d", v)) + } + return strings.Join(version, ".") +} + +// Version invokes 'rkt version' to get the version information of the rkt +// runtime on the machine. +// The return values are an int array containers the version number. +// +// Example: +// rkt:0.3.2+git --> []int{0, 3, 2}. +// +func (r *Runtime) Version() (kubecontainer.Version, error) { + output, err := r.runCommand("version") + if err != nil { + return nil, err + } + + // Example output for 'rkt version': + // rkt version 0.3.2+git + // appc version 0.3.0+git + for _, line := range output { + tuples := strings.Split(strings.TrimSpace(line), " ") + if len(tuples) != 3 { + glog.Warningf("rkt: cannot parse the output: %q.", line) + continue + } + if tuples[0] == "rkt" { + return parseVersion(tuples[2]) + } + } + return nil, fmt.Errorf("rkt: cannot determine the version") +} + +type systemdVersion int + +func (s systemdVersion) String() string { + return fmt.Sprintf("%d", s) +} + +func (s systemdVersion) Compare(other string) (int, error) { + v, err := strconv.Atoi(other) + if err != nil { + return -1, err + } + if int(s) < v { + return -1, nil + } else if int(s) > v { + return 1, nil + } + return 0, nil +} + +func getSystemdVersion() (systemdVersion, error) { + output, err := exec.Command("systemctl", "--version").Output() + if err != nil { + return -1, err + } + // Example output of 'systemctl --version': + // + // systemd 215 + // +PAM +AUDIT +SELINUX +IMA +SYSVINIT +LIBCRYPTSETUP +GCRYPT +ACL +XZ -SECCOMP -APPARMOR + // + lines := strings.Split(string(output), "\n") + tuples := strings.Split(lines[0], " ") + if len(tuples) != 2 { + return -1, fmt.Errorf("rkt: Failed to parse version %v", lines) + } + result, err := strconv.Atoi(string(tuples[1])) + if err != nil { + return -1, err + } + return systemdVersion(result), nil +}