Skip to content
This repository has been archived by the owner on Mar 4, 2024. It is now read-only.

Commit

Permalink
EVEREST-496 Add multi namespaces monitoring (#426)
Browse files Browse the repository at this point in the history
  • Loading branch information
recharte authored Feb 7, 2024
1 parent 9152e62 commit 3603057
Show file tree
Hide file tree
Showing 16 changed files with 440 additions and 701 deletions.
6 changes: 3 additions & 3 deletions api/backup_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (e *EverestServer) CreateBackupStorage(ctx echo.Context) error { //nolint:f
if err != nil {
e.l.Error(err)
// TODO: Move this logic to the operator
dErr := e.kubeClient.DeleteSecret(c, params.Name)
dErr := e.kubeClient.DeleteSecret(c, e.kubeClient.Namespace(), params.Name)
if dErr != nil {
return ctx.JSON(http.StatusInternalServerError, Error{
Message: pointer.ToString("Failed cleaning up secret for a backup storage"),
Expand Down Expand Up @@ -184,7 +184,7 @@ func (e *EverestServer) DeleteBackupStorage(ctx echo.Context, backupStorageName
Message: pointer.ToString("Failed to delete a backup storage"),
})
}
if err := e.kubeClient.DeleteSecret(ctx.Request().Context(), backupStorageName); err != nil {
if err := e.kubeClient.DeleteSecret(ctx.Request().Context(), e.kubeClient.Namespace(), backupStorageName); err != nil {
if k8serrors.IsNotFound(err) {
return ctx.NoContent(http.StatusNoContent)
}
Expand Down Expand Up @@ -245,7 +245,7 @@ func (e *EverestServer) UpdateBackupStorage(ctx echo.Context, backupStorageName
})
}

secret, err := e.kubeClient.GetSecret(c, backupStorageName)
secret, err := e.kubeClient.GetSecret(c, e.kubeClient.Namespace(), backupStorageName)
if err != nil {
if k8serrors.IsNotFound(err) {
return ctx.JSON(http.StatusNotFound, Error{
Expand Down
2 changes: 1 addition & 1 deletion api/database_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (e *EverestServer) GetDatabaseClusterCredentials(ctx echo.Context, name str
e.l.Error(err)
return ctx.JSON(http.StatusInternalServerError, Error{Message: pointer.ToString(err.Error())})
}
secret, err := e.kubeClient.GetSecret(ctx.Request().Context(), databaseCluster.Spec.Engine.UserSecretsName)
secret, err := e.kubeClient.GetSecret(ctx.Request().Context(), "percona-everest", databaseCluster.Spec.Engine.UserSecretsName)
if err != nil {
e.l.Error(err)
return ctx.JSON(http.StatusInternalServerError, Error{Message: pointer.ToString(err.Error())})
Expand Down
318 changes: 152 additions & 166 deletions api/everest-server.gen.go

Large diffs are not rendered by default.

50 changes: 0 additions & 50 deletions api/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"github.com/AlekSi/pointer"
"github.com/labstack/echo/v4"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"

"github.com/percona/percona-everest-backend/pkg/kubernetes"
)
Expand Down Expand Up @@ -57,55 +56,6 @@ func (e *EverestServer) GetKubernetesClusterResources(ctx echo.Context) error {
return ctx.JSON(http.StatusOK, res)
}

// SetKubernetesClusterMonitoring enables or disables Kubernetes cluster monitoring.
func (e *EverestServer) SetKubernetesClusterMonitoring(ctx echo.Context) error {
var params KubernetesClusterMonitoring
if err := ctx.Bind(&params); err != nil {
e.l.Error(err)
return ctx.JSON(http.StatusBadRequest, Error{
Message: pointer.ToString("Could not parse request body"),
})
}

if params.Enable {
return e.enableK8sClusterMonitoring(ctx, params)
}

return e.disableK8sClusterMonitoring(ctx)
}

func (e *EverestServer) disableK8sClusterMonitoring(ctx echo.Context) error {
if err := e.kubeClient.DeleteVMAgent(); err != nil {
if k8serrors.IsNotFound(err) {
// Nothing to disable
return ctx.NoContent(http.StatusOK)
}
return ctx.JSON(http.StatusInternalServerError, Error{
Message: pointer.ToString("Could not delete VMAgent"),
})
}

return ctx.NoContent(http.StatusOK)
}

func (e *EverestServer) enableK8sClusterMonitoring(ctx echo.Context, params KubernetesClusterMonitoring) error {
mc, err := e.kubeClient.GetMonitoringConfig(ctx.Request().Context(), params.MonitoringInstanceName)
if err != nil {
e.l.Error(err)
return ctx.JSON(http.StatusInternalServerError, Error{
Message: pointer.ToString("Could not create VMAgent in Kubernetes"),
})
}
if err := e.kubeClient.DeployVMAgent(ctx.Request().Context(), mc.Spec.CredentialsSecretName, mc.Spec.PMM.URL); err != nil {
e.l.Error(err)
return ctx.JSON(http.StatusInternalServerError, Error{
Message: pointer.ToString("Could not create VMAgent in Kubernetes"),
})
}

return ctx.NoContent(http.StatusOK)
}

func (e *EverestServer) calculateClusterResources(
ctx echo.Context, kubeClient *kubernetes.Kubernetes, clusterType kubernetes.ClusterType,
volumes *corev1.PersistentVolumeList,
Expand Down
64 changes: 39 additions & 25 deletions api/monitoring_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,19 @@ import (
"github.com/percona/percona-everest-backend/pkg/pmm"
)

const (
// MonitoringNamespace is the namespace where monitoring configs are created.
MonitoringNamespace = "percona-everest-monitoring"
)

// CreateMonitoringInstance creates a new monitoring instance.
func (e *EverestServer) CreateMonitoringInstance(ctx echo.Context) error {
params, err := validateCreateMonitoringInstanceRequest(ctx)
if err != nil {
return ctx.JSON(http.StatusBadRequest, Error{Message: pointer.ToString(err.Error())})
}
c := ctx.Request().Context()
m, err := e.kubeClient.GetMonitoringConfig(c, params.Name)
m, err := e.kubeClient.GetMonitoringConfig(c, MonitoringNamespace, params.Name)
if err != nil && !k8serrors.IsNotFound(err) {
e.l.Error(err)
return ctx.JSON(http.StatusInternalServerError, Error{
Expand Down Expand Up @@ -68,9 +73,10 @@ func (e *EverestServer) CreateMonitoringInstance(ctx echo.Context) error {
}

result := MonitoringInstance{
Type: MonitoringInstanceBaseWithNameType(params.Type),
Name: params.Name,
Url: params.Url,
Type: MonitoringInstanceBaseWithNameType(params.Type),
Name: params.Name,
Url: params.Url,
TargetNamespaces: params.TargetNamespaces,
}

return ctx.JSON(http.StatusOK, result)
Expand All @@ -94,7 +100,7 @@ func (e *EverestServer) createMonitoringK8sResources(
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: params.Name,
Namespace: e.kubeClient.Namespace(),
Namespace: MonitoringNamespace,
},
Type: corev1.SecretTypeOpaque,
StringData: e.monitoringConfigSecretData(apiKey),
Expand All @@ -114,20 +120,21 @@ func (e *EverestServer) createMonitoringK8sResources(
err := e.kubeClient.CreateMonitoringConfig(c, &everestv1alpha1.MonitoringConfig{
ObjectMeta: metav1.ObjectMeta{
Name: params.Name,
Namespace: e.kubeClient.Namespace(),
Namespace: MonitoringNamespace,
},
Spec: everestv1alpha1.MonitoringConfigSpec{
Type: everestv1alpha1.MonitoringType(params.Type),
PMM: everestv1alpha1.PMMConfig{
URL: params.Url,
},
CredentialsSecretName: params.Name,
TargetNamespaces: *params.TargetNamespaces,
},
})
if err != nil {
e.l.Error(err)
if dErr := e.kubeClient.DeleteSecret(c, params.Name); dErr != nil {
return fmt.Errorf("failed cleaning up the secret because failed creating backup storage")
if dErr := e.kubeClient.DeleteSecret(c, MonitoringNamespace, params.Name); dErr != nil {
return fmt.Errorf("failed cleaning up the secret because failed creating monitoring instance")
}
return fmt.Errorf("failed creating monitoring instance")
}
Expand All @@ -137,7 +144,7 @@ func (e *EverestServer) createMonitoringK8sResources(

// ListMonitoringInstances lists all monitoring instances.
func (e *EverestServer) ListMonitoringInstances(ctx echo.Context) error {
mcList, err := e.kubeClient.ListMonitoringConfigs(ctx.Request().Context())
mcList, err := e.kubeClient.ListMonitoringConfigs(ctx.Request().Context(), MonitoringNamespace)
if err != nil {
e.l.Error(err)
return ctx.JSON(http.StatusInternalServerError, Error{Message: pointer.ToString("Could not get a list of monitoring instances")})
Expand All @@ -147,17 +154,18 @@ func (e *EverestServer) ListMonitoringInstances(ctx echo.Context) error {
for _, mc := range mcList.Items {
mc := mc
result = append(result, &MonitoringInstance{
Type: MonitoringInstanceBaseWithNameType(mc.Spec.Type),
Name: mc.Name,
Url: mc.Spec.PMM.URL,
Type: MonitoringInstanceBaseWithNameType(mc.Spec.Type),
Name: mc.Name,
Url: mc.Spec.PMM.URL,
TargetNamespaces: &mc.Spec.TargetNamespaces,
})
}
return ctx.JSON(http.StatusOK, result)
}

// GetMonitoringInstance retrieves a monitoring instance.
func (e *EverestServer) GetMonitoringInstance(ctx echo.Context, name string) error {
m, err := e.kubeClient.GetMonitoringConfig(ctx.Request().Context(), name)
m, err := e.kubeClient.GetMonitoringConfig(ctx.Request().Context(), MonitoringNamespace, name)
if err != nil {
if k8serrors.IsNotFound(err) {
return ctx.JSON(http.StatusNotFound, Error{
Expand All @@ -169,9 +177,10 @@ func (e *EverestServer) GetMonitoringInstance(ctx echo.Context, name string) err
}

return ctx.JSON(http.StatusOK, &MonitoringInstance{
Type: MonitoringInstanceBaseWithNameType(m.Spec.Type),
Name: m.Name,
Url: m.Spec.PMM.URL,
Type: MonitoringInstanceBaseWithNameType(m.Spec.Type),
Name: m.Name,
Url: m.Spec.PMM.URL,
TargetNamespaces: &m.Spec.TargetNamespaces,
})
}

Expand All @@ -182,7 +191,7 @@ func (e *EverestServer) UpdateMonitoringInstance(ctx echo.Context, name string)
if err != nil {
return ctx.JSON(http.StatusBadRequest, Error{Message: pointer.ToString(err.Error())})
}
m, err := e.kubeClient.GetMonitoringConfig(c, name)
m, err := e.kubeClient.GetMonitoringConfig(c, MonitoringNamespace, name)
if err != nil {
if k8serrors.IsNotFound(err) {
return ctx.JSON(http.StatusNotFound, Error{
Expand Down Expand Up @@ -214,7 +223,7 @@ func (e *EverestServer) UpdateMonitoringInstance(ctx echo.Context, name string)
_, err = e.kubeClient.UpdateSecret(c, &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: e.kubeClient.Namespace(),
Namespace: MonitoringNamespace,
},
Type: corev1.SecretTypeOpaque,
StringData: e.monitoringConfigSecretData(apiKey),
Expand All @@ -228,6 +237,9 @@ func (e *EverestServer) UpdateMonitoringInstance(ctx echo.Context, name string)
if params.Url != "" {
m.Spec.PMM.URL = params.Url
}
if params.TargetNamespaces != nil {
m.Spec.TargetNamespaces = *params.TargetNamespaces
}
err = e.kubeClient.UpdateMonitoringConfig(c, m)
if err != nil {
e.l.Error(err)
Expand All @@ -237,15 +249,16 @@ func (e *EverestServer) UpdateMonitoringInstance(ctx echo.Context, name string)
}

return ctx.JSON(http.StatusOK, &MonitoringInstance{
Type: MonitoringInstanceBaseWithNameType(m.Spec.Type),
Name: m.Name,
Url: m.Spec.PMM.URL,
Type: MonitoringInstanceBaseWithNameType(m.Spec.Type),
Name: m.Name,
Url: m.Spec.PMM.URL,
TargetNamespaces: &m.Spec.TargetNamespaces,
})
}

// DeleteMonitoringInstance deletes a monitoring instance.
func (e *EverestServer) DeleteMonitoringInstance(ctx echo.Context, name string) error {
used, err := e.kubeClient.IsMonitoringConfigUsed(ctx.Request().Context(), name)
used, err := e.kubeClient.IsMonitoringConfigUsed(ctx.Request().Context(), MonitoringNamespace, name)
if err != nil {
if k8serrors.IsNotFound(err) {
return ctx.JSON(http.StatusNotFound, Error{
Expand All @@ -262,7 +275,7 @@ func (e *EverestServer) DeleteMonitoringInstance(ctx echo.Context, name string)
Message: pointer.ToString(fmt.Sprintf("Monitoring instance %s is used", name)),
})
}
if err := e.kubeClient.DeleteMonitoringConfig(ctx.Request().Context(), name); err != nil {
if err := e.kubeClient.DeleteMonitoringConfig(ctx.Request().Context(), MonitoringNamespace, name); err != nil {
if k8serrors.IsNotFound(err) {
return ctx.JSON(http.StatusNotFound, Error{
Message: pointer.ToString("Monitoring instance is not found"),
Expand All @@ -273,7 +286,7 @@ func (e *EverestServer) DeleteMonitoringInstance(ctx echo.Context, name string)
Message: pointer.ToString("Failed to get monitoring instance"),
})
}
if err := e.kubeClient.DeleteSecret(ctx.Request().Context(), name); err != nil {
if err := e.kubeClient.DeleteSecret(ctx.Request().Context(), MonitoringNamespace, name); err != nil {
if k8serrors.IsNotFound(err) {
return ctx.NoContent(http.StatusNoContent)
}
Expand All @@ -287,6 +300,7 @@ func (e *EverestServer) DeleteMonitoringInstance(ctx echo.Context, name string)

func (e *EverestServer) monitoringConfigSecretData(apiKey string) map[string]string {
return map[string]string{
"apiKey": apiKey,
"apiKey": apiKey,
"username": "api_key",
}
}
10 changes: 9 additions & 1 deletion api/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,10 @@ func validateCreateMonitoringInstanceRequest(ctx echo.Context) (*CreateMonitorin
return nil, ErrInvalidURL("url")
}

if params.TargetNamespaces == nil || len(*params.TargetNamespaces) == 0 {
return nil, errors.New("targetNamespaces is required")
}

switch params.Type {
case MonitoringInstanceCreateParamsTypePmm:
if params.Pmm == nil {
Expand Down Expand Up @@ -418,6 +422,10 @@ func validateUpdateMonitoringInstanceRequest(ctx echo.Context) (*UpdateMonitorin
}
}

if params.TargetNamespaces != nil && len(*params.TargetNamespaces) == 0 {
return nil, errors.New("targetNamespaces cannot be empty")
}

if err := validateUpdateMonitoringInstanceType(params); err != nil {
return nil, err
}
Expand Down Expand Up @@ -480,7 +488,7 @@ func (e *EverestServer) validateDatabaseClusterCR(ctx echo.Context, databaseClus
return err
}
if databaseCluster.Spec != nil && databaseCluster.Spec.Monitoring != nil && databaseCluster.Spec.Monitoring.MonitoringConfigName != nil {
if _, err := e.kubeClient.GetMonitoringConfig(context.Background(), *databaseCluster.Spec.Monitoring.MonitoringConfigName); err != nil {
if _, err := e.kubeClient.GetMonitoringConfig(context.Background(), MonitoringNamespace, *databaseCluster.Spec.Monitoring.MonitoringConfigName); err != nil {
if k8serrors.IsNotFound(err) {
return fmt.Errorf("monitoring config %s does not exist", *databaseCluster.Spec.Monitoring.MonitoringConfigName)
}
Expand Down
Loading

0 comments on commit 3603057

Please sign in to comment.