Skip to content

Commit

Permalink
Change logic in template to use a list of plugin manifests
Browse files Browse the repository at this point in the history
Currently, the template for the initial YAML (via sonobuoy gen)
has it hardcoded that both the e2e and systemd-logs plugin are
configured. This is 1) misleading to users who might see this and
think both are going to be run and 2) brittle.

With upcoming changes wanting to make the plugin list more
variable, it makes sense to put this change first: go ahead and
track a plugin list and adjust the template to iterate over the
plugins, inserting their YAML each time.

This makes the YAML more clear, flexible, and in some cases shorter.

Fixes #672

Signed-off-by: John Schnake <jschnake@vmware.com>
  • Loading branch information
johnSchnake committed Apr 16, 2019
1 parent 0f8ab46 commit 8f123df
Show file tree
Hide file tree
Showing 8 changed files with 555 additions and 143 deletions.
173 changes: 173 additions & 0 deletions pkg/client/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,18 @@ import (
"github.com/pkg/errors"

"github.com/heptio/sonobuoy/pkg/buildinfo"
"github.com/heptio/sonobuoy/pkg/plugin"
"github.com/heptio/sonobuoy/pkg/plugin/manifest"
"github.com/heptio/sonobuoy/pkg/templates"

corev1 "k8s.io/api/core/v1"
kuberuntime "k8s.io/apimachinery/pkg/runtime"
)

const (
e2ePluginName = "e2e"
systemdLogsName = "systemd-logs"
pluginResultsDir = "/tmp/results"
)

// templateValues are used for direct template substitution for manifest generation.
Expand All @@ -35,6 +46,8 @@ type templateValues struct {
E2ESkip string
E2EParallel string

Plugins []string

SonobuoyConfig string
SonobuoyImage string
Version string
Expand Down Expand Up @@ -67,6 +80,23 @@ func (*SonobuoyClient) GenerateManifest(cfg *GenConfig) ([]byte, error) {
}
}

plugins := []*manifest.Manifest{}
if includes(cfg.Config.PluginSelections, e2ePluginName) {
plugins = append(plugins, e2eManifest(cfg))
}
if includes(cfg.Config.PluginSelections, systemdLogsName) {
plugins = append(plugins, systemdLogsManifest(cfg))
}

pluginYAML := []string{}
for _, v := range plugins {
yaml, err := kuberuntime.Encode(manifest.Encoder, v)
if err != nil {
return nil, errors.Wrapf(err, "serializing plugin %v as YAML", v.SonobuoyConfig.PluginName)
}
pluginYAML = append(pluginYAML, strings.TrimSpace(string(yaml)))
}

// Template values that are regexps (`E2EFocus` and `E2ESkip`) are
// embedded in YAML files using single quotes to remove the need to
// escape characters e.g. `\` as they would be if using double quotes.
Expand All @@ -89,6 +119,8 @@ func (*SonobuoyClient) GenerateManifest(cfg *GenConfig) ([]byte, error) {
SSHKey: base64.StdEncoding.EncodeToString(sshKeyData),
SSHUser: cfg.SSHUser,

Plugins: pluginYAML,

// Often created from reading a file, this value could have trailing newline.
CustomRegistries: strings.TrimSpace(cfg.E2EConfig.CustomRegistries),
}
Expand All @@ -101,3 +133,144 @@ func (*SonobuoyClient) GenerateManifest(cfg *GenConfig) ([]byte, error) {

return buf.Bytes(), nil
}

func includes(set []plugin.Selection, s string) bool {
for _, v := range set {
if s == v.Name {
return true
}
}
return false
}

func systemdLogsManifest(cfg *GenConfig) *manifest.Manifest {
trueVal := true
return &manifest.Manifest{
SonobuoyConfig: manifest.SonobuoyConfig{
PluginName: "systemd-logs",
Driver: "DaemonSet",
ResultType: "systemd-logs",
},
Spec: manifest.Container{
Container: corev1.Container{
Name: "systemd-logs",
Image: "gcr.io/heptio-images/sonobuoy-plugin-systemd-logs:latest",
Command: []string{"/bin/sh", "-c", "/get_systemd_logs.sh && sleep 3600"},
ImagePullPolicy: corev1.PullPolicy(cfg.ImagePullPolicy),
Env: []corev1.EnvVar{
corev1.EnvVar{Name: "CHROOT_DIR", Value: "/node"},
corev1.EnvVar{Name: "RESULTS_DIR", Value: pluginResultsDir},
corev1.EnvVar{Name: "NODE_NAME",
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{FieldPath: "spec.nodeName"},
},
},
},
SecurityContext: &corev1.SecurityContext{
Privileged: &trueVal,
},
VolumeMounts: []corev1.VolumeMount{
corev1.VolumeMount{
ReadOnly: false,
Name: "results",
MountPath: pluginResultsDir,
}, corev1.VolumeMount{
ReadOnly: false,
Name: "root",
MountPath: "/node",
},
},
},
},
}
}

