Skip to content
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

feat(agent): add pod mutation webhook to inject agent #991

Merged
merged 14 commits into from
Jan 14, 2025
Prev Previous commit
Next Next commit
Refactor agent proxy service, add test
  • Loading branch information
ebaron committed Jan 14, 2025
commit 9710ca5fc77f86464b12a64c126818d92b151b24
26 changes: 13 additions & 13 deletions config/webhook/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,38 @@ webhooks:
service:
name: webhook-service
namespace: system
path: /mutate--v1-pod
failurePolicy: Ignore
name: mpod.cryostat.io
path: /mutate-operator-cryostat-io-v1beta2-cryostat
failurePolicy: Fail
name: mcryostat.kb.io
rules:
- apiGroups:
- ""
- operator.cryostat.io
apiVersions:
- v1
- v1beta2
operations:
- CREATE
- UPDATE
resources:
- pods
- cryostats
sideEffects: None
- admissionReviewVersions:
- v1
clientConfig:
service:
name: webhook-service
namespace: system
path: /mutate-operator-cryostat-io-v1beta2-cryostat
failurePolicy: Fail
name: mcryostat.kb.io
path: /mutate--v1-pod
failurePolicy: Ignore
name: mpod.cryostat.io
rules:
- apiGroups:
- operator.cryostat.io
- ""
apiVersions:
- v1beta2
- v1
operations:
- CREATE
- UPDATE
resources:
- cryostats
- pods
sideEffects: None
---
apiVersion: admissionregistration.k8s.io/v1
Expand Down
6 changes: 6 additions & 0 deletions internal/controllers/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,16 @@ const (
AgentProxyConfigFilePath string = "/etc/nginx-cryostat"
AgentProxyConfigFileName string = "nginx.conf"

// Labels applied by operator to track cross-namespace ownership
targetNamespaceCRLabelPrefix = "operator.cryostat.io/"
TargetNamespaceCRNameLabel = targetNamespaceCRLabelPrefix + "name"
TargetNamespaceCRNamespaceLabel = targetNamespaceCRLabelPrefix + "namespace"

// Labels for agent auto-configuration
agentLabelPrefix = "cryostat.io/"
AgentLabelCryostatName = agentLabelPrefix + "name"
AgentLabelCryostatNamespace = agentLabelPrefix + "namespace"

CryostatCATLSCommonName = "cryostat-ca-cert-manager"
CryostatTLSCommonName = "cryostat"
ReportsTLSCommonName = "cryostat-reports"
Expand Down
28 changes: 15 additions & 13 deletions internal/controllers/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@ import (
"context"
"fmt"
"net/url"
"strconv"

operatorv1beta2 "github.com/cryostatio/cryostat-operator/api/v1beta2"
common "github.com/cryostatio/cryostat-operator/internal/controllers/common"
"github.com/cryostatio/cryostat-operator/internal/controllers/common/resource_definitions"
"github.com/cryostatio/cryostat-operator/internal/controllers/constants"
"github.com/cryostatio/cryostat-operator/internal/controllers/model"
"github.com/cryostatio/cryostat-operator/internal/webhooks/agent"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -51,7 +49,7 @@ func (r *Reconciler) reconcileCoreService(ctx context.Context, cr *model.Cryosta
appProtocol := "http"
svc.Spec.Ports = []corev1.ServicePort{
{
Name: "http",
Name: constants.HttpPortName,
Port: *config.HTTPPort,
TargetPort: intstr.IntOrString{IntVal: constants.AuthProxyHttpContainerPort},
AppProtocol: &appProtocol,
Expand Down Expand Up @@ -91,7 +89,7 @@ func (r *Reconciler) reconcileReportsService(ctx context.Context, cr *model.Cryo
}
svc.Spec.Ports = []corev1.ServicePort{
{
Name: "http",
Name: constants.HttpPortName,
Port: *config.HTTPPort,
TargetPort: intstr.IntOrString{IntVal: constants.ReportsContainerPort},
},
Expand All @@ -109,19 +107,23 @@ func (r *Reconciler) reconcileReportsService(ctx context.Context, cr *model.Cryo
}
specs.ReportsURL = &url.URL{
Scheme: scheme,
Host: svc.Name + ":" + strconv.Itoa(int(svc.Spec.Ports[0].Port)), // TODO use getHTTPPort?
Host: fmt.Sprintf("%s:%d", svc.Name, *config.HTTPPort),
}
return nil
}

func (r *Reconciler) reconcileAgentService(ctx context.Context, cr *model.CryostatInstance) error {
config := configureAgentService(cr)
svc := &corev1.Service{
func NewAgentService(cr *model.CryostatInstance) *corev1.Service {
return &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: cr.Name + "-agent",
Namespace: cr.InstallNamespace,
},
}
}

func (r *Reconciler) reconcileAgentService(ctx context.Context, cr *model.CryostatInstance) error {
svc := NewAgentService(cr)
config := GetAgentServiceConfig(cr)

return r.createOrUpdateService(ctx, svc, cr.Object, &config.ServiceConfig, func() error {
svc.Spec.Selector = map[string]string{
Expand All @@ -130,7 +132,7 @@ func (r *Reconciler) reconcileAgentService(ctx context.Context, cr *model.Cryost
}
svc.Spec.Ports = []corev1.ServicePort{
{
Name: "http",
Name: constants.HttpPortName,
Port: *config.HTTPPort,
TargetPort: intstr.IntOrString{IntVal: constants.AgentProxyContainerPort},
},
Expand Down Expand Up @@ -162,12 +164,12 @@ func (r *Reconciler) reconcileAgentHeadlessServices(ctx context.Context, cr *mod
err := r.createOrUpdateService(ctx, svc, nil, config, func() error {
// Select agent auto-configuration labels
svc.Spec.Selector = map[string]string{
agent.LabelCryostatName: cr.Name,
agent.LabelCryostatNamespace: cr.InstallNamespace,
constants.AgentLabelCryostatName: cr.Name,
constants.AgentLabelCryostatNamespace: cr.InstallNamespace,
}
svc.Spec.Ports = []corev1.ServicePort{
{
Name: "http",
Name: constants.HttpPortName,
Port: 9977, // TODO make configurable
TargetPort: intstr.IntOrString{IntVal: 9977},
},
Expand Down Expand Up @@ -245,7 +247,7 @@ func configureReportsService(cr *model.CryostatInstance) *operatorv1beta2.Report
return config
}

func configureAgentService(cr *model.CryostatInstance) *operatorv1beta2.AgentServiceConfig {
func GetAgentServiceConfig(cr *model.CryostatInstance) *operatorv1beta2.AgentServiceConfig {
// Check CR for config
var config *operatorv1beta2.AgentServiceConfig
if cr.Spec.ServiceOptions == nil || cr.Spec.ServiceOptions.AgentConfig == nil {
Expand Down
6 changes: 3 additions & 3 deletions internal/webhooks/agent/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
package agent

const (
labelprefix = "cryostat.io/"
LabelCryostatName = labelprefix + "name"
LabelCryostatNamespace = labelprefix + "namespace"
agentLabelPrefix = "cryostat.io/"
AgentLabelCryostatName = agentLabelPrefix + "name"
AgentLabelCryostatNamespace = agentLabelPrefix + "namespace"
)
26 changes: 13 additions & 13 deletions internal/webhooks/agent/pod_defaulter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"

operatorv1beta2 "github.com/cryostatio/cryostat-operator/api/v1beta2"
"github.com/cryostatio/cryostat-operator/internal/controllers"
"github.com/cryostatio/cryostat-operator/internal/controllers/common"
"github.com/cryostatio/cryostat-operator/internal/controllers/constants"
"github.com/cryostatio/cryostat-operator/internal/controllers/model"
Expand Down Expand Up @@ -58,16 +59,16 @@ func (r *podMutator) Default(ctx context.Context, obj runtime.Object) error {

// TODO do this with objectSelector: https://github.com/kubernetes-sigs/controller-tools/issues/553
// Check for required labels and return early if missing
if !metav1.HasLabel(pod.ObjectMeta, LabelCryostatName) || !metav1.HasLabel(pod.ObjectMeta, LabelCryostatNamespace) {
if !metav1.HasLabel(pod.ObjectMeta, constants.AgentLabelCryostatName) || !metav1.HasLabel(pod.ObjectMeta, constants.AgentLabelCryostatNamespace) {
fmt.Println(pod.Labels)
return nil
}

// Look up Cryostat
cr := &operatorv1beta2.Cryostat{}
err := r.client.Get(ctx, types.NamespacedName{
Name: pod.Labels[LabelCryostatName],
Namespace: pod.Labels[LabelCryostatNamespace],
Name: pod.Labels[constants.AgentLabelCryostatName],
Namespace: pod.Labels[constants.AgentLabelCryostatNamespace],
}, cr)
if err != nil {
return err
Expand All @@ -80,7 +81,8 @@ func (r *podMutator) Default(ctx context.Context, obj runtime.Object) error {
}

// Check whether TLS is enabled for this CR
tlsEnabled := r.IsCertManagerEnabled(model.FromCryostat(cr))
crModel := model.FromCryostat(cr)
tlsEnabled := r.IsCertManagerEnabled(crModel)

// Select target container
if len(pod.Spec.Containers) == 0 {
Expand Down Expand Up @@ -131,7 +133,7 @@ func (r *podMutator) Default(ctx context.Context, obj runtime.Object) error {
container.Env = append(container.Env,
corev1.EnvVar{
Name: "CRYOSTAT_AGENT_BASEURI",
Value: cryostatURL(cr, tlsEnabled),
Value: cryostatURL(crModel, tlsEnabled),
},
corev1.EnvVar{
Name: podNameEnvVar,
Expand Down Expand Up @@ -265,18 +267,16 @@ func (r *podMutator) Default(ctx context.Context, obj runtime.Object) error {
return nil
}

func cryostatURL(cr *operatorv1beta2.Cryostat, tls bool) string {
func cryostatURL(cr *model.CryostatInstance, tls bool) string {
// Build the URL to the agent proxy service
scheme := "https"
if !tls {
scheme = "http"
}
// TODO see if this can be easily refactored
port := constants.AgentProxyContainerPort
if cr.Spec.ServiceOptions != nil && cr.Spec.ServiceOptions.AgentConfig != nil && cr.Spec.ServiceOptions.AgentConfig.HTTPPort != nil {
port = *cr.Spec.ServiceOptions.AgentConfig.HTTPPort
}
return fmt.Sprintf("%s://%s-agent.%s.svc:%d", scheme, cr.Name, cr.Namespace, // TODO maybe use agent service instead of CR meta
port)
svc := controllers.NewAgentService(cr)
config := controllers.GetAgentServiceConfig(cr)
return fmt.Sprintf("%s://%s.%s.svc:%d", scheme, svc.Name, svc.Namespace,
*config.HTTPPort)
}

func (r *podMutator) callbackEnv(cr *operatorv1beta2.Cryostat, namespace string, tls bool) []corev1.EnvVar {
Expand Down
12 changes: 12 additions & 0 deletions internal/webhooks/agent/pod_defaulter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,9 @@ var _ = Describe("PodDefaulter", func() {
}

JustAfterEach(func() {
// Reset state
agentWebhookConfig.OSUtils = saveOSUtils
agentWebhookConfig.InitImageTag = nil
})

Context("for development", func() {
Expand All @@ -276,6 +278,16 @@ var _ = Describe("PodDefaulter", func() {
ExpectPod()
})
})

Context("with a custom proxy port", func() {
BeforeEach(func() {
t.objs = append(t.objs, t.NewCryostatWithAgentSvc().Object)
originalPod = t.NewPod()
expectedPod = t.NewMutatedPodProxyPort()
})

ExpectPod()
})
})

Context("with a missing Cryostat CR", func() {
Expand Down
Loading