CentOS7 initial experimental support

This commit is contained in:
Justin Santa Barbara 2016-10-06 23:23:30 -04:00
parent a6fd824994
commit d86390c172
36 changed files with 228 additions and 20 deletions

View File

@ -96,8 +96,9 @@ gcs-publish-ci: gcs-upload
echo "${GCS_URL}/${VERSION}" > .build/upload/${LATEST_FILE} echo "${GCS_URL}/${VERSION}" > .build/upload/${LATEST_FILE}
gsutil cp .build/upload/${LATEST_FILE} ${GCS_LOCATION} gsutil cp .build/upload/${LATEST_FILE} ${GCS_LOCATION}
push: nodeup-dist # Assumes running on linux for speed (todo: crossbuild on OSX?)
scp -C .build/dist/nodeup ${TARGET}:/tmp/ push: nodeup-gocode
scp -C ${GOPATH_1ST}/bin/nodeup ${TARGET}:/tmp/
push-gce-dry: push push-gce-dry: push
ssh ${TARGET} sudo SKIP_PACKAGE_UPDATE=1 /tmp/nodeup --conf=metadata://gce/config --dryrun --v=8 ssh ${TARGET} sudo SKIP_PACKAGE_UPDATE=1 /tmp/nodeup --conf=metadata://gce/config --dryrun --v=8
@ -108,8 +109,9 @@ push-aws-dry: push
push-gce-run: push push-gce-run: push
ssh ${TARGET} sudo SKIP_PACKAGE_UPDATE=1 /tmp/nodeup --conf=metadata://gce/config --v=8 ssh ${TARGET} sudo SKIP_PACKAGE_UPDATE=1 /tmp/nodeup --conf=metadata://gce/config --v=8
# -t is for CentOS http://unix.stackexchange.com/questions/122616/why-do-i-need-a-tty-to-run-sudo-if-i-can-sudo-without-a-password
push-aws-run: push push-aws-run: push
ssh ${TARGET} sudo SKIP_PACKAGE_UPDATE=1 /tmp/nodeup --conf=/var/cache/kubernetes-install/kube_env.yaml --v=8 ssh -t ${TARGET} sudo SKIP_PACKAGE_UPDATE=1 /tmp/nodeup --conf=/var/cache/kubernetes-install/kube_env.yaml --v=8

View File

@ -29,3 +29,14 @@ which should be easier than editing your instance groups.
In addition, we support a few-well known aliases for the owner: In addition, we support a few-well known aliases for the owner:
`kope.io` => `383156758163` `kope.io` => `383156758163`
## Centos
CentOS7 support is still experimental.
The following steps are known:
* Accept the agreement at http://aws.amazon.com/marketplace/pp?sku=aw0evgkw8e5c1q413zgy5pjce
* Specify the AMI by id (there are no tags): us-east-1: ami-6d1c2007

View File

@ -49,6 +49,9 @@
"readOnly": true}, "readOnly": true},
{ "name": "etcpkitls", { "name": "etcpkitls",
"mountPath": "/etc/pki/tls", "mountPath": "/etc/pki/tls",
"readOnly": true},
{ "name": "etcpkicatrust",
"mountPath": "/etc/pki/ca-trust",
"readOnly": true} "readOnly": true}
] ]
} }
@ -79,6 +82,10 @@
{ "name": "etcpkitls", { "name": "etcpkitls",
"hostPath": { "hostPath": {
"path": "/etc/pki/tls"} "path": "/etc/pki/tls"}
},
{ "name": "etcpkicatrust",
"hostPath": {
"path": "/etc/pki/ca-trust"}
} }
] ]
}} }}

View File

