Skip to content

Commit

Permalink
Enhanced Synchronization and Configuration Handling in Keess (#30)
Browse files Browse the repository at this point in the history
# Enhanced Synchronization and Configuration Handling in Keess

This pull request introduces significant enhancements to the Keess
application, focusing on improving the reliability of secrets and
configmaps synchronization across namespaces and Kubernetes clusters.
Additionally, it modernizes the CLI experience and optimizes the Docker
image for better performance and usability.

## Key Changes

### Switch from Watch to Poll Method

- **Issue**: The previous implementation using the Kubernetes API's
watch method occasionally missed important events, leading to
synchronization gaps.
- **Solution**: Transitioned to a polling mechanism using the list
method, ensuring comprehensive coverage of all events without missing
updates.

### Adoption of Cobra CLI

- **Improvement**: Replaced the existing CLI library with
[Cobra](https://github.com/spf13/cobra), a widely adopted framework that
offers enhanced features and a more standardized command-line
experience.
- **Benefits**: Provides better support for command structuring,
argument parsing, and documentation, making Keess more accessible and
easier to use for the community.

### Docker Image Optimization

- **Update**: The Docker image has been revised to use a smaller base
image, reducing the overall size and improving the startup time of the
application.
- **Advantage**: Enhances the efficiency of deploying Keess in diverse
environments, from development to production, especially in cloud-native
ecosystems with resource constraints.

### Helm Chart Adjustments

- **Modification**: Updated the Helm chart to accommodate new
command-line options introduced by the switch to Cobra and the changes
in application configuration.
- **Flexibility**: These updates make deploying Keess with Helm more
customizable, allowing users to tailor the deployment to their specific
needs.

## Command Line Options

The new CLI, powered by Cobra, introduces several command line options
to enhance flexibility and usability:

- `--config`: Specify the configuration file path (default is
`$HOME/.keess.yaml`).
- `--logLevel`: Set the logging level (e.g., debug, info, warn).
- `--localCluster`: Name of the local Kubernetes cluster.
- `--kubeConfigPath`: Path to the kubeconfig file for cluster
authentication.
- `--namespacePollingInterval`: Interval in seconds to poll for
namespace changes.
- `--pollingInterval`: Interval in seconds to poll for secrets and
configmaps.
- `--housekeepingInterval`: Interval in seconds for orphan object
cleanup.

### Using Command Line Options

To use these options, include them when running Keess commands:

```shell
keess run --config /your/config/path.yaml --logLevel info
```

## Implementation Details

- **Commands Integration**: Migrated core application logic into
Cobra-based commands, enhancing modularity and testability.
- **Configuration Management**: Streamlined configuration handling using
Viper, supporting both environment variables and configuration files for
greater flexibility.
- **Logging and Monitoring**: Upgraded logging mechanism for better
clarity and troubleshooting support, crucial for maintaining
synchronization integrity.

## How to Test

1. Deploy the updated Keess application using the revised Helm chart.
2. Configure synchronization tasks across namespaces and clusters as
needed.
3. Change the `test.py` with the correct values for `source_cluster` and
`destination_cluster`.
4. Run `make test`.

## Helm diff
```diff
Comparing release=keess, chart=keess/keess
keess, keess, ClusterRole (rbac.authorization.k8s.io) has changed:
  # Source: keess/templates/cluster-role.yaml
  apiVersion: rbac.authorization.k8s.io/v1
  kind: ClusterRole
  metadata:
    name: keess
    labels:
-     helm.sh/chart: keess-0.2.15
+     helm.sh/chart: keess-1.0.0
      app.kubernetes.io/name: keess
      app.kubernetes.io/instance: keess
-     app.kubernetes.io/version: "0.2.14"
+     app.kubernetes.io/version: "1.0.0"
      app.kubernetes.io/managed-by: Helm
  rules:
  - apiGroups: [""]
    resources:
    - configmaps
    - secrets
    verbs: ["get", "create", "update", "patch", "delete", "list", "watch"]
  - apiGroups: [""]
    resources:
    - namespaces
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources:
    - nodes
    verbs: ["list"]
  - apiGroups: [""]
    resources:
    - events
    verbs: ["create"]
keess, keess, ClusterRoleBinding (rbac.authorization.k8s.io) has changed:
  # Source: keess/templates/cluster-role-binding.yaml
  apiVersion: rbac.authorization.k8s.io/v1
  kind: ClusterRoleBinding
  metadata:
    name: keess
    labels:
-     helm.sh/chart: keess-0.2.15
+     helm.sh/chart: keess-1.0.0
      app.kubernetes.io/name: keess
      app.kubernetes.io/instance: keess
-     app.kubernetes.io/version: "0.2.14"
+     app.kubernetes.io/version: "1.0.0"
      app.kubernetes.io/managed-by: Helm
  roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: ClusterRole
    name: keess
  subjects:
  - kind: ServiceAccount
    name: keess
    namespace: keess
keess, keess, Deployment (apps) has changed:
  # Source: keess/templates/deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: keess
    namespace: keess
    labels:
-     helm.sh/chart: keess-0.2.15
+     helm.sh/chart: keess-1.0.0
      app.kubernetes.io/name: keess
      app.kubernetes.io/instance: keess
-     app.kubernetes.io/version: "0.2.14"
+     app.kubernetes.io/version: "1.0.0"
      app.kubernetes.io/managed-by: Helm
  spec:
    selector:
      matchLabels:
        app.kubernetes.io/name: keess
        app.kubernetes.io/instance: keess
    template:
      metadata:
        labels:
          app.kubernetes.io/name: keess
          app.kubernetes.io/instance: keess
      spec:
        serviceAccountName: keess
        securityContext:
          {}
        volumes:
        - name: config
          secret:
            secretName: keess
            defaultMode: 420
        containers:
          - name: keess
            securityContext:
              {}
-           image: "image-registry.powerapp.cloud/keess/keess:0.2.14"
-           env:
-           - name: KEESS_SOURCE_CONTEXT
-             value: "app-alpha-gm"
-           - name: KEESS_DESTINATION_CONTEXTS
-             value: "app-beta-px"
-           - name: LOG_LEVEL
-             value: "DEBUG"
+           image: "image-registry.powerapp.cloud/keess/keess:PR-30-dac89984ddfead89fc06aafa0e80f0d8c2d4dc6a-1"        
+           args:
+             - --logLevel=debug
+             - --localCluster=app-alpha-gm            
+             - --kubeConfigPath=/root/.kube/config
+             - --pollingInterval=60
+             - --namespacePollingInterval=60
+             - --housekeepingInterval=60
            imagePullPolicy: IfNotPresent
            volumeMounts:
            - name: config
              mountPath: /root/.kube
              readOnly: true
            ports:
              - name: http
                containerPort: 8080
                protocol: TCP
            livenessProbe:
              httpGet:
                path: /health
                port: http
              initialDelaySeconds: 60
            readinessProbe:
              httpGet:
                path: /health
                port: http
            resources:
              limits:
                ephemeral-storage: 150Mi
                memory: 128Mi
              requests:
                cpu: 100m
                ephemeral-storage: 150Mi
                memory: 64Mi
keess, keess, Secret (v1) has changed:
  # Source: keess/templates/secret.yaml
  apiVersion: v1
  kind: Secret
  metadata:
    labels:
      app.kubernetes.io/instance: keess
      app.kubernetes.io/managed-by: Helm
      app.kubernetes.io/name: keess
-     app.kubernetes.io/version: 0.2.14
-     helm.sh/chart: keess-0.2.15
+     app.kubernetes.io/version: 1.0.0
+     helm.sh/chart: keess-1.0.0
    name: keess
    namespace: keess
  data:
    config: 'REDACTED # (7651 bytes)'

keess, keess, ServiceAccount (v1) has changed:
  # Source: keess/templates/serviceaccount.yaml
  apiVersion: v1
  kind: ServiceAccount
  metadata:
    name: keess
    namespace: keess
    labels:
-     helm.sh/chart: keess-0.2.15
+     helm.sh/chart: keess-1.0.0
      app.kubernetes.io/name: keess
      app.kubernetes.io/instance: keess
-     app.kubernetes.io/version: "0.2.14"
+     app.kubernetes.io/version: "1.0.0"
      app.kubernetes.io/managed-by: Helm
```

## Test execution result
``` log
 -- /Users/marcusvinicius.leandro/Source/keess/test.py 
[INFO] Deleted destination namespace 'test-namespace-dest-1'.
[INFO] Deleted destination namespace 'test-namespace-dest-2'.
[INFO] Created source namespace 'test-namespace'.
[INFO] Created destination namespace 'test-namespace-dest-1' with label 'keess.powerhrg.com/testing=yes'.
[INFO] Created destination namespace 'test-namespace-dest-2' with label 'keess.powerhrg.com/testing=yes'.
[INFO] Created secret 'new-test-secret' in source namespace 'test-namespace'.
[INFO] Created ConfigMap 'new-test-configmap' in source namespace 'test-namespace'.
[INFO] Waiting for resources to be created...
[INFO] Applied labels and annotations to secret 'new-test-secret' in namespace 'test-namespace'.
[INFO] Applied labels and annotations to configmap 'new-test-configmap' in namespace 'test-namespace'.
[INFO] Waiting for synchronization to complete...
[SUCCESS] Secret 'new-test-secret' in namespace 'test-namespace-dest-1' has been verified successfully with all annotations.
[SUCCESS] Configmap 'new-test-configmap' in namespace 'test-namespace-dest-1' has been verified successfully with all annotations.
[SUCCESS] Secret 'new-test-secret' in namespace 'test-namespace-dest-2' has been verified successfully with all annotations.
[SUCCESS] Configmap 'new-test-configmap' in namespace 'test-namespace-dest-2' has been verified successfully with all annotations.
[INFO] Applied labels and annotations to secret 'new-test-secret' in namespace 'test-namespace'.
[INFO] Applied labels and annotations to configmap 'new-test-configmap' in namespace 'test-namespace'.
[INFO] Waiting for synchronization to matching namespaces...
[SUCCESS] Secret 'new-test-secret' in namespace 'test-namespace-dest-1' has been verified successfully with all annotations.
[SUCCESS] Configmap 'new-test-configmap' in namespace 'test-namespace-dest-1' has been verified successfully with all annotations.
[SUCCESS] Secret 'new-test-secret' in namespace 'test-namespace-dest-2' has been verified successfully with all annotations.
[SUCCESS] Configmap 'new-test-configmap' in namespace 'test-namespace-dest-2' has been verified successfully with all annotations.
[INFO] Test scenario 3 completed.
[INFO] Applied labels and annotations to secret 'new-test-secret' in namespace 'test-namespace'.
[INFO] Applied labels and annotations to configmap 'new-test-configmap' in namespace 'test-namespace'.
[INFO] Labels and annotations applied for cross-cluster synchronization. Waiting for synchronization to complete...
[SUCCESS] Secret 'new-test-secret' in namespace 'test-namespace' has been verified successfully with all annotations.
[SUCCESS] Configmap 'new-test-configmap' in namespace 'test-namespace' has been verified successfully with all annotations.
[INFO] Test scenario 4 completed.
[INFO] Deleted secret 'new-test-secret' in source namespace 'test-namespace'.
[INFO] Deleted configmap 'new-test-configmap' in source namespace 'test-namespace'.
[INFO] Deleted source namespace 'test-namespace'.
[INFO] Deleted destination namespace 'test-namespace-dest-1'.
[INFO] Deleted destination namespace 'test-namespace-dest-2'.
```

## Conclusion

These improvements make Keess a more reliable and user-friendly tool for
Kubernetes configuration management. By addressing previous limitations
and leveraging more robust technologies, we're setting the stage for
more scalable and secure infrastructure management practices.

---------

Signed-off-by: Igor Valente Blackman <[email protected]>
Co-authored-by: Igor Blackman <[email protected]>
  • Loading branch information
mvleandro and iblackman authored Mar 21, 2024
1 parent 3792e7c commit 00d9f9a
Show file tree
Hide file tree
Showing 49 changed files with 2,991 additions and 3,289 deletions.
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
.sops.yaml
keess
README.md
Dockerfile.localtest
localTestKubeconfig
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ apiserver.local.config/**
.vscode
keess
coverage.*

localTestKubeconfig
3 changes: 0 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ RUN go mod download
# Copy the code into the container
COPY . .

# Run tests
RUN go test ./...

# Build the application
RUN go build -o keess .

Expand Down
5 changes: 5 additions & 0 deletions Dockerfile.localTest
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM python:3.12.2-bullseye

COPY ./requirements.txt .
RUN pip install -r requirements.txt
COPY ./test.py .
14 changes: 8 additions & 6 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
MIT License
The MIT License (MIT)

Copyright (c) 2023 Power Home Remodeling Group, LLC
Copyright © 2024 Power Home Remodeling

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
80 changes: 63 additions & 17 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,29 @@ DOCKER_TAG := "latest"
GOBASE := $(shell pwd)
GOBIN := $(GOBASE)/bin

.PHONY: build test docker-build coverage run docker-run help
.PHONY: build test docker-build coverage run docker-run create-local-clusters delete-local-clusters local-docker-run local-test help

# Build the project
build:
@echo "Building $(PROJECT_NAME)..."
@GOBIN=$(GOBIN) go build -o $(GOBIN)/$(PROJECT_NAME) $(GOBASE)

# Run tests
test:
gotest:
@echo "Running tests..."
@go test ./...

# Build Docker image
docker-build:
@echo "Building Docker image..."
@docker build -t $(DOCKER_IMAGE_NAME):$(DOCKER_TAG) .

# Remove the existing builder
@docker buildx rm mybuilder
@docker buildx create --name mybuilder --use
@docker buildx build --platform linux/amd64,linux/arm64 -t $(DOCKER_IMAGE_NAME):$(DOCKER_TAG) .
# Install requirements and run test.py
test:
@echo "Installing requirements..."
@pip3 install -r requirements.txt
@echo "Running test.py..."
@python3 test.py

# New target for code coverage
coverage:
Expand All @@ -39,22 +42,65 @@ coverage:
# Target to execute the application
run: build
@echo "Running the application..."
@./bin/keess run
@./bin/keess run --localCluster=$(LOCAL_CLUSTER) --logLevel=debug

# Target to run the Docker image with the .kube directory mounted
docker-run:
@echo "Running Docker image with .kube directory mounted..."
@docker run --rm -it -v ${HOME}/.kube:/root/.kube $(DOCKER_IMAGE_NAME):$(DOCKER_TAG)
@docker run --rm -it -v ${HOME}/.kube:/root/.kube $(DOCKER_IMAGE_NAME):$(DOCKER_TAG) run --localCluster=$(LOCAL_CLUSTER) --logLevel=debug

# Help
help:
@echo "Makefile commands:"
@echo "build - Build the project"
@echo "test - Run tests"
@echo "docker-build - Build Docker image"
@echo "coverage - Generate and view code coverage report"
@echo "run - Run the application"
@echo "docker-run - Run the Docker image with .kube directory mounted"
LOCAL_TEST_KUBECONFIG_FILE := "localTestKubeconfig"
# Target to start local kube clusters for testing purposes
create-local-clusters:
@kind create cluster --image=kindest/node:v1.22.17 -n source-cluster --kubeconfig $(LOCAL_TEST_KUBECONFIG_FILE)
@kind create cluster --image=kindest/node:v1.22.17 -n destination-cluster --kubeconfig $(LOCAL_TEST_KUBECONFIG_FILE)

# Target to delete local kube clusters
delete-local-clusters:
@kind delete clusters source-cluster destination-cluster

# Target to run the Docker image with local test kubeconfig mounted
local-docker-run:
@echo "Running Docker image with $(LOCAL_TEST_KUBECONFIG_FILE) mounted..."
@docker run \
--rm \
-it \
-v ./$(LOCAL_TEST_KUBECONFIG_FILE):/root/.kube/config \
--network host \
$(DOCKER_IMAGE_NAME):$(DOCKER_TAG) \
run \
--localCluster=kind-source-cluster \
--remoteCluster=kind-destination-cluster \
--kubeConfigPath=/root/.kube/config \
--pollingInterval=10 \
--housekeepingInterval=10 \
--namespacePollingInterval=10 \
--logLevel=debug

# Test locally using kind
local-test:
@echo "Building keess-test image"
@docker build -f Dockerfile.localTest -t keess-test:1.0 .
@echo "Running tests"
@docker run \
-it \
--rm \
--mount type=bind,source="./$(LOCAL_TEST_KUBECONFIG_FILE)",target=/root/.kube/config,readonly \
--network host \
--name keess-test \
keess-test:1.0 \
python test.py

# Help
help:
@echo "Makefile commands:"
@echo "build - Build the project"
@echo "test - Run tests"
@echo "docker-build - Build Docker image"
@echo "coverage - Generate and view code coverage report"
@echo "run - Run the application"
@echo "docker-run - Run the Docker image with .kube directory mounted"
@echo "create-local-clusters - Create 2 clusters locally using Kind"
@echo "delete-local-clusters - Delete the 2 local clusters created with Kind"
@echo "local-docker-run - Run the application locally using docker and pointing to the local cluster created with Kind"
@echo "local-test - Run the tests pointing to the local cluster created with Kind"
110 changes: 90 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,109 @@
# Keess

Keess keeps **Secrets** and **ConfigMaps** synchronized across namespaces and remote clusters.
# Keess: Kubernetes Secrets and ConfigMaps Synchronization

## Usage
Keess (Keep Stuff Synchronized) is a versatile command-line tool designed to synchronize secrets and configmaps across different namespaces and Kubernetes clusters. Built with simplicity and efficiency in mind, it ensures that your Kubernetes environments are consistently updated, secure, and easy to manage.

Keess uses labels and annotations from Secrets and ConfigMaps to work.
## Features

To enable the synchronization you have two steps:
- **Cross-Namespace Synchronization**: Effortlessly sync secrets and configmaps across multiple namespaces within a single Kubernetes cluster.
- **Inter-Cluster Synchronization**: Extend your synchronization capabilities to multiple clusters, keeping your configurations consistent across different environments.
- **Secure and Reliable**: Implements robust mechanisms to securely transfer sensitive information, ensuring data integrity and confidentiality.
- **Automation**: Automates the synchronization process, reducing manual overhead and minimizing human error.
- **Customizable**: Offers flexible command line options and Kubernetes annotations to tailor the synchronization process to your specific needs.
- **Efficient Monitoring**: Provides detailed logs for tracking operations and auditing changes.

First, you have to add a label indicating which type of synchronization you want: namespace or cluster. See the options below:
## Getting Started

`keess.powerhrg.com/sync: namespace`
### Prerequisites

`keess.powerhrg.com/sync: cluster`
- Kubernetes cluster setup
- kubectl installed and configured
- Helm (optional, for Helm chart deployment)

Then you need to configure the synchronization using annotations, which will be described in the next topics.
### Installation

### Namespace synchronization
Refer to the previous section on installing Keess via binaries, source, or Helm.

The namespace synchronization counts with three options. You can specify the destination namespaces names, and labels or you can choose to synchronize with all namespaces.
### Configuration

`keess.powerhrg.com/namespaces-names: all`
#### Using Configuration Files

`keess.powerhrg.com/namespaces-names: namespacea, namespaceb, namespacec`
Create a `.keess.yaml` configuration file as previously described or specify the path using the `--config` flag.

`keess.powerhrg.com/namespace-label: keess.powerhrg.com/sync="true"`
#### Using Command Line Flags

Keess supports various command line flags for on-the-fly configuration:

### Cluster synchronization
```shell
./keess run --logLevel debug --localCluster my-cluster --kubeConfigPath /path/to/kubeconfig
```

The cluster synchronization occurs across different clusters and the same namespaces.
You need to specify the cluster names using the annotation below:
For a full list of available flags, use:

`keess.powerhrg.com/clusters: clustera, clusterb, clusterc`
```shell
./keess --help
```

## Known flaws
### Configuring Synchronization

Changes made to synchronised resources while Keess is bootstrapping may not be detected and synchronised. Restarting the service should catch up if synchronisation is found to be behind as a result.
Keess uses Kubernetes labels and annotations to manage synchronization of Secrets and ConfigMaps.

#### Enable Synchronization

Add a label to your Secret or ConfigMap to indicate the synchronization type:

- For namespace synchronization: `keess.powerhrg.com/sync: namespace`
- For cluster synchronization: `keess.powerhrg.com/sync: cluster`

#### Namespace Synchronization

Configure which namespaces to synchronize with:

- All namespaces: `keess.powerhrg.com/namespaces-names: all`
- Specific namespaces: `keess.powerhrg.com/namespaces-names: namespacea, namespaceb`
- Based on labels: `keess.powerhrg.com/namespace-label: keess.powerhrg.com/sync="true"`

#### Cluster Synchronization

Specify the remote clusters for synchronization: `keess.powerhrg.com/clusters: clustera, clusterb`

## Contributing

Contributions are welcome! Please refer to our [Contributing Guidelines](CONTRIBUTING.md) for more information.

## Support

If you encounter any issues or have questions, please file an issue on the [GitHub Issues page](https://github.com/your-repo/keess/issues).

## License

Keess is open-source software licensed under the MIT license. See the [LICENSE](LICENSE) file for details.

## Local testing
We will use [kind](https://kind.sigs.k8s.io/) for this

First of all, create 2 clusters:
```
make create-local-clusters
```

Now build and run the application locally pointing to these new clusters:
```
make docker-build local-docker-run
```

To execute the local test:
```
make local-test
```

If you want to investigate the cluster you can do it by:
```
kubectl cluster-info --context kind-source-cluster --kubeconfig test/kubeconfig
kubectl cluster-info --context kind-destination-cluster --kubeconfig test/kubeconfig
```

Once we are done with the test and don't need the local clusters anymore you can delete them with
```
make delete-local-clusters
```
Loading

0 comments on commit 00d9f9a

Please sign in to comment.