github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/namespace/fluentd_configmap_test.go (about)

     1  // Copyright (c) 2022, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package namespace
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/verrazzano/verrazzano/pkg/constants"
    13  	corev1 "k8s.io/api/core/v1"
    14  	"k8s.io/apimachinery/pkg/api/errors"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	"k8s.io/apimachinery/pkg/types"
    17  	k8scheme "k8s.io/client-go/kubernetes/scheme"
    18  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    19  )
    20  
    21  const (
    22  	testNamespace = "unit-test-ns"
    23  	testLogID     = "ocid1.log.oc1.test"
    24  )
    25  
    26  const fluentdConfig = `|
    27  # Common config
    28  @include general.conf
    29  
    30  # Input sources
    31  @include systemd-input.conf
    32  @include kubernetes-input.conf
    33  
    34  # Parsing/Filtering
    35  @include systemd-filter.conf
    36  @include kubernetes-filter.conf
    37  
    38  # Send to storage
    39  @include output.conf
    40  # Start namespace logging configs
    41  # End namespace logging configs
    42  @include oci-logging-system.conf
    43  @include oci-logging-default-app.conf
    44  `
    45  
    46  // TestAddAndRemoveNamespaceLogging tests adding and removing namespace logging config to the Fluentd config map.
    47  func TestAddAndRemoveNamespaceLogging(t *testing.T) {
    48  	asserts := assert.New(t)
    49  
    50  	// GIVEN a system Fluentd config map
    51  	// WHEN I add namespace-specific logging configuration
    52  	// THEN the config map gets updated in the cluster and contains the new logging config
    53  	cm := newConfigMap()
    54  	cm.Data = make(map[string]string)
    55  	cm.Data[fluentdConfigKey] = fluentdConfig
    56  
    57  	client := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects(cm).Build()
    58  
    59  	updated, err := addNamespaceLogging(context.TODO(), client, testNamespace, testLogID)
    60  	asserts.NoError(err)
    61  	asserts.True(updated)
    62  
    63  	// fetch the config map and make sure it was updated correctly
    64  	cm = &corev1.ConfigMap{}
    65  	err = client.Get(context.TODO(), types.NamespacedName{Name: fluentdConfigMapName, Namespace: constants.VerrazzanoSystemNamespace}, cm)
    66  	asserts.NoError(err)
    67  
    68  	nsConfigKey := fmt.Sprintf(nsConfigKeyTemplate, testNamespace)
    69  	includeSection := fmt.Sprintf("%s\n@include oci-logging-ns-unit-test-ns.conf\n", startNamespaceConfigsMarker)
    70  	asserts.Contains(cm.Data[fluentdConfigKey], includeSection)
    71  	asserts.Contains(cm.Data, nsConfigKey)
    72  	asserts.Contains(cm.Data[nsConfigKey], testLogID)
    73  
    74  	// GIVEN a system Fluentd config map with namespace-specific logging config
    75  	// WHEN I remove the namespace-specific logging configuration
    76  	// THEN the config map gets updated in the cluster and the config map matches the state prior to adding the config
    77  	updated, err = removeNamespaceLogging(context.TODO(), client, testNamespace)
    78  	asserts.NoError(err)
    79  	asserts.True(updated)
    80  
    81  	// fetch the config map and make sure the namespace logging config was removed
    82  	cm = &corev1.ConfigMap{}
    83  	err = client.Get(context.TODO(), types.NamespacedName{Name: fluentdConfigMapName, Namespace: constants.VerrazzanoSystemNamespace}, cm)
    84  	asserts.NoError(err)
    85  	asserts.Equal(fluentdConfig, cm.Data[fluentdConfigKey])
    86  	asserts.NotContains(cm.Data, nsConfigKey)
    87  }
    88  
    89  // TestMissingFluentdConfigMap tests the cases where the system Fluentd config map is not found.
    90  func TestMissingFluentdConfigMap(t *testing.T) {
    91  	asserts := assert.New(t)
    92  
    93  	// GIVEN there is no system Fluentd config map
    94  	// WHEN I add namespace-specific logging configuration
    95  	// THEN an error is returned
    96  	client := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).Build()
    97  	_, err := addNamespaceLogging(context.TODO(), client, testNamespace, testLogID)
    98  	asserts.Error(err)
    99  	asserts.Contains(err.Error(), "must exist")
   100  
   101  	// GIVEN there is no system Fluentd config map
   102  	// WHEN I attempt to remove namespace-specific logging configuration
   103  	// THEN no error is returned and the config map has not been created
   104  	updated, err := removeNamespaceLogging(context.TODO(), client, testNamespace)
   105  	asserts.NoError(err)
   106  	asserts.False(updated)
   107  
   108  	cm := &corev1.ConfigMap{}
   109  	err = client.Get(context.TODO(), types.NamespacedName{Name: fluentdConfigMapName, Namespace: constants.VerrazzanoSystemNamespace}, cm)
   110  	asserts.True(errors.IsNotFound(err))
   111  }
   112  
   113  // TestAddNamespaceLoggingAlreadyExists tests the case where we add logging config and it already exists in the config map.
   114  // Since this operation is idempotent the config map should not be updated.
   115  func TestAddNamespaceLoggingAlreadyExists(t *testing.T) {
   116  	asserts := assert.New(t)
   117  
   118  	// GIVEN a system Fluentd config map
   119  	// WHEN I add namespace-specific logging configuration and I attempt to add the same logging configuration
   120  	// THEN the config map is not updated a second time
   121  	cm := newConfigMap()
   122  	cm.Data = make(map[string]string)
   123  	cm.Data[fluentdConfigKey] = fluentdConfig
   124  
   125  	client := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects(cm).Build()
   126  
   127  	updated, err := addNamespaceLogging(context.TODO(), client, testNamespace, testLogID)
   128  	asserts.NoError(err)
   129  	asserts.True(updated)
   130  
   131  	updated, err = addNamespaceLogging(context.TODO(), client, testNamespace, testLogID)
   132  	asserts.NoError(err)
   133  	asserts.False(updated)
   134  }
   135  
   136  // TestRemoveNamespaceLoggingDoesNotExist tests the case where we remove logging config and it does not exist in the config map.
   137  // Since this operation is idempotent the config map should not be updated.
   138  func TestRemoveNamespaceLoggingDoesNotExist(t *testing.T) {
   139  	asserts := assert.New(t)
   140  
   141  	// GIVEN a system Fluentd config map that does not contain namespace-specific logging config
   142  	// WHEN I attempt to remove the logging config
   143  	// THEN the config map is not updated
   144  	cm := newConfigMap()
   145  	cm.Data = make(map[string]string)
   146  	cm.Data[fluentdConfigKey] = fluentdConfig
   147  
   148  	client := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects(cm).Build()
   149  
   150  	updated, err := removeNamespaceLogging(context.TODO(), client, testNamespace)
   151  	asserts.NoError(err)
   152  	asserts.False(updated)
   153  }
   154  
   155  // TestUpdateExistingLoggingConfig tests the case where the logging config already exists but the log id is updated.
   156  func TestUpdateExistingLoggingConfig(t *testing.T) {
   157  	asserts := assert.New(t)
   158  
   159  	// GIVEN a system Fluentd config map that contains namespace-specific logging config
   160  	// WHEN I attempt to add logging config for the same namespace and the log id has changed
   161  	// THEN the config map is updated and it contains the updated log id
   162  	cm := newConfigMap()
   163  	cm.Data = make(map[string]string)
   164  	cm.Data[fluentdConfigKey] = fluentdConfig
   165  
   166  	client := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects(cm).Build()
   167  
   168  	updated, err := addNamespaceLogging(context.TODO(), client, testNamespace, testLogID)
   169  	asserts.NoError(err)
   170  	asserts.True(updated)
   171  
   172  	// add logging config with an updated log id
   173  	updatedLogID := "ocid1.log.oc1.updated"
   174  	updated, err = addNamespaceLogging(context.TODO(), client, testNamespace, updatedLogID)
   175  	asserts.NoError(err)
   176  	asserts.True(updated)
   177  
   178  	// fetch the config map and confirm that the config exists and it references the updated log id
   179  	cm = &corev1.ConfigMap{}
   180  	err = client.Get(context.TODO(), types.NamespacedName{Name: fluentdConfigMapName, Namespace: constants.VerrazzanoSystemNamespace}, cm)
   181  	asserts.NoError(err)
   182  
   183  	nsConfigKey := fmt.Sprintf(nsConfigKeyTemplate, testNamespace)
   184  	includeSection := fmt.Sprintf("%s\n@include oci-logging-ns-unit-test-ns.conf\n", startNamespaceConfigsMarker)
   185  	asserts.Contains(cm.Data[fluentdConfigKey], includeSection)
   186  	asserts.Contains(cm.Data, nsConfigKey)
   187  	asserts.Contains(cm.Data[nsConfigKey], updatedLogID)
   188  }
   189  
   190  // newConfigMap returns a ConfigMap populated with the system Fluentd config map name and namespace, and
   191  // a creation timestamp.
   192  func newConfigMap() *corev1.ConfigMap {
   193  	return &corev1.ConfigMap{
   194  		ObjectMeta: metav1.ObjectMeta{
   195  			Name:              fluentdConfigMapName,
   196  			Namespace:         constants.VerrazzanoSystemNamespace,
   197  			CreationTimestamp: metav1.Now(),
   198  		},
   199  	}
   200  }