func e2eManifest(cfg *GenConfig) *manifest.Manifest {
m := &manifest.Manifest{
SonobuoyConfig: manifest.SonobuoyConfig{
PluginName: "e2e",
Driver: "Job",
ResultType: "e2e",
},
Spec: manifest.Container{
Container: corev1.Container{
Name: "e2e",
Image: cfg.KubeConformanceImage,
Command: []string{"/run_e2e.sh"},
ImagePullPolicy: corev1.PullPolicy(cfg.ImagePullPolicy),
Env: []corev1.EnvVar{
corev1.EnvVar{Name: "E2E_FOCUS", Value: cfg.E2EConfig.Focus},
corev1.EnvVar{Name: "E2E_SKIP", Value: cfg.E2EConfig.Skip},
corev1.EnvVar{Name: "E2E_PARALLEL", Value: cfg.E2EConfig.Parallel},
},
VolumeMounts: []corev1.VolumeMount{
corev1.VolumeMount{
ReadOnly: false,
Name: "results",
MountPath: pluginResultsDir,
},
},
},
},
}

// Add volume mount, volume, and env var for custom registries.
if len(cfg.E2EConfig.CustomRegistries) > 0 {
m.ExtraVolumes = append(m.ExtraVolumes, manifest.Volume{
Volume: corev1.Volume{
Name: "repolist-vol",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{Name: "repolist-cm"},
},
},
},
})
m.Spec.Env = append(m.Spec.Env, corev1.EnvVar{
Name: "KUBE_TEST_REPO_LIST", Value: "/tmp/sonobuoy/repo-list.yaml",
})
m.Spec.VolumeMounts = append(m.Spec.VolumeMounts,
corev1.VolumeMount{
ReadOnly: false,
Name: "repolist-vol",
MountPath: "/tmp/sonobuoy",
},
)
}

// Add volume mount, volume, and 3 env vars (for different possible platforms) for SSH capabilities.
if len(cfg.SSHKeyPath) > 0 {
defMode := int32(256)
m.ExtraVolumes = append(m.ExtraVolumes, manifest.Volume{
Volume: corev1.Volume{
Name: "sshkey-vol",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: "ssh-key",
DefaultMode: &defMode,
},
},
},
})
m.Spec.Env = append(m.Spec.Env,
corev1.EnvVar{Name: "LOCAL_SSH_KEY", Value: "id_rsa"},
corev1.EnvVar{Name: "AWS_SSH_KEY", Value: "/root/.ssh/id_rsa"},
corev1.EnvVar{Name: "KUBE_SSH_KEY", Value: "id_rsa"},
)
m.Spec.VolumeMounts = append(m.Spec.VolumeMounts,
corev1.VolumeMount{
ReadOnly: false,
Name: "sshkey-vol",
MountPath: "/root/.ssh",
},
)
}

if len(cfg.SSHUser) > 0 {
m.Spec.Env = append(m.Spec.Env,
corev1.EnvVar{Name: "KUBE_SSH_USER", Value: cfg.SSHUser},
)
}

return m
}
46 changes: 43 additions & 3 deletions pkg/client/gen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/heptio/sonobuoy/pkg/config"
"github.com/heptio/sonobuoy/pkg/plugin"

"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes/scheme"
)

Expand Down Expand Up @@ -130,16 +130,56 @@ func TestGenerateManifest(t *testing.T) {
}

