Merge pull request #11852 from hakman/hooks-containerd

Handle containerExec hooks when using containerd
This commit is contained in:
Kubernetes Prow Robot 2021-06-23 23:27:40 -07:00 committed by GitHub
commit 0e4d766deb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 271 additions and 6 deletions

View File

@ -88,6 +88,7 @@ go_test(
"containerd_test.go",
"docker_test.go",
"fakes_test.go",
"hooks_test.go",
"kops_controller_test.go",
"kube_apiserver_test.go",
"kube_controller_manager_test.go",

View File

@ -36,11 +36,11 @@ func b(v bool) *bool {
return fi.Bool(v)
}
// buildDockerEnvironmentVars just converts a series of keypairs to docker environment variables switches
func buildDockerEnvironmentVars(env map[string]string) []string {
// buildContainerRuntimeEnvironmentVars just converts a series of keypairs to docker environment variables switches
func buildContainerRuntimeEnvironmentVars(env map[string]string) []string {
var list []string
for k, v := range env {
list = append(list, []string{"-e", fmt.Sprintf("%s=%s", k, v)}...)
list = append(list, []string{"--env", fmt.Sprintf("%s=%s", k, v)}...)
}
return list

View File

@ -124,8 +124,17 @@ func (h *HookBuilder) buildSystemdService(name string, hook *kops.HookSpec) (*no
case nil:
unit.SetSection("Service", hook.Manifest)
default:
if err := h.buildDockerService(unit, hook); err != nil {
return nil, err
switch h.Cluster.Spec.ContainerRuntime {
case "containerd":
if err := h.buildContainerdService(unit, hook, name); err != nil {
return nil, err
}
case "docker":
if err := h.buildDockerService(unit, hook); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unknown container runtime %q", h.Cluster.Spec.ContainerRuntime)
}
}
definition = s(unit.Render())
@ -141,6 +150,41 @@ func (h *HookBuilder) buildSystemdService(name string, hook *kops.HookSpec) (*no
return service, nil
}
// buildContainerdService is responsible for generating a containerd exec unit file
func (h *HookBuilder) buildContainerdService(unit *systemd.Manifest, hook *kops.HookSpec, name string) error {
containerdImage := hook.ExecContainer.Image
if !strings.Contains(containerdImage, "/") {
containerdImage = "docker.io/library/" + containerdImage
}
if !strings.Contains(containerdImage, ":") {
containerdImage = containerdImage + ":latest"
}
containerdArgs := []string{
"/usr/bin/ctr", "--namespace", "k8s.io", "run", "--rm",
"--mount", "type=bind,src=/,dst=/rootfs,options=rbind:rslave",
"--mount", "type=bind,src=/var/run/dbus,dst=/var/run/dbus,options=rbind:rprivate",
"--mount", "type=bind,src=/run/systemd,dst=/run/systemd,options=rbind:rprivate",
"--net-host",
"--privileged",
}
containerdArgs = append(containerdArgs, buildContainerRuntimeEnvironmentVars(hook.ExecContainer.Environment)...)
containerdArgs = append(containerdArgs, containerdImage)
containerdArgs = append(containerdArgs, name)
containerdArgs = append(containerdArgs, hook.ExecContainer.Command...)
containerdRunCommand := systemd.EscapeCommand(containerdArgs)
containerdPullCommand := systemd.EscapeCommand([]string{"/usr/bin/ctr", "--namespace", "k8s.io", "image", "pull", containerdImage})
unit.Set("Unit", "Requires", "containerd.service")
unit.Set("Service", "ExecStartPre", containerdPullCommand)
unit.Set("Service", "ExecStart", containerdRunCommand)
unit.Set("Service", "Type", "oneshot")
unit.Set("Install", "WantedBy", "multi-user.target")
return nil
}
// buildDockerService is responsible for generating a docker exec unit file
func (h *HookBuilder) buildDockerService(unit *systemd.Manifest, hook *kops.HookSpec) error {
dockerArgs := []string{
@ -151,7 +195,7 @@ func (h *HookBuilder) buildDockerService(unit *systemd.Manifest, hook *kops.Hook
"--net=host",
"--privileged",
}
dockerArgs = append(dockerArgs, buildDockerEnvironmentVars(hook.ExecContainer.Environment)...)
dockerArgs = append(dockerArgs, buildContainerRuntimeEnvironmentVars(hook.ExecContainer.Environment)...)
dockerArgs = append(dockerArgs, hook.ExecContainer.Image)
dockerArgs = append(dockerArgs, hook.ExecContainer.Command...)

View File

@ -0,0 +1,37 @@
/*
Copyright 2020 The Kubernetes Authors.
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 model
import (
"testing"
"k8s.io/kops/upup/pkg/fi"
)
func TestContainerdHooksBuilder(t *testing.T) {
RunGoldenTest(t, "tests/golden/hooks-containerd-exec", "hooks", func(nodeupModelContext *NodeupModelContext, target *fi.ModelBuilderContext) error {
builder := HookBuilder{NodeupModelContext: nodeupModelContext}
return builder.Build(target)
})
}
func TestDockerHooksBuilder(t *testing.T) {
RunGoldenTest(t, "tests/golden/hooks-docker-exec", "hooks", func(nodeupModelContext *NodeupModelContext, target *fi.ModelBuilderContext) error {
builder := HookBuilder{NodeupModelContext: nodeupModelContext}
return builder.Build(target)
})
}

View File

@ -0,0 +1,74 @@
apiVersion: kops.k8s.io/v1alpha2
kind: Cluster
metadata:
name: minimal.example.com
spec:
kubernetesApiAccess:
- 0.0.0.0/0
channel: stable
cloudProvider: aws
configBase: memfs://clusters.example.com/minimal.example.com
etcdClusters:
- cpuRequest: 200m
etcdMembers:
- instanceGroup: master-us-test-1a
name: us-test-1a
memoryRequest: 100Mi
name: main
provider: Manager
backups:
backupStore: memfs://clusters.example.com/minimal.example.com/backups/etcd-main
- cpuRequest: 100m
etcdMembers:
- instanceGroup: master-us-test-1a
name: us-test-1a
memoryRequest: 100Mi
name: events
provider: Manager
backups:
backupStore: memfs://clusters.example.com/minimal.example.com/backups/etcd-events
iam: {}
kubelet:
anonymousAuth: false
kubernetesVersion: v1.22.0
masterInternalName: api.internal.minimal.example.com
masterPublicName: api.minimal.example.com
networkCIDR: 172.20.0.0/16
networking:
kubenet: {}
nonMasqueradeCIDR: 100.64.0.0/10
sshAccess:
- 0.0.0.0/0
topology:
masters: public
nodes: public
subnets:
- cidr: 172.20.32.0/19
name: us-test-1a
type: Public
zone: us-test-1a
hooks:
- execContainer:
command:
- sh
- -c
- chroot /rootfs apt-get update && chroot /rootfs apt-get install -y ceph-common
image: busybox
---
apiVersion: kops.k8s.io/v1alpha2
kind: InstanceGroup
metadata:
name: master-us-test-1a
labels:
kops.k8s.io/cluster: minimal.example.com
spec:
associatePublicIp: true
image: ami-1234
machineType: m3.medium
maxSize: 1
minSize: 1
role: Master
subnets:
- us-test-1a

View File

@ -0,0 +1,17 @@
Name: kops-hook-0.service
definition: |
[Unit]
Description=Kops Hook kops-hook-0
Requires=containerd.service
[Service]
ExecStartPre=/usr/bin/ctr --namespace k8s.io image pull docker.io/library/busybox:latest
ExecStart=/usr/bin/ctr --namespace k8s.io run --rm --mount type=bind,src=/,dst=/rootfs,options=rbind:rslave --mount type=bind,src=/var/run/dbus,dst=/var/run/dbus,options=rbind:rprivate --mount type=bind,src=/run/systemd,dst=/run/systemd,options=rbind:rprivate --net-host --privileged docker.io/library/busybox:latest kops-hook-0 sh -c "chroot /rootfs apt-get update && chroot /rootfs apt-get install -y ceph-common"
Type=oneshot
[Install]
WantedBy=multi-user.target
enabled: true
manageState: true
running: true
smartRestart: true

View File

@ -0,0 +1,75 @@
apiVersion: kops.k8s.io/v1alpha2
kind: Cluster
metadata:
name: minimal.example.com
spec:
kubernetesApiAccess:
- 0.0.0.0/0
channel: stable
cloudProvider: aws
configBase: memfs://clusters.example.com/minimal.example.com
etcdClusters:
- cpuRequest: 200m
etcdMembers:
- instanceGroup: master-us-test-1a
name: us-test-1a
memoryRequest: 100Mi
name: main
provider: Manager
backups:
backupStore: memfs://clusters.example.com/minimal.example.com/backups/etcd-main
- cpuRequest: 100m
etcdMembers:
- instanceGroup: master-us-test-1a
name: us-test-1a
memoryRequest: 100Mi
name: events
provider: Manager
backups:
backupStore: memfs://clusters.example.com/minimal.example.com/backups/etcd-events
iam: {}
kubelet:
anonymousAuth: false
kubernetesVersion: v1.22.0
masterInternalName: api.internal.minimal.example.com
masterPublicName: api.minimal.example.com
networkCIDR: 172.20.0.0/16
networking:
kubenet: {}
nonMasqueradeCIDR: 100.64.0.0/10
sshAccess:
- 0.0.0.0/0
topology:
masters: public
nodes: public
subnets:
- cidr: 172.20.32.0/19
name: us-test-1a
type: Public
zone: us-test-1a
containerRuntime: docker
hooks:
- execContainer:
command:
- sh
- -c
- chroot /rootfs apt-get update && chroot /rootfs apt-get install -y ceph-common
image: busybox
---
apiVersion: kops.k8s.io/v1alpha2
kind: InstanceGroup
metadata:
name: master-us-test-1a
labels:
kops.k8s.io/cluster: minimal.example.com
spec:
associatePublicIp: true
image: ami-1234
machineType: m3.medium
maxSize: 1
minSize: 1
role: Master
subnets:
- us-test-1a

View File

@ -0,0 +1,17 @@
Name: kops-hook-0.service
definition: |
[Unit]
Description=Kops Hook kops-hook-0
Requires=docker.service
[Service]
ExecStartPre=/usr/bin/docker pull busybox
ExecStart=/usr/bin/docker run -v /:/rootfs/ -v /var/run/dbus:/var/run/dbus -v /run/systemd:/run/systemd --net=host --privileged busybox sh -c "chroot /rootfs apt-get update && chroot /rootfs apt-get install -y ceph-common"
Type=oneshot
[Install]
WantedBy=multi-user.target
enabled: true
manageState: true
running: true
smartRestart: true