Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ The available retrievers are:
- **Local file**
- **Google Cloud Storage**
- **Kubernetes ConfigMaps**
- **Kubernetes Secrets**
- **MongoDB**
- **Redis**
- **BitBucket**
Expand Down
42 changes: 29 additions & 13 deletions cmdhelpers/retrieverconf/init/retriever_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"time"

awsConf "github.com/aws/aws-sdk-go-v2/config"
"k8s.io/client-go/rest"

Check failure on line 9 in cmdhelpers/retrieverconf/init/retriever_init.go

View workflow job for this annotation

GitHub Actions / Lint

File is not properly formatted (gci)

"github.com/thomaspoignant/go-feature-flag/cmdhelpers/retrieverconf"
"github.com/thomaspoignant/go-feature-flag/retriever"
azblobretriever "github.com/thomaspoignant/go-feature-flag/retriever/azblobstorageretriever"
Expand All @@ -20,26 +22,26 @@
"github.com/thomaspoignant/go-feature-flag/retriever/postgresqlretriever"
"github.com/thomaspoignant/go-feature-flag/retriever/redisretriever"
"github.com/thomaspoignant/go-feature-flag/retriever/s3retrieverv2"
"k8s.io/client-go/rest"
)

// retrieverFactory defines the signature for retriever factory functions
type retrieverFactory func(*retrieverconf.RetrieverConf, time.Duration) (retriever.Retriever, error)

// retrieverFactories maps retriever kinds to their factory functions
var retrieverFactories = map[retrieverconf.RetrieverKind]retrieverFactory{
retrieverconf.GitHubRetriever: createGitHubRetriever,
retrieverconf.GitlabRetriever: createGitlabRetriever,
retrieverconf.BitbucketRetriever: createBitbucketRetriever,
retrieverconf.FileRetriever: createFileRetriever,
retrieverconf.S3Retriever: createS3Retriever,
retrieverconf.HTTPRetriever: createHTTPRetriever,
retrieverconf.GoogleStorageRetriever: createGoogleStorageRetriever,
retrieverconf.KubernetesRetriever: createKubernetesRetriever,
retrieverconf.MongoDBRetriever: createMongoDBRetriever,
retrieverconf.RedisRetriever: createRedisRetriever,
retrieverconf.AzBlobStorageRetriever: createAzBlobStorageRetriever,
retrieverconf.PostgreSQLRetriever: createPostgreSQLRetriever,
retrieverconf.GitHubRetriever: createGitHubRetriever,
retrieverconf.GitlabRetriever: createGitlabRetriever,
retrieverconf.BitbucketRetriever: createBitbucketRetriever,
retrieverconf.FileRetriever: createFileRetriever,
retrieverconf.S3Retriever: createS3Retriever,
retrieverconf.HTTPRetriever: createHTTPRetriever,
retrieverconf.GoogleStorageRetriever: createGoogleStorageRetriever,
retrieverconf.KubernetesRetriever: createKubernetesRetriever,
retrieverconf.KubernetesSecretRetriever: createKubernetesSecretRetriever,
retrieverconf.MongoDBRetriever: createMongoDBRetriever,
retrieverconf.RedisRetriever: createRedisRetriever,
retrieverconf.AzBlobStorageRetriever: createAzBlobStorageRetriever,
retrieverconf.PostgreSQLRetriever: createPostgreSQLRetriever,
}

// InitRetriever initialize the retriever based on the configuration
Expand Down Expand Up @@ -151,6 +153,20 @@
}, nil
}

func createKubernetesSecretRetriever(
c *retrieverconf.RetrieverConf, _ time.Duration) (retriever.Retriever, error) {
client, err := rest.InClusterConfig()
if err != nil {
return nil, err
}
return &k8sretriever.SecretRetriever{
Namespace: c.Namespace,
SecretName: c.Secret,
SecretKey: c.Key,
ClientConfig: *client,
}, nil
}