func TestGenerateManifestSSH(t *testing.T) {
newConfigWithoutUUID := func() *config.Config {
c := config.New()
c.UUID = ""
return c
}

fromConfig := func(f func(*config.Config) *config.Config) *config.Config {
c := newConfigWithoutUUID()
return f(c)
}

tcs := []struct {
name string
inputcm *client.GenConfig
goldenFile string
}{
{
name: "Default",
inputcm: &client.GenConfig{
E2EConfig: &client.E2EConfig{},
Config: newConfigWithoutUUID(),
},
goldenFile: filepath.Join("testdata", "default.golden"),
}, {
name: "Only e2e",
inputcm: &client.GenConfig{
E2EConfig: &client.E2EConfig{},
Config: fromConfig(func(c *config.Config) *config.Config {
c.PluginSelections = []plugin.Selection{plugin.Selection{Name: "e2e"}}
return c
}),
},
goldenFile: filepath.Join("testdata", "e2e-default.golden"),
}, {
name: "Only systemd_logs",
inputcm: &client.GenConfig{
E2EConfig: &client.E2EConfig{},
Config: fromConfig(func(c *config.Config) *config.Config {
c.PluginSelections = []plugin.Selection{plugin.Selection{Name: "systemd-logs"}}
return c
}),
},
goldenFile: filepath.Join("testdata", "systemd-logs-default.golden"),
}, {
name: "Enabling SSH",
inputcm: &client.GenConfig{
E2EConfig: &client.E2EConfig{},
Config: &config.Config{},
E2EConfig: &client.E2EConfig{},
Config: &config.Config{
PluginSelections: []plugin.Selection{plugin.Selection{Name: "e2e"}},
},
SSHKeyPath: filepath.Join("testdata", "test_ssh.key"),
SSHUser: "ssh-user",
},
Expand Down
85 changes: 85 additions & 0 deletions pkg/client/testdata/default.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@

---
apiVersion: v1
kind: Namespace
metadata:
name:
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
component: sonobuoy
name: sonobuoy-serviceaccount
namespace:
---
apiVersion: v1
data:
config.json: |
{"Description":"DEFAULT","UUID":"","Version":"v0.14.1","ResultsDir":"/tmp/sonobuoy","Resources":["CertificateSigningRequests","ClusterRoleBindings","ClusterRoles","ComponentStatuses","CustomResourceDefinitions","Nodes","PersistentVolumes","PodSecurityPolicies","ServerGroups","ServerVersion","StorageClasses","ConfigMaps","ControllerRevisions","CronJobs","DaemonSets","Deployments","Endpoints","Ingresses","Jobs","LimitRanges","NetworkPolicies","PersistentVolumeClaims","PodDisruptionBudgets","PodTemplates","Pods","ReplicaSets","ReplicationControllers","ResourceQuotas","RoleBindings","Roles","ServiceAccounts","Services","StatefulSets"],"Filters":{"Namespaces":".*","LabelSelector":""},"Limits":{"PodLogs":{"LimitSize":"","LimitTime":""}},"Server":{"bindaddress":"0.0.0.0","bindport":8080,"advertiseaddress":"","timeoutseconds":10800},"Plugins":null,"PluginSearchPath":["./plugins.d","/etc/sonobuoy/plugins.d","~/sonobuoy/plugins.d"],"Namespace":"heptio-sonobuoy","LoadedPlugins":null,"WorkerImage":"gcr.io/heptio-images/sonobuoy:v0.14.1","ImagePullPolicy":"IfNotPresent"}
kind: ConfigMap
metadata:
labels:
component: sonobuoy
name: sonobuoy-config-cm
namespace:
---
---
apiVersion: v1
kind: Pod
metadata:
labels:
component: sonobuoy
run: sonobuoy-master
tier: analysis
name: sonobuoy
namespace:
spec:
containers:
- command:
- /bin/bash
- -c
- /sonobuoy master --no-exit=true -v 3 --logtostderr
env:
- name: SONOBUOY_ADVERTISE_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
image:
imagePullPolicy:
name: kube-sonobuoy
volumeMounts:
- mountPath: /etc/sonobuoy
name: sonobuoy-config-volume
- mountPath: /plugins.d
name: sonobuoy-plugins-volume
- mountPath: /tmp/sonobuoy
name: output-volume
restartPolicy: Never
serviceAccountName: sonobuoy-serviceaccount
volumes:
- configMap:
name: sonobuoy-config-cm
name: sonobuoy-config-volume
- configMap:
name: sonobuoy-plugins-cm
name: sonobuoy-plugins-volume
- emptyDir: {}
name: output-volume
---
apiVersion: v1
kind: Service
metadata:
labels:
component: sonobuoy
run: sonobuoy-master
name: sonobuoy-master
namespace:
spec:
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
run: sonobuoy-master
type: ClusterIP
Loading

0 comments on commit 8f123df

Please sign in to comment.