-
-
Notifications
You must be signed in to change notification settings - Fork 133
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1044928
commit 0a9378d
Showing
12 changed files
with
622 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# Kubernetes configmaps | ||
The [**KubernetesRetriever**](https://pkg.go.dev/github.com/thomaspoignant/go-feature-flag#KubernetesRetriever) | ||
will access flags in a Kubernetes ConfigMap via the [Kubernetes Go client](https://github.com/kubernetes/client-go) | ||
|
||
## Example | ||
```go linenums="1" | ||
import ( | ||
restclient "k8s.io/client-go/rest" | ||
) | ||
|
||
config, _ := restclient.InClusterConfig() | ||
err = ffclient.Init(ffclient.Config{ | ||
PollingInterval: 3 * time.Second, | ||
Retriever: &ffclient.KubernetesRetriever{ | ||
Path: "file-example.yaml", | ||
Namespace: "default" | ||
ConfigMapName: "my-configmap" | ||
Key: "somekey.yml" | ||
ClientConfig: &config | ||
}, | ||
}) | ||
defer ffclient.Close() | ||
``` | ||
|
||
## Configuration fields | ||
To configure your retriever: | ||
|
||
| Field | Description | | ||
|---------------------|----------------------------------------------------| | ||
| **`Namespace`** | The namespace of the ConfigMap. | | ||
| **`ConfigMapName`** | The name of the ConfigMap. | | ||
| **`Key`** | The key within the ConfigMap storing the flags. | | ||
| **`ClientConfig`** | The configuration object for the Kubernetes client | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
FROM golang:1.18 AS build | ||
|
||
ARG VERSION=127.0.0.1 | ||
|
||
WORKDIR /go/src/app | ||
COPY . /go/src/app | ||
|
||
RUN go build -o /go/src/app/examples/retriever_configmap/goff-test-configmap /go/src/app/examples/retriever_configmap/main.go | ||
|
||
FROM gcr.io/distroless/base-debian11:latest | ||
COPY --from=build /go/src/app/examples/retriever_configmap/goff-test-configmap / | ||
CMD ["/goff-test-configmap"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
new-admin-access: | ||
percentage: 30 | ||
true: true | ||
false: false | ||
default: false | ||
|
||
flag-only-for-admin: | ||
rule: admin eq true | ||
percentage: 100 | ||
true: true | ||
false: false | ||
default: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: | ||
- configmaps | ||
- 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-configmap | ||
name: goff-test-configmap | ||
namespace: default | ||
spec: | ||
progressDeadlineSeconds: 600 | ||
replicas: 1 | ||
revisionHistoryLimit: 10 | ||
selector: | ||
matchLabels: | ||
app: goff-test-configmap | ||
strategy: | ||
rollingUpdate: | ||
maxSurge: 25% | ||
maxUnavailable: 25% | ||
type: RollingUpdate | ||
template: | ||
metadata: | ||
labels: | ||
app: goff-test-configmap | ||
spec: | ||
containers: | ||
- image: goff-test-configmap:latest | ||
imagePullPolicy: Never | ||
name: goff-test-configmap | ||
resources: {} | ||
terminationMessagePath: /dev/termination-log | ||
terminationMessagePolicy: File | ||
dnsPolicy: ClusterFirst | ||
restartPolicy: Always | ||
schedulerName: default-scheduler | ||
securityContext: {} | ||
terminationGracePeriodSeconds: 30 | ||
serviceAccountName: goff-sa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/thomaspoignant/go-feature-flag/ffuser" | ||
"k8s.io/client-go/rest" | ||
"log" | ||
"net/http" | ||
"os" | ||
"time" | ||
|
||
ffclient "github.com/thomaspoignant/go-feature-flag" | ||
) | ||
|
||
func main() { | ||
// Init ffclient with an http retriever. | ||
config, err := rest.InClusterConfig() | ||
if err != nil { | ||
panic(err.Error()) | ||
} | ||
|
||
err = ffclient.Init(ffclient.Config{ | ||
PollingInterval: 10 * time.Second, | ||
Logger: log.New(os.Stdout, "", 0), | ||
Context: context.Background(), | ||
Retriever: &ffclient.KubernetesRetriever{ | ||
Namespace: "default", | ||
ConfigMapName: "goff", | ||
Key: "flags.yaml", | ||
ClientConfig: *config, | ||
}}) | ||
|
||
// Check init errors. | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
// defer closing ffclient | ||
defer ffclient.Close() | ||
|
||
http.HandleFunc("/", handler) | ||
http.ListenAndServe(":8080", nil) | ||
|
||
} | ||
|
||
func handler(w http.ResponseWriter, req *http.Request) { | ||
user1 := ffuser.NewAnonymousUser("aea2fdc1-b9a0-417a-b707-0c9083de68e3") | ||
user2 := ffuser.NewUser("332460b9-a8aa-4f7a-bc5d-9cc33632df9a") | ||
user3 := ffuser.NewUserBuilder("785a14bf-d2c5-4caa-9c70-2bbc4e3732a5"). | ||
AddCustom("email", "[email protected]"). | ||
AddCustom("firstname", "John"). | ||
AddCustom("lastname", "Doe"). | ||
AddCustom("admin", true). | ||
Build() | ||
|
||
// --- test flag with no rule | ||
// user1 | ||
user1HasAccessToNewAdmin, err := ffclient.BoolVariation("new-admin-access", user1, false) | ||
if err != nil { | ||
// we log the error, but we still have a meaningful value in user1HasAccessToNewAdmin (the default value). | ||
log.Printf("something went wrong when getting the flag: %v", err) | ||
} | ||
if user1HasAccessToNewAdmin { | ||
fmt.Fprintf(w, "user1 has access to the new admin\n") | ||
} | ||
|
||
// user2 | ||
user2HasAccessToNewAdmin, err := ffclient.BoolVariation("new-admin-access", user2, false) | ||
if err != nil { | ||
// we log the error, but we still have a meaningful value in hasAccessToNewAdmin (the default value). | ||
fmt.Fprintf(w, "something went wrong when getting the flag: %v\n", err) | ||
} | ||
if !user2HasAccessToNewAdmin { | ||
fmt.Fprintf(w, "user2 has not access to the new admin\n") | ||
} | ||
|
||
// --- test flag with rule only for admins | ||
// user 1 is not admin so should not access to the flag | ||
user1HasAccess, _ := ffclient.BoolVariation("flag-only-for-admin", user1, false) | ||
if !user1HasAccess { | ||
fmt.Fprintf(w, "user1 is not admin so no access to the flag\n") | ||
} | ||
|
||
// user 3 is admin and the flag apply to this key. | ||
if user3HasAccess, _ := ffclient.BoolVariation("flag-only-for-admin", user3, false); user3HasAccess { | ||
fmt.Fprintf(w, "user 3 is admin and the flag apply to this key.\n") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.