-
Notifications
You must be signed in to change notification settings - Fork 722
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Init container updates for persisting kibana plugins. #8389
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: Michael Montgomery <[email protected]>
updating unit tests. Signed-off-by: Michael Montgomery <[email protected]>
Should we reuse the existing init container for this? What is the overhead of adding another container? |
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
The problem with using the existing init container is that the |
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Ok, I've verified that this is working as intended by:
I'm going to now see about adding this functionality to the existing init container, and not adding a 2nd.... |
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Logs from recent commit showing init container that does both plugins initialization/copy, and config init.
Also keystore init container logs:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a very quick browse through the code, which looks good to me. I still want to some testing.
verify: func(v volume.ConfigMapVolume) error { | ||
if v.Name() != "kibana-scripts" { | ||
return fmt.Errorf("unexpected name: %s", v.Name()) | ||
} | ||
if v.VolumeMount().MountPath != "/mnt/elastic-internal/scripts" { | ||
return fmt.Errorf("unexpected mount path: %s", v.VolumeMount().MountPath) | ||
} | ||
if *v.Volume().ConfigMap.DefaultMode != 0755 { | ||
return fmt.Errorf("unexpected default mode: %d", *v.Volume().ConfigMap.DefaultMode) | ||
} | ||
return nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this not be simple equality check on the struct? Or even use something like go-snaps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately the struct's fields aren't exported, so that's why this was a bit... odd.
type ConfigMapVolume struct {
configMapName string
name string
mountPath string
items []corev1.KeyToPath
defaultMode int32
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if this needs changing.
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
Signed-off-by: Michael Montgomery <[email protected]>
pkg/controller/kibana/pod.go
Outdated
builder.WithContainersSecurityContext(defaultSecurityContext). | ||
WithPodSecurityContext(defaultPodSecurityContext). | ||
WithVolumes(LogsVolume.Volume()).WithVolumeMounts(LogsVolume.VolumeMount()). | ||
WithVolumes(PluginsVolume.Volume()).WithVolumeMounts(PluginsVolume.VolumeMount()). | ||
WithVolumes(TempVolume.Volume()).WithVolumeMounts(TempVolume.VolumeMount()) | ||
WithVolumes(TempVolume.Volume()).WithVolumeMounts(TempVolume.VolumeMount()). | ||
WithVolumes(scriptsConfigMapVolume.Volume()).WithVolumeMounts(scriptsConfigMapVolume.VolumeMount()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scripts volume mount has to be present also when the security context is disabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some unit tests to fix tomorrow, but it shouldn't block the continued review of this.
Signed-off-by: Michael Montgomery <[email protected]>
buildkite test this -f E2E_TAGS=kb -m p=gke,p=ocp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just did a quick static code review. I'll try to run some tests shortly...
|
||
import ( | ||
"bytes" | ||
"html/template" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need the html/template
package?
"html/template" | |
"text/template" |
err = initcontainer.ReconcileScriptsConfigMap(ctx, d.client, *kb, params.SetDefaultSecurityContext) | ||
if err != nil { | ||
return results.WithError(err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
err = initcontainer.ReconcileScriptsConfigMap(ctx, d.client, *kb, params.SetDefaultSecurityContext) | |
if err != nil { | |
return results.WithError(err) | |
} | |
if err := initcontainer.ReconcileScriptsConfigMap(ctx, d.client, *kb, params.SetDefaultSecurityContext); err != nil { | |
return results.WithError(err) | |
} |
// RenderInitScript renders the init script that will be run by the init container. | ||
func RenderInitScript(kb kbv1.Kibana, setDefaultSecurityContext bool) (string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// RenderInitScript renders the init script that will be run by the init container. | |
func RenderInitScript(kb kbv1.Kibana, setDefaultSecurityContext bool) (string, error) { | |
// renderInitScript renders the init script that will be run by the init container. | |
func renderInitScript(kb kbv1.Kibana, setDefaultSecurityContext bool) (string, error) { |
// RenderScriptTemplate renders initFsScriptTemplate using the given TemplateParams | ||
func RenderScriptTemplate(params templateParams) (string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// RenderScriptTemplate renders initFsScriptTemplate using the given TemplateParams | |
func RenderScriptTemplate(params templateParams) (string, error) { | |
// renderScriptTemplate renders initFsScriptTemplate using the given TemplateParams | |
func renderScriptTemplate(params templateParams) (string, error) { |
!reflect.DeepEqual(expected.Labels, reconciled.Labels) || | ||
!reflect.DeepEqual(expected.Annotations, reconciled.Annotations) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really want a strict equality?
!reflect.DeepEqual(expected.Labels, reconciled.Labels) || | |
!reflect.DeepEqual(expected.Annotations, reconciled.Annotations) | |
!maps.IsSubset(expected.Labels, reconciled.Labels) || | |
!maps.IsSubset(expected.Annotations, reconciled.Annotations) |
var HardenedSecurityContextSupportedVersion = version.From(7, 9, 0) | ||
|
||
// NewScriptsConfigMapVolume creates a new volume for the ConfigMap containing scripts used by the Kibana init container. | ||
func NewScriptsConfigMapVolume(kbName string) volume.ConfigMapVolume { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think configmaps are watched by this controller:
cloud-on-k8s/pkg/controller/kibana/controller.go
Lines 66 to 106 in 6c1bf95
func addWatches(mgr manager.Manager, c controller.Controller, r *ReconcileKibana) error { | |
// Watch for changes to Kibana | |
if err := c.Watch(source.Kind(mgr.GetCache(), &kbv1.Kibana{}, &handler.TypedEnqueueRequestForObject[*kbv1.Kibana]{})); err != nil { | |
return err | |
} | |
// Watch deployments | |
if err := c.Watch(source.Kind(mgr.GetCache(), &appsv1.Deployment{}, handler.TypedEnqueueRequestForOwner[*appsv1.Deployment]( | |
mgr.GetScheme(), mgr.GetRESTMapper(), | |
&kbv1.Kibana{}, handler.OnlyControllerOwner(), | |
))); err != nil { | |
return err | |
} | |
// Watch Pods, to ensure `status.version` and version upgrades are correctly reconciled on any change. | |
// Watching Deployments only may lead to missing some events. | |
if err := watches.WatchPods(mgr, c, kblabel.KibanaNameLabelName); err != nil { | |
return err | |
} | |
// Watch services | |
if err := c.Watch(source.Kind(mgr.GetCache(), &corev1.Service{}, handler.TypedEnqueueRequestForOwner[*corev1.Service]( | |
mgr.GetScheme(), mgr.GetRESTMapper(), | |
&kbv1.Kibana{}, handler.OnlyControllerOwner(), | |
))); err != nil { | |
return err | |
} | |
// Watch owned and soft-owned secrets | |
if err := c.Watch(source.Kind(mgr.GetCache(), &corev1.Secret{}, handler.TypedEnqueueRequestForOwner[*corev1.Secret]( | |
mgr.GetScheme(), mgr.GetRESTMapper(), | |
&kbv1.Kibana{}, handler.OnlyControllerOwner(), | |
))); err != nil { | |
return err | |
} | |
if err := watches.WatchSoftOwnedSecrets(mgr, c, kbv1.Kind); err != nil { | |
return err | |
} | |
// dynamically watch referenced secrets to connect to Elasticsearch | |
return c.Watch(source.Kind(mgr.GetCache(), &corev1.Secret{}, r.dynamicWatches.Secrets)) | |
} |
} | ||
} | ||
|
||
// ReconcileScriptsConfigMap reconciles the ConfigMap containing scripts used by the Kibana init container. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: just to make it clear that it is not used by the one used for the keystore?
// ReconcileScriptsConfigMap reconciles the ConfigMap containing scripts used by the Kibana init container. | |
// ReconcileScriptsConfigMap reconciles the ConfigMap containing scripts used by the Kibana elastic-internal-init init container. |
} | ||
|
||
// newConfigMapWithData constructs a new ConfigMap with the given data. | ||
func newConfigMapWithData(cm, kb types.NamespacedName, data map[string]string) corev1.ConfigMap { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we just inline this function? IMHO I'm not sure it adds much, and it would make the code easier to read.
} | ||
|
||
// reconcileConfigMap checks for an existing ConfigMap and updates it or creates one if it does not exist. | ||
func reconcileConfigMap( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, this function is just calling reconciler.ReconcileResource
I would inline it.
// IncludePlugins indicates whether the script should include | ||
// the plugins persistence logic. | ||
IncludePlugins bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit, naming...
// IncludePlugins indicates whether the script should include | |
// the plugins persistence logic. | |
IncludePlugins bool | |
// CopyPlugins indicates whether the script should copy the plugins from the Kibana image. | |
// This should be set to true when the file system is in read only mode and the plugin directory is replaced by an emptyDir. | |
CopyPlugins bool |
if err != nil { | ||
return corev1.Container{}, err // error unlikely and should have been caught during validation | ||
} | ||
enablePluginsMounts := v.GTE(HardenedSecurityContextSupportedVersion) && setDefaultSecurityContext |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why setDefaultSecurityContext
is required to be true
? Does it mean that if a user is running on OCP and wants to set readOnlyRootFilesystem: true
for example, then this volume and the init-container should be added/adjusted manually?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could just always copy the plugins and avoid a lot of conditional logic?
Resources: defaultResources, | ||
} | ||
|
||
if enablePluginsMounts { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: we can remove the variable (or move it closer to where it is used)
if enablePluginsMounts { | |
if v.GTE(HardenedSecurityContextSupportedVersion) && setDefaultSecurityContext { | |
container.VolumeMounts = append(container.VolumeMounts, PluginsSharedVolume.InitContainerVolumeMount()) | |
} |
resolves: #8388
With the recent changes in #7787 we broke users being able to install custom Kibana plugins. This updates the existing init container for Kibana configuration, and adds the ability to copy plugins to an
emptyDir
volume, and then mount this to/usr/share/kibana/plugins
in the primary Kibana container. (effectively mirroring what we do with Elasticsearch)Review Notes
Changes
settings
package to avoid import cycles.Testing