func createMongoDBRetriever(c *retrieverconf.RetrieverConf, _ time.Duration) (retriever.Retriever, error) {
return &mongodbretriever.Retriever{Database: c.Database, URI: c.URI, Collection: c.Collection}, nil
}
Expand Down
30 changes: 16 additions & 14 deletions cmdhelpers/retrieverconf/retriever_conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type RetrieverConf struct {
Item string `mapstructure:"item" koanf:"item"`
Namespace string `mapstructure:"namespace" koanf:"namespace"`
ConfigMap string `mapstructure:"configmap" koanf:"configmap"`
Secret string `mapstructure:"secret" koanf:"secret"`
Key string `mapstructure:"key" koanf:"key"`
BaseURL string `mapstructure:"baseUrl" koanf:"baseurl"`
AuthToken string `mapstructure:"token" koanf:"token"`
Expand Down Expand Up @@ -199,26 +200,27 @@ func (c *RetrieverConf) validateAzBlobStorageRetriever() error {
type RetrieverKind string

const (
HTTPRetriever RetrieverKind = "http"
GitHubRetriever RetrieverKind = "github"
GitlabRetriever RetrieverKind = "gitlab"
S3Retriever RetrieverKind = "s3"
FileRetriever RetrieverKind = "file"
GoogleStorageRetriever RetrieverKind = "googleStorage"
KubernetesRetriever RetrieverKind = "configmap"
MongoDBRetriever RetrieverKind = "mongodb"
RedisRetriever RetrieverKind = "redis"
BitbucketRetriever RetrieverKind = "bitbucket"
AzBlobStorageRetriever RetrieverKind = "azureBlobStorage"
PostgreSQLRetriever RetrieverKind = "postgresql"
HTTPRetriever RetrieverKind = "http"
GitHubRetriever RetrieverKind = "github"
GitlabRetriever RetrieverKind = "gitlab"
S3Retriever RetrieverKind = "s3"
FileRetriever RetrieverKind = "file"
GoogleStorageRetriever RetrieverKind = "googleStorage"
KubernetesRetriever RetrieverKind = "configmap"
KubernetesSecretRetriever RetrieverKind = "secret"
MongoDBRetriever RetrieverKind = "mongodb"
RedisRetriever RetrieverKind = "redis"
BitbucketRetriever RetrieverKind = "bitbucket"
AzBlobStorageRetriever RetrieverKind = "azureBlobStorage"
PostgreSQLRetriever RetrieverKind = "postgresql"
)

// IsValid is checking if the value is part of the enum
func (r RetrieverKind) IsValid() error {
switch r {
case HTTPRetriever, GitHubRetriever, GitlabRetriever, S3Retriever, RedisRetriever,
FileRetriever, GoogleStorageRetriever, KubernetesRetriever, MongoDBRetriever,
BitbucketRetriever, AzBlobStorageRetriever, PostgreSQLRetriever:
FileRetriever, GoogleStorageRetriever, KubernetesRetriever, KubernetesSecretRetriever,
MongoDBRetriever, BitbucketRetriever, AzBlobStorageRetriever, PostgreSQLRetriever:
return nil
}
return fmt.Errorf("invalid retriever: kind \"%s\" is not supported", r)
Expand Down
9 changes: 9 additions & 0 deletions examples/retriever_k8s_secret/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM golang:1.21

ARG VERSION=127.0.0.1

WORKDIR /go/src/app
COPY . /go/src/app

RUN go build -o /goff-test-k8s-secret /go/src/app/examples/retriever_k8s_secret/main.go
CMD ["/goff-test-k8s-secret"]
70 changes: 70 additions & 0 deletions examples/retriever_k8s_secret/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Kubernetes config map example

This example contains everything you need to use a **`configmap`** as the source for your flags.
We will use minikube to test the solution, but it works the same in your cluster.

As you can see the `main.go` file contains a basic HTTP server that expose an API that use your flags.
For this example we are using a `InClusterConfig` because we will run the service inside kubernetes.

## How to setup the example
_All commands should be run in the root level of the repository._

1. Load all dependencies

```shell
make vendor
```

2. Create a minikube environment in your machine:

```shell
minikube start --vm
```

3. Use the minikube docker cli in your shell

```shell
eval $(minikube docker-env)
```

4. Build the docker image of the service

```shell
docker build -f examples/retriever_configmap/Dockerfile -t goff-test-configmap .
```

5. Create a `configmap` based on your `go-feature-flag` config file

```shell
kubectl create configmap goff --from-file=examples/retriever_configmap/flags.goff.yaml
```

6. Deploy your service to your kubernetes instance

```shell
kubectl apply -f examples/retriever_configmap/k8s-manifests.yaml
```

7. Forward the port to the service

```shell
kubectl port-forward $(kubectl get pod | grep "goff-test-configmap" | cut -d ' ' -f1) 9090:8080
```

8. Access to the service and check the values for different users

```shell
curl http://localhost:9090/
```

9. Play with the values in the `go-feature-flag` config file

```shell
kubectl edit configmap goff
```

10. Delete your minikube instance

```shell
minikube delete
```
22 changes: 22 additions & 0 deletions examples/retriever_k8s_secret/flags.goff.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
new-admin-access:
variations:
default_var: false
false_var: false
true_var: true
defaultRule:
percentage:
false_var: 70
true_var: 30

flag-only-for-admin:
variations:
default_var: false
false_var: false
true_var: true
targeting:
- query: admin eq true
percentage:
false_var: 0
true_var: 100
defaultRule:
variation: default_var
73 changes: 73 additions & 0 deletions examples/retriever_k8s_secret/k8s-manifests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: goff-sa

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: goff-sa
namespace: default
rules:
- apiGroups: [""]
resources:
- secrets
- namespaces
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: goff-sa
subjects:
- kind: ServiceAccount
name: goff-sa
roleRef:
kind: ClusterRole
name: goff-sa
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "2"
labels:
app: goff-test-k8s-secret
name: goff-test-k8s-secret
namespace: default
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: goff-test-k8s-secret
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: goff-test-k8s-secret
spec:
containers:
- image: goff-test-k8s-secret:latest
imagePullPolicy: Never
name: goff-test-k8s-secret
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
serviceAccountName: goff-sa
Loading
Loading