diff --git a/README.md b/README.md index 40bc8d1..9dc8d6c 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,15 @@ Ngrok operator provide developer easy access to private Kubernetes cluster for testing purpose via ngrok. Automate the creation of ngrok tunnel via CRDs! ### Feature -- [x] basic ngrok feature - [x] support HTTP -- [ ] support TCP -- [ ] support Costum Configuration +- [x] support TCP +- [x] support costum configuration + - [x] custom domain + - [x] custom TCP address + - [x] custom region + - [x] enable/disable inspection + - [x] support HTTP auth + - [ ] service for ngrok object (dashboard related) ### Developing ngrok-operator This operator build based on [operator-sdk](https://sdk.operatorframework.io/docs/install-operator-sdk/). To build this operator, you need [operator-sdk](https://sdk.operatorframework.io/docs/install-operator-sdk/). @@ -38,11 +43,13 @@ kubectl apply -f examples/helloworld/ ``` kubectl get ngrok --all-namespaces NAMESPACE NAME STATUS URL -default nginx-ngrok created https://d5150f7c3588.ngrok.io -helloworld helloworld-ngrok created https://fa03f71fbe18.ngrok.io +default nginx-ngrok created https://9496e56ed0bc.ngrok.io +default nginx-ngrok-full created https://ngrok.zufardhiyaulhaq.com +helloworld helloworld-ngrok created https://d00ba8cb0b95.ngrok.io ``` - access the URL ``` https://d5150f7c3588.ngrok.io +https://ngrok.zufardhiyaulhaq.com https://fa03f71fbe18.ngrok.io/hello ``` diff --git a/build/Dockerfile b/build/Dockerfile index ebfafe8..4dc3f82 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -8,6 +8,8 @@ ENV OPERATOR=/usr/local/bin/ngrok-operator \ COPY build/_output/bin/ngrok-operator ${OPERATOR} COPY build/bin /usr/local/bin +COPY templates /templates + RUN /usr/local/bin/user_setup ENTRYPOINT ["/usr/local/bin/entrypoint"] diff --git a/charts/Chart.yaml b/charts/Chart.yaml index 12b1d84..7584b33 100644 --- a/charts/Chart.yaml +++ b/charts/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v1 name: ngrok-operator type: application -version: 0.0.1 -appVersion: 0.0.1 +version: 0.0.2 +appVersion: 0.0.2 description: ngrok-operator for managing ngrok lifecycle home: https://github.com/zufardhiyaulhaq/ngrok-operator keywords: diff --git a/charts/README.md b/charts/README.md index d22ef93..ca778c4 100644 --- a/charts/README.md +++ b/charts/README.md @@ -4,7 +4,7 @@ Helm chart for ngrok-operators ### Installing the charts From root directory of ngrok-operator. Please edit the values.yaml inside charts before applying. ``` -helm install ./charts --name ngrok-operator +helm install ./charts --name-template ngrok-operator ``` ### Configuration @@ -12,7 +12,7 @@ helm install ./charts --name ngrok-operator | Parameter | Description | Default | |-|-| -| | operator.image | Image for ngrok-operator | zufardhiyaulhaq/ngrok-operator | -| operator.tag | Tag for image ngrok-operator | 0.0.1 | +| operator.tag | Tag for image ngrok-operator | 0.0.2 | | operator.pullPolicy | pullPolicy | Always | | operator.replica | number of replica | 1 | diff --git a/charts/crds/crds.yaml b/charts/crds/crds.yaml index 48df6f8..0898f9c 100644 --- a/charts/crds/crds.yaml +++ b/charts/crds/crds.yaml @@ -40,9 +40,36 @@ spec: spec: description: NgrokSpec defines the desired state of Ngrok properties: + auth: + type: string + authtoken: + type: string + hostname: + type: string + inspect: + default: false + type: boolean port: format: int32 type: integer + protocol: + default: http + enum: + - http + - tcp + type: string + region: + enum: + - us + - eu + - ap + - au + - sa + - jp + - in + type: string + remote_addr: + type: string service: type: string required: diff --git a/charts/values.yaml b/charts/values.yaml index 755b0ef..df478f6 100644 --- a/charts/values.yaml +++ b/charts/values.yaml @@ -2,7 +2,7 @@ operator: # image of ngrok-operator image: "zufardhiyaulhaq/ngrok-operator" # tag of ngrok-operator image - tag: "0.0.1" + tag: "0.0.2" # pullPolicy for operator image pullPolicy: "Always" # number of replica for deployment diff --git a/deploy/crds/ngrok.com_ngroks_crd.yaml b/deploy/crds/ngrok.com_ngroks_crd.yaml index 48df6f8..0898f9c 100644 --- a/deploy/crds/ngrok.com_ngroks_crd.yaml +++ b/deploy/crds/ngrok.com_ngroks_crd.yaml @@ -40,9 +40,36 @@ spec: spec: description: NgrokSpec defines the desired state of Ngrok properties: + auth: + type: string + authtoken: + type: string + hostname: + type: string + inspect: + default: false + type: boolean port: format: int32 type: integer + protocol: + default: http + enum: + - http + - tcp + type: string + region: + enum: + - us + - eu + - ap + - au + - sa + - jp + - in + type: string + remote_addr: + type: string service: type: string required: diff --git a/examples/helloworld/ngrok.yaml b/examples/helloworld/ngrok.yaml index 40b136d..83700c6 100644 --- a/examples/helloworld/ngrok.yaml +++ b/examples/helloworld/ngrok.yaml @@ -4,9 +4,15 @@ metadata: name: helloworld-ngrok namespace: helloworld spec: + # protocol used, currently support http & tcp + # tcp is less tested, please create issue + # default is http + protocol: http + # service section represent # the service name in the same namespace service: helloworld-service + # port section represent # the service port in the same namespace port: 5000 diff --git a/examples/http-full-configuration/deployment.yaml b/examples/http-full-configuration/deployment.yaml new file mode 100644 index 0000000..5398d5f --- /dev/null +++ b/examples/http-full-configuration/deployment.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + namespace: default +spec: + selector: + matchLabels: + run: nginx + replicas: 1 + template: + metadata: + labels: + run: nginx + spec: + containers: + - name: nginx + image: nginx + ports: + - containerPort: 80 diff --git a/examples/http-full-configuration/ngrok.yaml b/examples/http-full-configuration/ngrok.yaml new file mode 100644 index 0000000..91195e0 --- /dev/null +++ b/examples/http-full-configuration/ngrok.yaml @@ -0,0 +1,41 @@ +apiVersion: ngrok.com/v1alpha1 +kind: Ngrok +metadata: + name: nginx-ngrok-full + namespace: default +spec: + # ngrok authtoken + authtoken: your-auth-token + + # protocol used, currently support http & tcp + # tcp is less tested, please create issue + # if there is an issue with tcp + protocol: http + + # region where ngrok run + # refer to the docs + # https://ngrok.com/docs + region: ap + + # auth protect your http + # with user password combination + # : + auth: user:password + + # enable inspection + # only works for http protocol + inspect: true + + # this supported starting with basic plan + # using custom hostname require to set authtoken + # the behaviour when set `hostname` with free account + # is unknown! + hostname: ngrok.zufardhiyaulhaq.com + + # service section represent + # the service name in the same namespace + service: nginx-service + + # port section represent + # the service port in the same namespace + port: 80 diff --git a/examples/http-full-configuration/service.yaml b/examples/http-full-configuration/service.yaml new file mode 100644 index 0000000..91a16dc --- /dev/null +++ b/examples/http-full-configuration/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: nginx-service + namespace: default +spec: + ports: + - port: 80 + protocol: TCP + selector: + run: nginx diff --git a/examples/nginx/ngrok.yaml b/examples/nginx/ngrok.yaml index 90231cb..c7514b2 100644 --- a/examples/nginx/ngrok.yaml +++ b/examples/nginx/ngrok.yaml @@ -4,9 +4,15 @@ metadata: name: nginx-ngrok namespace: default spec: + # protocol used, currently support http & tcp + # tcp is less tested, please create issue + # default is http + protocol: http + # service section represent # the service name in the same namespace service: nginx-service + # port section represent # the service port in the same namespace port: 80 diff --git a/pkg/apis/ngrok/v1alpha1/ngrok_types.go b/pkg/apis/ngrok/v1alpha1/ngrok_types.go index 96e2aa3..8e02434 100644 --- a/pkg/apis/ngrok/v1alpha1/ngrok_types.go +++ b/pkg/apis/ngrok/v1alpha1/ngrok_types.go @@ -8,6 +8,31 @@ import ( type NgrokSpec struct { Service string `json:"service"` Port int32 `json:"port"` + + // +kubebuilder:validation:Enum=http;tcp + // +kubebuilder:default:=http + // +optional + Protocol string `json:"protocol"` + + // +optional + AuthToken string `json:"authtoken"` + + // +optional + Auth string `json:"auth"` + + // +optional + Hostname string `json:"hostname"` + + // +optional + RemoteAddr string `json:"remote_addr"` + + // +kubebuilder:validation:Enum=us;eu;ap;au;sa;jp;in + // +optional + Region string `json:"region"` + + // +kubebuilder:default:=false + // +optional + Inspect bool `json:"inspect"` } // NgrokStatus defines the observed state of Ngrok diff --git a/pkg/controller/ngrok/ngrok_controller.go b/pkg/controller/ngrok/ngrok_controller.go index 993f510..3d7558b 100644 --- a/pkg/controller/ngrok/ngrok_controller.go +++ b/pkg/controller/ngrok/ngrok_controller.go @@ -1,11 +1,12 @@ package ngrok import ( + "bytes" "context" "io/ioutil" "net/http" "regexp" - "strconv" + "text/template" "time" ngrokv1alpha1 "github.com/zufardhiyaulhaq/ngrok-operator/pkg/apis/ngrok/v1alpha1" @@ -182,7 +183,7 @@ func (r *ReconcileNgrok) Reconcile(request reconcile.Request) (reconcile.Result, bodyString := string(body) - matcher, err := regexp.Compile(`https(.*?)io`) + matcher, err := regexp.Compile(`https://.[^"]+`) if err != nil { return reconcile.Result{}, err } @@ -202,9 +203,7 @@ func (r *ReconcileNgrok) Reconcile(request reconcile.Request) (reconcile.Result, func newNgrokConfigMap(cr *ngrokv1alpha1.Ngrok) *corev1.ConfigMap { configMapData := make(map[string]string, 0) - ngrokProperties := ` -web_addr: 0.0.0.0:4040` - configMapData["ngrok.conf"] = ngrokProperties + configMapData["ngrok.conf"] = generateConfiguration(cr) labels := map[string]string{ "app": cr.Name, @@ -236,7 +235,7 @@ func newNgrokPod(cr *ngrokv1alpha1.Ngrok) *corev1.Pod { { Name: "ngrok", Image: "wernight/ngrok", - Command: []string{"ngrok", "http", "--config", "/ngrok/ngrok.conf", cr.Spec.Service + ":" + strconv.FormatInt(int64(cr.Spec.Port), 10)}, + Command: []string{"ngrok", "start", "--config", "/ngrok/ngrok.conf", "--all"}, Ports: []corev1.ContainerPort{ {ContainerPort: ngrokPort}, }, @@ -263,3 +262,20 @@ func newNgrokPod(cr *ngrokv1alpha1.Ngrok) *corev1.Pod { }, } } + +func generateConfiguration(cr *ngrokv1alpha1.Ngrok) string { + var output bytes.Buffer + tmpl := "templates/configuration.tmpl" + + tpl, err := template.ParseFiles(tmpl) + if err != nil { + panic(err) + } + + err = tpl.Execute(&output, cr) + if err != nil { + panic(err) + } + + return output.String() +} diff --git a/templates/configuration.tmpl b/templates/configuration.tmpl new file mode 100644 index 0000000..20f26e8 --- /dev/null +++ b/templates/configuration.tmpl @@ -0,0 +1,32 @@ +web_addr: 0.0.0.0:4040 + +{{if .Spec.AuthToken }} +authtoken: {{ .Spec.AuthToken }} +{{end}} + +{{if .Spec.Region }} +region: {{ .Spec.Region }} +{{end}} + +tunnels: + app: + proto: {{ .Spec.Protocol }} + addr: {{ .Spec.Service }}:{{ .Spec.Port }} + + {{if eq .Spec.Protocol "http"}} + inspect: {{ .Spec.Inspect }} + + {{if .Spec.Auth }} + auth: {{ .Spec.Auth }} + {{ end }} + + {{if .Spec.AuthToken }}{{if .Spec.Hostname }} + hostname: {{ .Spec.Hostname }} + {{end}}{{end}} + {{end}} + + {{if eq .Spec.Protocol "tcp"}} + {{if .Spec.AuthToken }}{{if .Spec.Hostname }} + remote_addr: {{ .Spec.Hostname }} + {{end}}{{end}} + {{end}}