@ -0,0 +1,22 @@
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network.target docker.socket
Requires=docker.socket
[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/docker
ExecStart=/usr/bin/docker daemon -H fd:// "$DOCKER_OPTS"
MountFlags=slave
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity
Restart=always
RestartSec=2s
StartLimitInterval=0
# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,2 @@
DOCKER_OPTS="{{ BuildFlags .Docker }}"
DOCKER_NOFILE=1000000

View File

@ -0,0 +1,5 @@
{
"version": "1.11.2",
"source": "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-1.11.2-1.el7.centos.x86_64.rpm",
"hash": "432e6d7948df9e05f4190fce2f423eedbfd673d5"
}

View File

@ -0,0 +1,5 @@
{
"version": "1.11.2",
"source": "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-selinux-1.11.2-1.el7.centos.noarch.rpm",
"hash": "f6da608fa8eeb2be8071489086ed9ff035f6daba"
}

View File

@ -14,6 +14,7 @@ import (
type CloudInitTarget struct { type CloudInitTarget struct {
Config *CloudConfig Config *CloudConfig
out io.Writer out io.Writer
Tags map[string]struct{}
} }
type AddBehaviour int type AddBehaviour int
@ -23,10 +24,11 @@ const (
Once Once
) )
func NewCloudInitTarget(out io.Writer) *CloudInitTarget { func NewCloudInitTarget(out io.Writer, tags map[string]struct{}) *CloudInitTarget {
t := &CloudInitTarget{ t := &CloudInitTarget{
Config: &CloudConfig{}, Config: &CloudConfig{},
out: out, out: out,
Tags: tags,
} }
return t return t
} }
@ -49,6 +51,11 @@ type CloudConfigFile struct {
Content string `json:"content,omitempty"` Content string `json:"content,omitempty"`
} }
func (t *CloudInitTarget) HasTag(tag string) bool {
_, found := t.Tags[tag]
return found
}
func (t *CloudInitTarget) AddMkdirpCommand(p string, dirMode os.FileMode) { func (t *CloudInitTarget) AddMkdirpCommand(p string, dirMode os.FileMode) {
t.AddCommand(Once, "mkdir", "-p", "-m", fi.FileModeToString(dirMode), p) t.AddCommand(Once, "mkdir", "-p", "-m", fi.FileModeToString(dirMode), p)

View File

@ -198,12 +198,13 @@ func (c *NodeUpCommand) Run(out io.Writer) error {
case "direct": case "direct":
target = &local.LocalTarget{ target = &local.LocalTarget{
CacheDir: c.CacheDir, CacheDir: c.CacheDir,
Tags: tags,
} }
case "dryrun": case "dryrun":
target = fi.NewDryRunTarget(out) target = fi.NewDryRunTarget(out)
case "cloudinit": case "cloudinit":
checkExisting = false checkExisting = false
target = cloudinit.NewCloudInitTarget(out) target = cloudinit.NewCloudInitTarget(out, tags)
default: default:
return fmt.Errorf("unsupported target type %q", c.Target) return fmt.Errorf("unsupported target type %q", c.Target)
} }

View File

@ -4,6 +4,7 @@ import "k8s.io/kops/upup/pkg/fi"
type LocalTarget struct { type LocalTarget struct {
CacheDir string CacheDir string
Tags map[string]struct{}
} }
var _ fi.Target = &LocalTarget{} var _ fi.Target = &LocalTarget{}
@ -11,3 +12,8 @@ var _ fi.Target = &LocalTarget{}
func (t *LocalTarget) Finish(taskMap map[string]fi.Task) error { func (t *LocalTarget) Finish(taskMap map[string]fi.Task) error {
return nil return nil
} }
func (t *LocalTarget) HasTag(tag string) bool {
_, found := t.Tags[tag]
return found
}

View File

@ -7,6 +7,7 @@ import (
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/nodeup/cloudinit" "k8s.io/kops/upup/pkg/fi/nodeup/cloudinit"
"k8s.io/kops/upup/pkg/fi/nodeup/local" "k8s.io/kops/upup/pkg/fi/nodeup/local"
"k8s.io/kops/upup/pkg/fi/nodeup/tags"
"k8s.io/kops/util/pkg/hashing" "k8s.io/kops/util/pkg/hashing"
"os" "os"
"os/exec" "os/exec"
@ -87,6 +88,20 @@ func NewPackage(name string, contents string, meta string) (fi.Task, error) {
} }
func (e *Package) Find(c *fi.Context) (*Package, error) { func (e *Package) Find(c *fi.Context) (*Package, error) {
target := c.Target.(*local.LocalTarget)
if target.HasTag(tags.TagOSFamilyDebian) {
return e.findDpkg(c)
}
if target.HasTag(tags.TagOSFamilyCentos) {
return e.findYum(c)
}
return nil, fmt.Errorf("unsupported package system")
}
func (e *Package) findDpkg(c *fi.Context) (*Package, error) {
args := []string{"dpkg-query", "-f", "${db:Status-Abbrev}${Version}\\n", "-W", e.Name} args := []string{"dpkg-query", "-f", "${db:Status-Abbrev}${Version}\\n", "-W", e.Name}
human := strings.Join(args, " ") human := strings.Join(args, " ")
@ -146,6 +161,54 @@ func (e *Package) Find(c *fi.Context) (*Package, error) {
}, nil }, nil
} }
func (e *Package) findYum(c *fi.Context) (*Package, error) {
args := []string{"/usr/bin/rpm", "-q", e.Name, "--queryformat", "%{NAME} %{VERSION}"}
human := strings.Join(args, " ")
glog.V(2).Infof("Listing installed packages: %s", human)
cmd := exec.Command(args[0], args[1:]...)
output, err := cmd.CombinedOutput()
if err != nil {
if strings.Contains(string(output), "is not installed") {
return nil, nil
}
return nil, fmt.Errorf("error listing installed packages: %v: %s", err, string(output))
}
installed := false
var healthy *bool
installedVersion := ""
for _, line := range strings.Split(string(output), "\n") {
if line == "" {
continue
}
tokens := strings.Split(line, " ")
if len(tokens) != 2 {
return nil, fmt.Errorf("error parsing rpm line %q", line)
}
name := tokens[0]
if name != e.Name {
return nil, fmt.Errorf("error parsing rpm line %q", line)
}
installed = true
installedVersion = tokens[1]
// If we implement unhealthy; be sure to implement repair in Render
healthy = fi.Bool(true)
}
if !installed {
return nil, nil
}
return &Package{
Name: e.Name,
Version: fi.String(installedVersion),
Healthy: healthy,
}, nil
}
func (e *Package) Run(c *fi.Context) error { func (e *Package) Run(c *fi.Context) error {
return fi.DefaultDeltaRunMethod(e, c) return fi.DefaultDeltaRunMethod(e, c)
} }
@ -186,7 +249,14 @@ func (_ *Package) RenderLocal(t *local.LocalTarget, a, e, changes *Package) erro
return err return err
} }
args := []string{"dpkg", "-i", local} var args []string
if t.HasTag(tags.TagOSFamilyDebian) {
args = []string{"dpkg", "-i", local}
} else if t.HasTag(tags.TagOSFamilyCentos) {
args = []string{"/usr/bin/rpm", "-i", local}
} else {
return fmt.Errorf("unsupported package system")
}
glog.Infof("running command %s", args) glog.Infof("running command %s", args)
cmd := exec.Command(args[0], args[1:]...) cmd := exec.Command(args[0], args[1:]...)
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
@ -194,7 +264,16 @@ func (_ *Package) RenderLocal(t *local.LocalTarget, a, e, changes *Package) erro
return fmt.Errorf("error installing package %q: %v: %s", e.Name, err, string(output)) return fmt.Errorf("error installing package %q: %v: %s", e.Name, err, string(output))
} }
} else { } else {
args := []string{"apt-get", "install", "--yes", e.Name} var args []string
if t.HasTag(tags.TagOSFamilyDebian) {
args = []string{"apt-get", "install", "--yes", e.Name}
} else if t.HasTag(tags.TagOSFamilyCentos) {
args = []string{"/usr/bin/yum", "install", "-y", e.Name}
} else {
return fmt.Errorf("unsupported package system")
}
glog.Infof("running command %s", args) glog.Infof("running command %s", args)
cmd := exec.Command(args[0], args[1:]...) cmd := exec.Command(args[0], args[1:]...)
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
@ -204,15 +283,22 @@ func (_ *Package) RenderLocal(t *local.LocalTarget, a, e, changes *Package) erro
} }
} else { } else {
if changes.Healthy != nil { if changes.Healthy != nil {
args := []string{"dpkg", "--configure", "-a"} if t.HasTag(tags.TagOSFamilyDebian) {
glog.Infof("package is not healthy; runnning command %s", args) args := []string{"dpkg", "--configure", "-a"}
cmd := exec.Command(args[0], args[1:]...) glog.Infof("package is not healthy; runnning command %s", args)
output, err := cmd.CombinedOutput() cmd := exec.Command(args[0], args[1:]...)
if err != nil { output, err := cmd.CombinedOutput()
return fmt.Errorf("error running `dpkg --configure -a`: %v: %s", err, string(output)) if err != nil {
} return fmt.Errorf("error running `dpkg --configure -a`: %v: %s", err, string(output))
}
changes.Healthy = nil changes.Healthy = nil
} else if t.HasTag(tags.TagOSFamilyCentos) {
// We can't reach here anyway...
return fmt.Errorf("package repair not supported on centos")
} else {
return fmt.Errorf("unsupported package system")
}
} }
if !reflect.DeepEqual(changes, &Package{}) { if !reflect.DeepEqual(changes, &Package{}) {

View File

@ -8,6 +8,7 @@ import (
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/nodeup/cloudinit" "k8s.io/kops/upup/pkg/fi/nodeup/cloudinit"
"k8s.io/kops/upup/pkg/fi/nodeup/local" "k8s.io/kops/upup/pkg/fi/nodeup/local"
"k8s.io/kops/upup/pkg/fi/nodeup/tags"
"k8s.io/kops/upup/pkg/fi/utils" "k8s.io/kops/upup/pkg/fi/utils"
"os" "os"
"os/exec" "os/exec"
@ -18,7 +19,8 @@ import (
) )
const ( const (
systemdSystemPath = "/lib/systemd/system" // TODO: Different on redhat debianSystemdSystemPath = "/lib/systemd/system"
centosSystemdSystemPath = "/usr/lib/systemd/system"
) )
type Service struct { type Service struct {
@ -109,7 +111,22 @@ func getSystemdStatus(name string) (map[string]string, error) {
return properties, nil return properties, nil
} }
func (e *Service) systemdSystemPath(target tags.HasTags) (string, error) {
if target.HasTag(tags.TagOSFamilyDebian) {
return debianSystemdSystemPath, nil
} else if target.HasTag(tags.TagOSFamilyCentos) {
return centosSystemdSystemPath, nil
} else {
return "", fmt.Errorf("unsupported systemd system")
}
}
func (e *Service) Find(c *fi.Context) (*Service, error) { func (e *Service) Find(c *fi.Context) (*Service, error) {
systemdSystemPath, err := e.systemdSystemPath(c.Target.(tags.HasTags))
if err != nil {
return nil, err
}
servicePath := path.Join(systemdSystemPath, e.Name) servicePath := path.Join(systemdSystemPath, e.Name)
d, err := ioutil.ReadFile(servicePath) d, err := ioutil.ReadFile(servicePath)
@ -203,6 +220,11 @@ func (s *Service) CheckChanges(a, e, changes *Service) error {
} }
func (_ *Service) RenderLocal(t *local.LocalTarget, a, e, changes *Service) error { func (_ *Service) RenderLocal(t *local.LocalTarget, a, e, changes *Service) error {
systemdSystemPath, err := e.systemdSystemPath(t)
if err != nil {
return err
}
serviceName := e.Name serviceName := e.Name
action := "" action := ""
@ -311,10 +333,15 @@ func (_ *Service) RenderLocal(t *local.LocalTarget, a, e, changes *Service) erro
} }
func (_ *Service) RenderCloudInit(t *cloudinit.CloudInitTarget, a, e, changes *Service) error { func (_ *Service) RenderCloudInit(t *cloudinit.CloudInitTarget, a, e, changes *Service) error {
systemdSystemPath, err := e.systemdSystemPath(t)
if err != nil {
return err
}
serviceName := e.Name serviceName := e.Name
servicePath := path.Join(systemdSystemPath, serviceName) servicePath := path.Join(systemdSystemPath, serviceName)
err := t.WriteFile(servicePath, fi.NewStringResource(*e.Definition), 0644, 0755) err = t.WriteFile(servicePath, fi.NewStringResource(*e.Definition), 0644, 0755)
if err != nil { if err != nil {
return err return err
} }

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/golang/glog" "github.com/golang/glog"
"io/ioutil" "io/ioutil"
"k8s.io/kops/upup/pkg/fi/nodeup/tags"
"os" "os"
"path" "path"
"strings" "strings"
@ -18,7 +19,7 @@ func FindOSTags(rootfs string) ([]string, error) {
for _, line := range strings.Split(string(lsbRelease), "\n") { for _, line := range strings.Split(string(lsbRelease), "\n") {
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
if line == "DISTRIB_CODENAME=xenial" { if line == "DISTRIB_CODENAME=xenial" {
return []string{"_xenial", "_debian_family", "_systemd"}, nil return []string{"_xenial", tags.TagOSFamilyDebian, tags.TagSystemd}, nil
} }
} }
glog.Warningf("unhandled lsb-release info %q", string(lsbRelease)) glog.Warningf("unhandled lsb-release info %q", string(lsbRelease))
@ -31,7 +32,7 @@ func FindOSTags(rootfs string) ([]string, error) {
if err == nil { if err == nil {
debianVersion := strings.TrimSpace(string(debianVersionBytes)) debianVersion := strings.TrimSpace(string(debianVersionBytes))
if strings.HasPrefix(debianVersion, "8.") { if strings.HasPrefix(debianVersion, "8.") {
return []string{"_jessie", "_debian_family", "_systemd"}, nil return []string{"_jessie", tags.TagOSFamilyDebian, tags.TagSystemd}, nil
} else { } else {
return nil, fmt.Errorf("unhandled debian version %q", debianVersion) return nil, fmt.Errorf("unhandled debian version %q", debianVersion)
} }
@ -39,6 +40,20 @@ func FindOSTags(rootfs string) ([]string, error) {
glog.Warningf("error reading /etc/debian_version: %v", err) glog.Warningf("error reading /etc/debian_version: %v", err)
} }
// Centos has /etc/centos-release
centosRelease, err := ioutil.ReadFile(path.Join(rootfs, "etc/centos-release"))
if err == nil {
for _, line := range strings.Split(string(centosRelease), "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "CentOS Linux release 7.") {
return []string{"_centos7", tags.TagOSFamilyCentos, tags.TagSystemd}, nil
}
}
glog.Warningf("unhandled centos-release info %q", string(lsbRelease))
} else if !os.IsNotExist(err) {
glog.Warningf("error reading /etc/centos-release: %v", err)
}
return nil, fmt.Errorf("cannot identify distro") return nil, fmt.Errorf("cannot identify distro")
} }

View File

@ -0,0 +1,12 @@
package tags
const (
TagOSFamilyCentos = "_centos_family"
TagOSFamilyDebian = "_debian_family"
TagSystemd = "_systemd"
)
type HasTags interface {
HasTag(tag string) bool
}