├── helm
├── charts
│ ├── jaeger
│ │ ├── values.yaml
│ │ ├── templates
│ │ │ ├── namespace.yml
│ │ │ ├── cassandra.yml
│ │ │ ├── ui.yml
│ │ │ ├── collector.yml
│ │ │ ├── agent.yml
│ │ │ └── kubernetes.yml
│ │ ├── .helmignore
│ │ └── Chart.yaml
│ ├── grafana
│ │ ├── Chart.yaml
│ │ ├── templates
│ │ │ ├── dashboards-configmap.yaml
│ │ │ ├── configmap.yaml
│ │ │ ├── secret.yaml
│ │ │ ├── _helpers.tpl
│ │ │ ├── ingress.yaml
│ │ │ ├── pvc.yaml
│ │ │ ├── svc.yaml
│ │ │ ├── job.yaml
│ │ │ ├── NOTES.txt
│ │ │ └── deployment.yaml
│ │ └── README.md
│ └── influxdb
│ │ ├── Chart.yaml
│ │ ├── .helmignore
│ │ ├── templates
│ │ ├── _helpers.tpl
│ │ ├── secret.yaml
│ │ ├── pvc.yaml
│ │ ├── NOTES.txt
│ │ ├── service.yaml
│ │ ├── post-install-set-auth.yaml
│ │ └── deployment.yaml
│ │ └── README.md
├── templates
│ ├── serviceAccount.yaml
│ ├── service.yaml
│ ├── hpa.yaml
│ ├── clusterRoleBinding.yaml
│ ├── clusterRole.yaml
│ ├── config.yaml
│ ├── deployment.yaml
│ ├── decryptSecret.yaml
│ └── tlsSecret.yaml
├── values.yaml
├── requirements.yaml
└── Chart.yaml
├── .dockerignore
├── logo
├── logo.png
├── name_black.png
├── name_white.png
├── logo_with_name.png
├── name_black.svg
└── name_white.svg
├── assets
├── grafana.png
├── jaeger1.png
└── jaeger2.png
├── scripts
├── updateLicenses.sh
├── install.sh
├── helm-permissions.sh
├── cover.sh
└── updateLicense.py
├── .gitignore
├── docs
├── docs.md
├── apikeybinding.md
└── apikey.md
├── travis
├── upload-to-docker.sh
└── build-docker-images.sh
├── glide.yaml
├── examples
├── exampleOne.yaml
├── exampleTwo.yaml
├── exampleThree.yaml
├── exampleSix.yaml
├── exampleEight.yaml
├── exampleNine.yaml
└── exampleSeven.yaml
├── config.toml
├── cmd
├── start_test.go
├── root.go
├── version_test.go
└── version.go
├── spec
├── endpoints.go
└── store.go
├── Dockerfile
├── config
├── process.go
├── tracing.go
├── plugins.go
├── tls.go
├── server.go
├── flag.go
├── flag_test.go
├── analytics.go
└── proxy.go
├── utils
├── errors_test.go
├── errors.go
├── utils.go
└── utils_test.go
├── main.go
├── flow
├── step.go
├── flow.go
└── flow_test.go
├── server
├── server_test.go
└── udp_test.go
├── metrics
├── metrics.go
└── metrics_test.go
├── handlers
├── logger.go
├── logger_test.go
└── incoming.go
├── tracer
├── jaeger_test.go
├── jaeger.go
└── tags.go
├── steps
├── pluginsonrequest_test.go
├── pluginsonresponse_test.go
├── writeresponse.go
├── writeresponse_test.go
├── mockplugins_test.go
├── pluginsonrequest.go
├── pluginsonresponse.go
├── validateproxy.go
├── mockservice.go
└── validateproxy_test.go
├── Makefile
├── .travis.yml
├── controller
├── controller.go
├── tpr.go
└── tpr_test.go
└── plugins
└── plugin.go
/helm/charts/jaeger/values.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | ca-certificates.crt
2 | kanali
3 | vendor/
4 | .idea/
--------------------------------------------------------------------------------
/logo/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/northwesternmutual/kanali/HEAD/logo/logo.png
--------------------------------------------------------------------------------
/assets/grafana.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/northwesternmutual/kanali/HEAD/assets/grafana.png
--------------------------------------------------------------------------------
/assets/jaeger1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/northwesternmutual/kanali/HEAD/assets/jaeger1.png
--------------------------------------------------------------------------------
/assets/jaeger2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/northwesternmutual/kanali/HEAD/assets/jaeger2.png
--------------------------------------------------------------------------------
/logo/name_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/northwesternmutual/kanali/HEAD/logo/name_black.png
--------------------------------------------------------------------------------
/logo/name_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/northwesternmutual/kanali/HEAD/logo/name_white.png
--------------------------------------------------------------------------------
/logo/logo_with_name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/northwesternmutual/kanali/HEAD/logo/logo_with_name.png
--------------------------------------------------------------------------------
/scripts/updateLicenses.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | python scripts/updateLicense.py $(git ls-files "*\.go" | grep -v thrift-gen | grep -v tracetest)
6 |
--------------------------------------------------------------------------------
/helm/charts/jaeger/templates/namespace.yml:
--------------------------------------------------------------------------------
1 | # ---
2 | #
3 | # apiVersion: v1
4 | # kind: Namespace
5 | # metadata:
6 | # name: tracing
7 | # labels:
8 | # name: tracing
--------------------------------------------------------------------------------
/helm/templates/serviceAccount.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: v1
4 | kind: ServiceAccount
5 | metadata:
6 | labels:
7 | app: kanali
8 | name: kanali
9 | namespace: default
--------------------------------------------------------------------------------
/helm/charts/jaeger/.helmignore:
--------------------------------------------------------------------------------
1 | templates/agent.yml
2 | templates/cassandra.yml
3 | templates/collector.yml
4 | templates/jaeger.yml
5 | templates/namespace.yml
6 | templates/ui.yml
7 | templates/.DS_Store
--------------------------------------------------------------------------------
/helm/values.yaml:
--------------------------------------------------------------------------------
1 |
2 | imageRegistry: northwesternmutual
3 |
4 | dockerImageTag: v1.2.3
5 |
6 | pullPolicy: Always
7 |
8 | decryptKeySecretName: kanali-key-decription
9 |
10 | kanaliConfigName: kanali-config
11 |
12 | tlsSecretName: kanali
--------------------------------------------------------------------------------
/helm/templates/service.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: v1
4 | kind: Service
5 | metadata:
6 | name: kanali
7 | namespace: default
8 | spec:
9 | selector:
10 | k8s-app: kanali
11 | ports:
12 | - name: app-port
13 | port: 8443
14 | type: NodePort
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ca-certificates.crt
2 | kanali
3 | vendor/
4 | .idea/
5 | *.out
6 | git-hack.sh
7 | *.crt
8 | *.key
9 | *.csr
10 | *.srl
11 | *.test
12 | debug
13 | launch.json
14 | settings.json
15 | kanali.wiki/
16 | *.so
17 | *.pem
18 | *.log
19 | LICENSE_*
20 | .cover/
21 | *.html
--------------------------------------------------------------------------------
/helm/charts/jaeger/Chart.yaml:
--------------------------------------------------------------------------------
1 | description: simple helm chart for Jaeger
2 | engine: gotpl
3 | home: http://jaeger.readthedocs.io/en/latest/
4 | keywords:
5 | - jaeger
6 | - uber
7 | - opentracing
8 | maintainers:
9 | - email: frankgreco@northwesternmutual.com
10 | name: Frank B Greco Jr
11 | name: jaeger
12 | version: 1.0.0
13 |
--------------------------------------------------------------------------------
/helm/templates/hpa.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: autoscaling/v1
4 | kind: HorizontalPodAutoscaler
5 | metadata:
6 | name: kanali
7 | namespace: default
8 | spec:
9 | maxReplicas: 8
10 | minReplicas: 2
11 | scaleTargetRef:
12 | apiVersion: v1
13 | kind: Deployment
14 | name: kanali
15 | targetCPUUtilizationPercentage: 500
--------------------------------------------------------------------------------
/docs/docs.md:
--------------------------------------------------------------------------------
1 | # Documentation
2 |
3 | ## `ApiProxy`
4 |
5 | Find detailed documentation for an `ApiProxy` [here](./apiproxy.md#apiproxy).
6 |
7 | ## `ApiKey`
8 |
9 | Find detailed documentation for an `ApiKey` [here](./apikey.md#apikey).
10 |
11 | ## `ApiKeyBinding`
12 |
13 | Find detailed documentation for an `ApiKeyBinding` [here](./apikeybinding.md#apikeybinding).
--------------------------------------------------------------------------------
/helm/requirements.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | dependencies:
4 | - name: influxdb
5 | version: 0.4.1
6 | repository: https://github.com/kubernetes/charts/tree/master/stable/influxdb
7 | - name: grafana
8 | version: 0.3.6
9 | repository: https://github.com/kubernetes/charts/tree/master/stable/grafana
10 | - name: jaeger
11 | version: 1.0.0
12 | repository: file://./charts/jaeger
--------------------------------------------------------------------------------
/helm/templates/clusterRoleBinding.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: rbac.authorization.k8s.io/v1beta1
4 | kind: ClusterRoleBinding
5 | metadata:
6 | name: kanali
7 | labels:
8 | app: kanali
9 | roleRef:
10 | apiGroup: rbac.authorization.k8s.io
11 | kind: ClusterRole
12 | name: kanali
13 | subjects:
14 | - kind: ServiceAccount
15 | name: kanali
16 | namespace: default
--------------------------------------------------------------------------------
/travis/upload-to-docker.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | if [[ "$DOCKER_REPO" == "" ]]; then
6 | echo "skip docker upload, DOCKER_REPO=$DOCKER_REPO"
7 | exit 0
8 | fi
9 |
10 | if [[ "$DOCKER_TAG" == "" ]]; then
11 | echo "skip docker upload, DOCKER_TAG=$DOCKER_TAG"
12 | exit 0
13 | fi
14 |
15 | docker login -u $DOCKER_USER -p $DOCKER_PASS
16 | docker push $DOCKER_REPO:$DOCKER_TAG
17 |
--------------------------------------------------------------------------------
/helm/charts/grafana/Chart.yaml:
--------------------------------------------------------------------------------
1 | description: The leading tool for querying and visualizing time series and metrics.
2 | engine: gotpl
3 | home: https://grafana.net
4 | icon: https://raw.githubusercontent.com/grafana/grafana/master/public/img/logo_transparent_400x.png
5 | maintainers:
6 | - email: zanhsieh@gmail.com
7 | name: Ming Hsieh
8 | name: grafana
9 | sources:
10 | - https://github.com/grafana/grafana
11 | version: 0.3.6
12 |
--------------------------------------------------------------------------------
/helm/charts/influxdb/Chart.yaml:
--------------------------------------------------------------------------------
1 | description: Scalable datastore for metrics, events, and real-time analytics.
2 | engine: gotpl
3 | home: https://www.influxdata.com/time-series-platform/influxdb/
4 | keywords:
5 | - influxdb
6 | - database
7 | - timeseries
8 | maintainers:
9 | - email: jack@influxdb.com
10 | name: Jack Zampolin
11 | name: influxdb
12 | sources:
13 | - https://github.com/influxdata/influxdb
14 | version: 0.4.1
15 |
--------------------------------------------------------------------------------
/helm/charts/influxdb/.helmignore:
--------------------------------------------------------------------------------
1 | # Patterns to ignore when building packages.
2 | # This supports shell glob matching, relative path matching, and
3 | # negation (prefixed with !). Only one pattern per line.
4 | .DS_Store
5 | # Common VCS dirs
6 | .git/
7 | .gitignore
8 | .bzr/
9 | .bzrignore
10 | .hg/
11 | .hgignore
12 | .svn/
13 | # Common backup files
14 | *.swp
15 | *.bak
16 | *.tmp
17 | *~
18 | # Various IDEs
19 | .project
20 | .idea/
21 | *.tmproj
22 |
--------------------------------------------------------------------------------
/helm/charts/grafana/templates/dashboards-configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | labels:
5 | app: {{ template "grafana.fullname" . }}
6 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
7 | component: "{{ .Values.server.name }}"
8 | heritage: "{{ .Release.Service }}"
9 | release: "{{ .Release.Name }}"
10 | name: {{ template "grafana.server.fullname" . }}-dashs
11 | data:
12 | {{ toYaml .Values.serverDashboardFiles | indent 2 }}
13 |
--------------------------------------------------------------------------------
/helm/Chart.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | name: helm
4 | version: 1.0.0
5 | description: helm chart to deploy kanali
6 | keywords:
7 | - ingress
8 | - kubernetes
9 | - api
10 | - management
11 | home: "https://github.com/northwesternmutual/kanali"
12 | sources:
13 | - "https://github.com/northwesternmutual/kanali"
14 | maintainers:
15 | - name: Frank B Greco Jr
16 | email: frankgreco@northwesternmutual.com
17 | engine: gotpl
18 | appVersion: v1.0.0
19 | deprecated: false
20 |
--------------------------------------------------------------------------------
/helm/templates/clusterRole.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | kind: ClusterRole
4 | apiVersion: rbac.authorization.k8s.io/v1beta1
5 | metadata:
6 | name: kanali
7 | rules:
8 | - apiGroups: ["kanali.io"]
9 | resources: ["apikeies", "apiproxies", "apikeybindings"]
10 | verbs: ["watch"]
11 | - apiGroups: ["extensions"]
12 | resources: ["thirdpartyresources"]
13 | verbs: ["create"]
14 | - apiGroups: [""]
15 | resources: ["services", "secrets", "endpoints", "configmaps"]
16 | verbs: ["watch"]
--------------------------------------------------------------------------------
/helm/charts/grafana/templates/configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | labels:
5 | app: {{ template "grafana.fullname" . }}
6 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
7 | component: "{{ .Values.server.name }}"
8 | heritage: "{{ .Release.Service }}"
9 | release: "{{ .Release.Name }}"
10 | name: {{ template "grafana.server.fullname" . }}-config
11 | data:
12 | {{- if .Values.server.installPlugins }}
13 | grafana-install-plugins: {{ .Values.server.installPlugins | quote }}
14 | {{- end }}
15 | {{ toYaml .Values.serverConfigFile | indent 2 }}
16 |
--------------------------------------------------------------------------------
/helm/charts/influxdb/templates/_helpers.tpl:
--------------------------------------------------------------------------------
1 | {{/* vim: set filetype=mustache: */}}
2 | {{/*
3 | Expand the name of the chart.
4 | */}}
5 | {{- define "name" -}}
6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
7 | {{- end -}}
8 |
9 | {{/*
10 | Create a default fully qualified app name.
11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
12 | */}}
13 | {{- define "fullname" -}}
14 | {{- $name := default .Chart.Name .Values.nameOverride -}}
15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
16 | {{- end -}}
17 |
--------------------------------------------------------------------------------
/helm/charts/grafana/templates/secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | labels:
5 | app: {{ template "grafana.fullname" . }}
6 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
7 | heritage: "{{ .Release.Service }}"
8 | release: "{{ .Release.Name }}"
9 | name: {{ template "grafana.server.fullname" . }}
10 | type: Opaque
11 | data:
12 | {{- if .Values.server.adminPassword }}
13 | grafana-admin-password: {{ .Values.server.adminPassword | b64enc | quote }}
14 | {{- else }}
15 | grafana-admin-password: {{ randAlphaNum 10 | b64enc | quote }}
16 | {{- end }}
17 | grafana-admin-user: {{ .Values.server.adminUser | b64enc | quote }}
18 |
--------------------------------------------------------------------------------
/helm/charts/influxdb/templates/secret.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.setDefaultUser.enabled -}}
2 | apiVersion: v1
3 | kind: Secret
4 | metadata:
5 | labels:
6 | app: {{ template "fullname" . }}
7 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
8 | heritage: "{{ .Release.Service }}"
9 | release: "{{ .Release.Name }}"
10 | name: {{ template "fullname" . }}-auth
11 | data:
12 | {{- if .Values.setDefaultUser.user.password }}
13 | influxdb-password: {{ .Values.setDefaultUser.user.password | b64enc | quote }}
14 | {{- else }}
15 | influxdb-password: {{ randAscii 10 | b64enc | quote }}
16 | {{- end }}
17 | influxdb-user: {{ .Values.setDefaultUser.user.username | b64enc | quote }}
18 | {{- end -}}
19 |
--------------------------------------------------------------------------------
/scripts/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | LIGHT_BLUE='\033[1;34m'
4 | LIGHT_GREEN='\033[1;32m'
5 | NC='\033[0m'
6 |
7 | echo -e "${LIGHT_BLUE}Step 1: Verify that Helm is installed${NC}"
8 | which helm > /dev/null || $(curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get > get_helm.sh | bash)
9 |
10 | echo -e "${LIGHT_BLUE}Step 2: Deploy Helm"
11 | helm init > /dev/null
12 |
13 | echo -e "${LIGHT_BLUE}Step 3: Patch RBAC for Helm${NC}"
14 | ./scripts/helm-permissions.sh > /dev/null
15 |
16 | echo -e "${LIGHT_BLUE}Step 4: Install Kanali, Grafana, InfluxDB, and Jaeger (may take a few minutes)${NC}"
17 |
18 | while sleep 1
19 | do
20 | helm install ./helm --name kanali &>/dev/null && break || continue
21 | done
22 |
23 | echo -e "${LIGHT_GREEN}Deployment Complete!${NC}"
24 |
--------------------------------------------------------------------------------
/glide.yaml:
--------------------------------------------------------------------------------
1 | package: github.com/northwesternmutual/kanali
2 | import:
3 | - package: github.com/Sirupsen/logrus
4 | version: 0.11.5
5 | - package: github.com/spf13/pflag
6 | - package: github.com/spf13/cobra
7 | - package: github.com/spf13/viper
8 | version: v1.0.0
9 | - package: github.com/armon/go-proxyproto
10 | - package: github.com/influxdata/influxdb/client/v2
11 | - package: k8s.io/kubernetes
12 | version: v1.5.7
13 | subpackages:
14 | - pkg/api
15 | - pkg/api/errors
16 | - pkg/api/unversioned
17 | - pkg/apis/extensions
18 | - pkg/client/clientset_generated/internalclientset
19 | - pkg/client/restclient
20 | - pkg/kubectl/cmd/util
21 | - package: github.com/opentracing/opentracing-go
22 | version: 1.0.2
23 | - package: github.com/uber/jaeger-client-go
24 | version: 2.9.0
25 | testImport:
26 | - package: github.com/stretchr/testify
27 | version: v1.1.4
28 | subpackages:
29 | - assert
--------------------------------------------------------------------------------
/helm/charts/grafana/templates/_helpers.tpl:
--------------------------------------------------------------------------------
1 | {{/* vim: set filetype=mustache: */}}
2 | {{/*
3 | Expand the name of the chart.
4 | */}}
5 | {{- define "grafana.name" -}}
6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
7 | {{- end -}}
8 |
9 | {{/*
10 | Create a default fully qualified app name.
11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
12 | */}}
13 | {{- define "grafana.fullname" -}}
14 | {{- $name := default "grafana" .Values.nameOverride -}}
15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
16 | {{- end -}}
17 |
18 | {{/*
19 | Create a fully qualified server name.
20 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
21 | */}}
22 | {{- define "grafana.server.fullname" -}}
23 | {{- printf "%s-%s" .Release.Name "grafana" | trunc 63 | trimSuffix "-" -}}
24 | {{- end -}}
25 |
--------------------------------------------------------------------------------
/scripts/helm-permissions.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "{\"apiVersion\":\"v1\",\"kind\":\"ServiceAccount\",\"metadata\":{\"name\":\"tiller\",\"namespace\":\"kube-system\"}}" | kubectl apply -f -
4 | echo "{\"apiVersion\":\"rbac.authorization.k8s.io/v1beta1\",\"kind\":\"ClusterRoleBinding\",\"metadata\":{\"name\":\"tiller\"},\"roleRef\":{\"apiGroup\":\"rbac.authorization.k8s.io\",\"kind\":\"ClusterRole\",\"name\":\"cluster-admin\"},\"subjects\":[{\"kind\":\"ServiceAccount\",\"name\":\"tiller\",\"namespace\":\"kube-system\"}]}" | kubectl apply -f -
5 | kubectl -n kube-system patch deploy/tiller-deploy -p '{"spec": {"template": {"spec": {"serviceAccountName": "tiller"}}}}'
6 | echo "{\"apiVersion\":\"rbac.authorization.k8s.io/v1beta1\",\"kind\":\"ClusterRole\",\"metadata\":{\"annotations\":{\"rbac.authorization.kubernetes.io/autoupdate\":\"true\"},\"labels\":{\"kubernetes.io/bootstrapping\":\"rbac-defaults\"},\"name\":\"cluster-admin\"},\"rules\":[{\"apiGroups\":[\"*\"],\"resources\":[\"*\"],\"verbs\":[\"*\"]},{\"nonResourceURLs\":[\"*\"],\"verbs\":[\"*\"]}]}" | kubectl apply -f -
--------------------------------------------------------------------------------
/helm/charts/influxdb/templates/pvc.yaml:
--------------------------------------------------------------------------------
1 | {{- if and (.Values.persistence.enabled) (not .Values.persistence.useExisting) }}
2 | kind: PersistentVolumeClaim
3 | apiVersion: v1
4 | metadata:
5 | name: "{{- if not (empty .Values.persistence.name) }}{{ .Values.persistence.name }}{{- else }}{{ template "fullname" . }}{{- end }}"
6 | labels:
7 | app: "{{- if not (empty .Values.persistence.name) }}{{ .Values.persistence.name }}{{- else }}{{ template "fullname" . }}{{- end }}"
8 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
9 | release: "{{ .Release.Name }}"
10 | heritage: "{{ .Release.Service }}"
11 | annotations:
12 | {{- if .Values.persistence.storageClass }}
13 | volume.beta.kubernetes.io/storage-class: {{ .Values.persistence.storageClass | quote }}
14 | {{- else }}
15 | volume.alpha.kubernetes.io/storage-class: default
16 | {{- end }}
17 | spec:
18 | accessModes:
19 | - {{ .Values.persistence.accessMode | quote }}
20 | resources:
21 | requests:
22 | storage: {{ .Values.persistence.size | quote }}
23 | {{- end }}
24 |
--------------------------------------------------------------------------------
/travis/build-docker-images.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | if [[ "$TRAVIS_SECURE_ENV_VARS" == "false" ]]; then
6 | echo "skip docker upload, TRAVIS_SECURE_ENV_VARS=$TRAVIS_SECURE_ENV_VARS"
7 | exit 0
8 | fi
9 |
10 | export DOCKER_REPO="northwesternmutual/kanali"
11 |
12 | if [[ $TRAVIS_TAG =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
13 | echo "uploading docker image TAG=$TRAVIS_TAG"
14 | export DOCKER_TAG=$TRAVIS_TAG
15 | docker build --build-arg VERSION=$DOCKER_TAG -t $DOCKER_REPO:$DOCKER_TAG .
16 | bash ./travis/upload-to-docker.sh
17 | elif [ $TRAVIS_PULL_REQUEST == "false" ] && [ $TRAVIS_BRANCH == "master" ]; then
18 | echo "uploading docker image BRANCH=$TRAVIS_BRANCH"
19 | for component in latest ${TRAVIS_COMMIT::8}
20 | do
21 | export DOCKER_TAG=${component}
22 | docker build --build-arg VERSION=$DOCKER_TAG -t $DOCKER_REPO:$DOCKER_TAG .
23 | bash ./travis/upload-to-docker.sh
24 | done
25 | else
26 | echo "skip docker upload - neither master branch nor tag"
27 | exit 0
28 | fi
29 |
30 | echo "docker upload successfull"
31 | exit 0
--------------------------------------------------------------------------------
/helm/charts/grafana/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.server.ingress.enabled -}}
2 | {{- $releaseName := .Release.Name -}}
3 | {{- $servicePort := .Values.server.httpPort -}}
4 | apiVersion: extensions/v1beta1
5 | kind: Ingress
6 | metadata:
7 | annotations:
8 | {{- range $key, $value := .Values.server.ingress.annotations }}
9 | {{ $key }}: {{ $value | quote }}
10 | {{- end }}
11 | labels:
12 | app: {{ template "grafana.fullname" . }}
13 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
14 | component: "{{ .Values.server.name }}"
15 | heritage: "{{ .Release.Service }}"
16 | release: "{{ .Release.Name }}"
17 | name: {{ template "grafana.server.fullname" . }}
18 | spec:
19 | rules:
20 | {{- range .Values.server.ingress.hosts }}
21 | - host: {{ . }}
22 | http:
23 | paths:
24 | - path: /
25 | backend:
26 | serviceName: {{ printf "%s-%s" $releaseName "grafana" | trunc 63 }}
27 | servicePort: {{ $servicePort }}
28 | {{- end -}}
29 | {{- if .Values.server.ingress.tls }}
30 | tls:
31 | {{ toYaml .Values.server.ingress.tls | indent 4 }}
32 | {{- end -}}
33 | {{- end -}}
34 |
--------------------------------------------------------------------------------
/examples/exampleOne.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: v1
4 | kind: Namespace
5 | metadata:
6 | name: application
7 | labels:
8 | name: application
9 |
10 | ---
11 |
12 | apiVersion: extensions/v1beta1
13 | kind: Deployment
14 | metadata:
15 | name: example-one
16 | namespace: application
17 | spec:
18 | replicas: 1
19 | selector:
20 | matchLabels:
21 | k8s-app: example-one
22 | template:
23 | metadata:
24 | labels:
25 | k8s-app: example-one
26 | spec:
27 | containers:
28 | - name: example-one
29 | imagePullPolicy: Always
30 | image: fbgrecojr/helloworld:node-opentracing
31 | ports:
32 | - containerPort: 8080
33 |
34 | ---
35 |
36 | apiVersion: v1
37 | kind: Service
38 | metadata:
39 | name: example-one
40 | namespace: application
41 | spec:
42 | selector:
43 | k8s-app: example-one
44 | ports:
45 | - name: http
46 | port: 8080
47 | type: ClusterIP
48 |
49 | ---
50 |
51 | apiVersion: kanali.io/v1
52 | kind: ApiProxy
53 | metadata:
54 | name: example-one
55 | namespace: application
56 | spec:
57 | path: /api/v1/example-one
58 | target: /
59 | service:
60 | port: 8080
61 | name: example-one
62 |
--------------------------------------------------------------------------------
/helm/charts/grafana/templates/pvc.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.server.persistentVolume.enabled -}}
2 | {{- if not .Values.server.persistentVolume.existingClaim -}}
3 | apiVersion: v1
4 | kind: PersistentVolumeClaim
5 | metadata:
6 | annotations:
7 | {{- if .Values.server.persistentVolume.storageClass }}
8 | volume.beta.kubernetes.io/storage-class: {{ .Values.server.persistentVolume.storageClass | quote }}
9 | {{- else }}
10 | volume.alpha.kubernetes.io/storage-class: default
11 | {{- end }}
12 | {{- if .Values.server.persistentVolume.annotations }}
13 | {{ toYaml .Values.server.persistentVolume.annotations | indent 4 }}
14 | {{- end }}
15 | labels:
16 | app: {{ template "grafana.fullname" . }}
17 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
18 | component: "{{ .Values.server.name }}"
19 | heritage: "{{ .Release.Service }}"
20 | release: "{{ .Release.Name }}"
21 | name: {{ template "grafana.server.fullname" . }}
22 | spec:
23 | accessModes:
24 | {{- range .Values.server.persistentVolume.accessModes }}
25 | - {{ . | quote }}
26 | {{- end }}
27 | resources:
28 | requests:
29 | storage: {{ .Values.server.persistentVolume.size | quote }}
30 | {{- end -}}
31 | {{- end -}}
32 |
--------------------------------------------------------------------------------
/config.toml:
--------------------------------------------------------------------------------
1 | # This is an example configuration file for Kanali.
2 | # Note that all of the following options can be specified via cli flags.
3 | # In addtion, JSON and YAML formats can also be used.
4 |
5 | [tracing]
6 | jaeger_server_url = "jaeger-agent.kube-system.svc.cluster.local"
7 | jaeger_agent_url = "jaeger-agent.kube-system.svc.cluster.local"
8 |
9 | [analytics]
10 | influx_addr = "http://kanali-influxdb.default.svc.cluster.local:8086"
11 | influx_db = "kanali"
12 | influx_username = ""
13 | influx_password = ""
14 |
15 | [plugins]
16 | location = "/"
17 |
18 | [plugins.apiKey]
19 | decryption_key_file = "/etc/kanali/key.pem"
20 | header_key = "apikey"
21 |
22 | [tls]
23 | cert_file = "/etc/pki/tls.crt"
24 | key_file = "/etc/pki/tls.key"
25 | ca_file = ""
26 |
27 | [server]
28 | port = 8443
29 | bind_address = "0.0.0.0"
30 | peer_udp_port = 10001
31 | proxy_protocol = false
32 |
33 | [process]
34 | log_level = "info"
35 |
36 | [proxy]
37 | enable_cluster_ip = true
38 | enable_mock_responses = true
39 | upstream_timeout = "0h0m10s"
40 | header_mask_value = "ommitted"
41 | tls_common_name_validation = false
42 | mask_header_keys = [
43 | "apikey"
44 | ]
45 |
46 | [proxy.default_header_values]
47 | x-canary-deployment = "stable"
--------------------------------------------------------------------------------
/helm/charts/influxdb/templates/NOTES.txt:
--------------------------------------------------------------------------------
1 | InfluxDB can be accessed via port {{ .Values.config.http.bind_address }} on the following DNS name from within your cluster:
2 |
3 | - http://{{ template "fullname" . }}.{{ .Release.Namespace }}:{{ .Values.config.http.bind_address }}
4 |
5 | You can easily connect to the remote instance with your local influx cli. To forward the API port to localhost:8086 run the following:
6 |
7 | - kubectl port-forward --namespace {{ .Release.Namespace }} $(kubectl get pods --namespace {{ .Release.Namespace }} -l app={{ template "fullname" . }} -o jsonpath='{ .items[0].metadata.name }') 8086:{{ .Values.config.http.bind_address }}
8 |
9 | You can also connect to the influx cli from inside the container. To open a shell session in the InfluxDB pod run the following:
10 |
11 | - kubectl exec -i -t --namespace {{ .Release.Namespace }} $(kubectl get pods --namespace {{ .Release.Namespace }} -l app={{ template "fullname" . }} -o jsonpath='{.items[0].metadata.name}') /bin/sh
12 |
13 | To tail the logs for the InfluxDB pod run the following:
14 |
15 | - kubectl logs -f --namespace {{ .Release.Namespace }} $(kubectl get pods --namespace {{ .Release.Namespace }} -l app={{ template "fullname" . }} -o jsonpath='{ .items[0].metadata.name }')
16 |
--------------------------------------------------------------------------------
/helm/charts/grafana/templates/svc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | app: {{ template "grafana.fullname" . }}
6 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
7 | component: "{{ .Values.server.name }}"
8 | heritage: "{{ .Release.Service }}"
9 | release: "{{ .Release.Name }}"
10 | name: {{ template "grafana.server.fullname" . }}
11 | spec:
12 | ports:
13 | - name: {{ default "http" .Values.server.httpPortName | quote }}
14 | port: {{ .Values.server.httpPort }}
15 | protocol: TCP
16 | targetPort: 3000
17 | {{- if contains "NodePort" .Values.server.serviceType }}
18 | {{- if .Values.server.nodePort }}
19 | nodePort: {{ .Values.server.nodePort }}
20 | {{- end }}
21 | {{- end }}
22 | selector:
23 | app: {{ template "grafana.fullname" . }}
24 | component: "{{ .Values.server.name }}"
25 | type: "{{ .Values.server.serviceType }}"
26 | {{- if contains "LoadBalancer" .Values.server.serviceType }}
27 | {{- if .Values.server.loadBalancerIP }}
28 | loadBalancerIP: {{ .Values.server.loadBalancerIP }}
29 | {{- end -}}
30 | {{- if .Values.server.loadBalancerSourceRanges}}
31 | loadBalancerSourceRanges:
32 | {{- range .Values.server.loadBalancerSourceRanges }}
33 | - {{ . }}
34 | {{- end }}
35 | {{- end -}}
36 | {{- end -}}
37 |
--------------------------------------------------------------------------------
/examples/exampleTwo.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: v1
4 | kind: Namespace
5 | metadata:
6 | name: application
7 | labels:
8 | name: application
9 |
10 | ---
11 |
12 | apiVersion: extensions/v1beta1
13 | kind: Deployment
14 | metadata:
15 | name: example-two
16 | namespace: application
17 | spec:
18 | replicas: 1
19 | selector:
20 | matchLabels:
21 | k8s-app: example-two
22 | template:
23 | metadata:
24 | labels:
25 | k8s-app: example-two
26 | spec:
27 | containers:
28 | - name: example-two
29 | imagePullPolicy: Always
30 | image: fbgrecojr/helloworld:golang
31 | ports:
32 | - containerPort: 8080
33 |
34 | ---
35 |
36 | apiVersion: v1
37 | kind: Service
38 | metadata:
39 | name: example-two
40 | namespace: application
41 | labels:
42 | app: example-two
43 | release: production
44 | spec:
45 | selector:
46 | k8s-app: example-two
47 | ports:
48 | - name: http
49 | port: 8080
50 | type: ClusterIP
51 |
52 | ---
53 |
54 | apiVersion: "kanali.io/v1"
55 | kind: ApiProxy
56 | metadata:
57 | name: example-two
58 | namespace: application
59 | spec:
60 | path: /api/v1/example-two
61 | target: /
62 | service:
63 | port: 8080
64 | labels:
65 | - name: app
66 | value: example-two
67 | - name: release
68 | header: deployment
69 |
--------------------------------------------------------------------------------
/helm/templates/config.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | kind: ConfigMap
4 | apiVersion: v1
5 | metadata:
6 | name: kanali-config
7 | namespace: default
8 | data:
9 | config.toml: |-
10 | [tracing]
11 | jaeger_server_url = "jaeger-agent.kube-system.svc.cluster.local"
12 | jaeger_agent_url = "jaeger-agent.kube-system.svc.cluster.local"
13 |
14 | [analytics]
15 | influx_addr = "http://kanali-influxdb.default.svc.cluster.local:8086"
16 | influx_db = "kanali"
17 | influx_username = ""
18 | influx_password = ""
19 | influx_buffer_size = 5
20 |
21 | [plugins]
22 | location = "/"
23 |
24 | [plugins.apiKey]
25 | decryption_key_file = "/etc/pki/key.pem"
26 | header_key = "apikey"
27 |
28 | [tls]
29 | cert_file = "/etc/pki/tls.crt"
30 | key_file = "/etc/pki/tls.key"
31 | ca_file = ""
32 |
33 | [server]
34 | port = 8443
35 | bind_address = "0.0.0.0"
36 | peer_udp_port = 10001
37 | proxy_protocol = false
38 |
39 | [process]
40 | log_level = "info"
41 |
42 | [proxy]
43 | enable_cluster_ip = true
44 | enable_mock_responses = true
45 | upstream_timeout = "0h0m10s"
46 | header_mask_value = "ommitted"
47 | tls_common_name_validation = false
48 | mask_header_keys = [
49 | "apikey"
50 | ]
51 |
52 | [proxy.default_header_values]
53 | deployment = "production"
--------------------------------------------------------------------------------
/scripts/cover.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | COVER=.cover
6 | ROOT_PKG=github.com/northwesternmutual/kanali/
7 |
8 | if [[ -d "$COVER" ]]; then
9 | rm -rf "$COVER"
10 | fi
11 | mkdir -p "$COVER"
12 |
13 | # If a package directory has a .nocover file, don't count it when calculating
14 | # coverage.
15 | filter=""
16 | for pkg in "$@"; do
17 | if [[ -f "$GOPATH/src/$pkg/.nocover" ]]; then
18 | if [[ -n "$filter" ]]; then
19 | filter="$filter, "
20 | fi
21 | filter="\"$pkg\": true"
22 | fi
23 | done
24 |
25 |
26 | i=0
27 | for pkg in "$@"; do
28 | i=$((i + 1))
29 |
30 | extracoverpkg=""
31 | if [[ -f "$GOPATH/src/$pkg/.extra-coverpkg" ]]; then
32 | extracoverpkg=$( \
33 | sed -e "s|^|$pkg/|g" < "$GOPATH/src/$pkg/.extra-coverpkg" \
34 | | tr '\n' ',')
35 | fi
36 |
37 | coverpkg=$(go list -json "$pkg" | jq -r '
38 | .Deps
39 | | . + ["'"$pkg"'"]
40 | | map
41 | ( select(startswith("'"$ROOT_PKG"'"))
42 | | select(contains("/vendor/") | not)
43 | | select(in({'"$filter"'}) | not)
44 | )
45 | | join(",")
46 | ')
47 | if [[ -n "$extracoverpkg" ]]; then
48 | coverpkg="$extracoverpkg$coverpkg"
49 | fi
50 |
51 | args=""
52 | if [[ -n "$coverpkg" ]]; then
53 | args="-coverprofile $COVER/cover.${i}.out" # -coverpkg $coverpkg"
54 | fi
55 |
56 | echo go test -v -race "$pkg"
57 | go test $args -v -race "$pkg"
58 | done
59 |
60 | gocovmerge "$COVER"/*.out > cover.out
61 |
--------------------------------------------------------------------------------
/helm/charts/jaeger/templates/cassandra.yml:
--------------------------------------------------------------------------------
1 | # ---
2 | #
3 | # apiVersion: extensions/v1beta1
4 | # kind: Deployment
5 | # metadata:
6 | # name: jaeger-cassandra
7 | # namespace: tracing
8 | # spec:
9 | # replicas: 1
10 | # selector:
11 | # name: jaeger-cassandra
12 | # selector:
13 | # matchLabels:
14 | # name: jaeger-cassandra
15 | # template:
16 | # metadata:
17 | # labels:
18 | # name: jaeger-cassandra
19 | # spec:
20 | # containers:
21 | # - name: cassandra
22 | # image: registry.nmlv.nml.com/epitropos/jaegertracing/cassandra:latest
23 | # imagePullPolicy: Always
24 | # ports:
25 | # - containerPort: 9042
26 | # protocol: TCP
27 | # - name: status
28 | # image: registry.nmlv.nml.com/epitropos/jaegertracing/cassandra-status:latest
29 | # imagePullPolicy: Always
30 | # ports:
31 | # - containerPort: 8080
32 | # protocol: TCP
33 | #
34 | # ---
35 | #
36 | # apiVersion: v1
37 | # kind: Service
38 | # metadata:
39 | # name: jaeger-cassandra
40 | # namespace: tracing
41 | # spec:
42 | # ports:
43 | # - name: cassandra-port
44 | # port: 9042
45 | # protocol: TCP
46 | # targetPort: 9042
47 | # - name: health-port
48 | # port: 8080
49 | # protocol: TCP
50 | # targetPort: 8080
51 | # selector:
52 | # name: jaeger-cassandra
53 | # type: ClusterIP
54 |
--------------------------------------------------------------------------------
/cmd/start_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package cmd
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/stretchr/testify/assert"
27 | )
28 |
29 | func TestStartCmdInit(t *testing.T) {
30 | assert.Equal(t, len(RootCmd.Commands()), 2)
31 | assert.Equal(t, RootCmd.Commands()[0], startCmd)
32 | }
33 |
--------------------------------------------------------------------------------
/spec/endpoints.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package spec
22 |
23 | import "k8s.io/kubernetes/pkg/api"
24 |
25 | // KanaliEndpoints represents the endpoints of all running instances of Kanali
26 | var KanaliEndpoints *api.Endpoints
27 |
28 | func init() {
29 | KanaliEndpoints = &api.Endpoints{}
30 | }
31 |
--------------------------------------------------------------------------------
/helm/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: extensions/v1beta1
4 | kind: Deployment
5 | metadata:
6 | name: kanali
7 | namespace: default
8 | spec:
9 | selector:
10 | matchLabels:
11 | k8s-app: kanali
12 | template:
13 | metadata:
14 | labels:
15 | k8s-app: kanali
16 | spec:
17 | serviceAccountName: kanali
18 | containers:
19 | - name: kanali
20 | imagePullPolicy: {{default "IfNotPresent" .Values.pullPolicy}}
21 | image: {{.Values.imageRegistry}}/kanali:{{.Values.dockerImageTag}}
22 | command:
23 | - /kanali
24 | - start
25 | env:
26 | - name: POD_IP
27 | valueFrom:
28 | fieldRef:
29 | fieldPath: status.podIP
30 | ports:
31 | - containerPort: 8443
32 | volumeMounts:
33 | - name: pki
34 | mountPath: /etc/pki
35 | - name: config
36 | mountPath: /etc/kanali
37 | volumes:
38 | - name: pki
39 | projected:
40 | sources:
41 | - secret:
42 | name: {{.Values.tlsSecretName}}
43 | items:
44 | - key: tls.crt
45 | path: tls.crt
46 | - key: tls.key
47 | path: tls.key
48 | - secret:
49 | name: {{.Values.decryptKeySecretName}}
50 | items:
51 | - key: key.pem
52 | path: key.pem
53 | - name: config
54 | configMap:
55 | name: {{.Values.kanaliConfigName}}
--------------------------------------------------------------------------------
/spec/store.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package spec
22 |
23 | // Store is an interface that provides a common set of operations for
24 | // a data structure
25 | type Store interface {
26 | Set(obj interface{}) error
27 | Update(obj interface{}) error
28 | Get(params ...interface{}) (interface{}, error)
29 | Delete(obj interface{}) (interface{}, error)
30 | Clear()
31 | IsEmpty() bool
32 | }
33 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG GO_VERSION=1.8.3
2 | ARG CENTOS_VERSION=7
3 |
4 | FROM golang:${GO_VERSION} AS BUILD
5 | LABEL maintainer="frankgreco@northwesternmutual.com"
6 | LABEL version="${VERSION}"
7 | ARG VERSION=""
8 | ARG GLIDE_VERSION=0.12.3
9 | WORKDIR /go/src/github.com/northwesternmutual/kanali/
10 | RUN wget "https://github.com/Masterminds/glide/releases/download/v${GLIDE_VERSION}/glide-v${GLIDE_VERSION}-`go env GOHOSTOS`-`go env GOHOSTARCH`.tar.gz" -O /tmp/glide.tar.gz \
11 | && mkdir /tmp/glide \
12 | && tar --directory=/tmp/glide -xvf /tmp/glide.tar.gz \
13 | && rm -rf /tmp/glide.tar.gz \
14 | && export PATH=$PATH:/tmp/glide/`go env GOHOSTOS`-`go env GOHOSTARCH`
15 | COPY glide.lock glide.yaml Makefile /go/src/github.com/northwesternmutual/kanali/
16 | RUN make install
17 | COPY ./ /go/src/github.com/northwesternmutual/kanali/
18 | RUN sed -ie "s/changeme/`echo ${VERSION}`/g" /go/src/github.com/northwesternmutual/kanali/cmd/version.go
19 | RUN curl -O https://raw.githubusercontent.com/northwesternmutual/kanali-plugin-apikey/v1.2.0/plugin.go
20 | RUN GOOS=`go env GOHOSTOS` GOARCH=`go env GOHOSTARCH` go build -buildmode=plugin -o apiKey_v1.2.0.so plugin.go
21 | RUN GOOS=`go env GOHOSTOS` GOARCH=`go env GOHOSTARCH` go build -o kanali
22 |
23 | FROM centos:${CENTOS_VERSION}
24 | LABEL maintainer="frankgreco@northwesternmutual.com"
25 | LABEL version="${VERSION}"
26 | COPY --from=BUILD /go/src/github.com/northwesternmutual/kanali/apiKey_v1.2.0.so /go/src/github.com/northwesternmutual/kanali/kanali /
27 | RUN cp /apiKey_v1.2.0.so /apiKey.so
28 | ENTRYPOINT ["/kanali"]
--------------------------------------------------------------------------------
/cmd/root.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package cmd
22 |
23 | import (
24 | "github.com/spf13/cobra"
25 | )
26 |
27 | // RootCmd is the base command of this project
28 | var RootCmd = &cobra.Command{
29 | Use: "kanali",
30 | Short: "layer 7 load balancer for Kubernetes with API management",
31 | Long: "kanali provides a dynamic layer 7 load balancer for a Kubernetes cluster coupled tightly with an API management solution",
32 | }
33 |
--------------------------------------------------------------------------------
/config/process.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package config
22 |
23 | func init() {
24 | Flags.Add(
25 | FlagProcessLogLevel,
26 | )
27 | }
28 |
29 | var (
30 | // FlagProcessLogLevel sets the logging level. Choose between 'debug', 'info', 'warn', 'error', 'fatal'
31 | FlagProcessLogLevel = Flag{
32 | Long: "process.log_level",
33 | Short: "l",
34 | Value: "info",
35 | Usage: "Sets the logging level. Choose between 'debug', 'info', 'warn', 'error', 'fatal'.",
36 | }
37 | )
38 |
--------------------------------------------------------------------------------
/helm/charts/influxdb/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: {{ template "fullname" . }}
5 | labels:
6 | app: {{ template "fullname" . }}
7 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
8 | release: "{{ .Release.Name }}"
9 | heritage: "{{ .Release.Service }}"
10 | spec:
11 | type: {{ .Values.service.type }}
12 | ports:
13 | {{- if .Values.config.http.enabled }}
14 | - name: api
15 | port: {{ .Values.config.http.bind_address }}
16 | targetPort: {{ .Values.config.http.bind_address }}
17 | {{- end }}
18 | {{- if .Values.config.admin.enabled }}
19 | - name: admin
20 | port: {{ .Values.config.admin.bind_address }}
21 | targetPort: {{ .Values.config.admin.bind_address }}
22 | {{- end }}
23 | {{- if .Values.config.graphite.enabled }}
24 | - name: graphite
25 | port: {{ .Values.config.graphite.bind_address }}
26 | targetPort: {{ .Values.config.graphite.bind_address }}
27 | {{- end }}
28 | {{- if .Values.config.collectd.enabled }}
29 | - name: collectd
30 | port: {{ .Values.config.collectd.bind_address }}
31 | targetPort: {{ .Values.config.collectd.bind_address }}
32 | {{- end }}
33 | {{- if .Values.config.udp.enabled }}
34 | - name: udp
35 | port: {{ .Values.config.udp.bind_address }}
36 | targetPort: {{ .Values.config.udp.bind_address }}
37 | {{- end }}
38 | {{- if .Values.config.opentsdb.enabled }}
39 | - name: opentsdb
40 | port: {{ .Values.config.opentsdb.bind_address }}
41 | targetPort: {{ .Values.config.opentsdb.bind_address }}
42 | {{- end }}
43 | selector:
44 | app: {{ template "fullname" . }}
45 |
--------------------------------------------------------------------------------
/utils/errors_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package utils
22 |
23 | import (
24 | "errors"
25 | "testing"
26 |
27 | "github.com/stretchr/testify/assert"
28 | )
29 |
30 | func TestStatus(t *testing.T) {
31 | se := StatusError{
32 | Code: 400,
33 | Err: errors.New("new error"),
34 | }
35 | assert.Equal(t, "new error", se.Error())
36 | }
37 |
38 | func TestError(t *testing.T) {
39 | se := StatusError{
40 | Code: 400,
41 | Err: errors.New("new error"),
42 | }
43 | assert.Equal(t, 400, se.Status())
44 | }
45 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package main
22 |
23 | import (
24 | "os"
25 | "runtime"
26 |
27 | "github.com/Sirupsen/logrus"
28 | "github.com/northwesternmutual/kanali/cmd"
29 | )
30 |
31 | func init() {
32 |
33 | logrus.SetFormatter(&logrus.JSONFormatter{})
34 | logrus.SetOutput(os.Stdout)
35 |
36 | }
37 |
38 | func main() {
39 | runtime.GOMAXPROCS(runtime.NumCPU())
40 | if err := cmd.RootCmd.Execute(); err != nil {
41 | logrus.Fatalf("error executing root cobra command: %s\n", err)
42 | os.Exit(1)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/helm/charts/influxdb/templates/post-install-set-auth.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.setDefaultUser.enabled -}}
2 | apiVersion: batch/v1
3 | kind: Job
4 | metadata:
5 | labels:
6 | app: {{ template "fullname" . }}
7 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
8 | release: "{{ .Release.Name }}"
9 | heritage: "{{ .Release.Service }}"
10 | name: {{ template "fullname" . }}-set-auth
11 | annotations:
12 | "helm.sh/hook": post-install
13 | spec:
14 | activeDeadlineSeconds: {{ default 300 .Values.setDefaultUser.activeDeadlineSeconds }}
15 | template:
16 | metadata:
17 | labels:
18 | app: {{ template "fullname" . }}
19 | release: "{{ .Release.Name }}"
20 | spec:
21 | containers:
22 | - name: {{ template "fullname" . }}-set-auth
23 | image: "{{ .Values.setDefaultUser.image }}"
24 | env:
25 | - name: INFLUXDB_USER
26 | valueFrom:
27 | secretKeyRef:
28 | name: {{ template "fullname" . }}-auth
29 | key: influxdb-user
30 | - name: INFLUXDB_PASSWORD
31 | valueFrom:
32 | secretKeyRef:
33 | name: {{ template "fullname" . }}-auth
34 | key: influxdb-password
35 | args:
36 | - "/bin/sh"
37 | - "-c"
38 | - |
39 | curl -X POST http://{{ template "fullname" . }}:{{ .Values.config.http.bind_address }}/query \
40 | --data-urlencode \
41 | "q=CREATE USER \"${INFLUXDB_USER}\" WITH PASSWORD '${INFLUXDB_PASSWORD}' {{ .Values.setDefaultUser.user.privileges }}"
42 | restartPolicy: {{ .Values.setDefaultUser.restartPolicy }}
43 | {{- end -}}
44 |
--------------------------------------------------------------------------------
/cmd/version_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package cmd
22 |
23 | import (
24 | "bytes"
25 | "testing"
26 |
27 | "github.com/stretchr/testify/assert"
28 | )
29 |
30 | func TestVersionCmdInit(t *testing.T) {
31 | assert.Equal(t, len(RootCmd.Commands()), 2)
32 | assert.Equal(t, RootCmd.Commands()[1], versionCmd)
33 | }
34 |
35 | func TestVersionCmdRun(t *testing.T) {
36 | org := out
37 | out = new(bytes.Buffer)
38 | defer func() { out = org }()
39 |
40 | versionCmdRun(nil, nil)
41 | assert.Equal(t, out.(*bytes.Buffer).String(), "changeme\n")
42 | }
43 |
--------------------------------------------------------------------------------
/flow/step.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package flow
22 |
23 | import (
24 | "context"
25 | "net/http"
26 |
27 | "github.com/northwesternmutual/kanali/metrics"
28 | "github.com/northwesternmutual/kanali/spec"
29 | opentracing "github.com/opentracing/opentracing-go"
30 | )
31 |
32 | // Step is an interface that represents a step to be used in a flow
33 | type Step interface {
34 | GetName() string
35 | Do(ctx context.Context, proxy *spec.APIProxy, metrics *metrics.Metrics, w http.ResponseWriter, r *http.Request, resp *http.Response, trace opentracing.Span) error
36 | }
37 |
--------------------------------------------------------------------------------
/cmd/version.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package cmd
22 |
23 | import (
24 | "fmt"
25 | "io"
26 | "os"
27 |
28 | "github.com/spf13/cobra"
29 | )
30 |
31 | func init() {
32 | RootCmd.AddCommand(versionCmd)
33 | }
34 |
35 | var out io.Writer = os.Stdout
36 |
37 | var versionCmd = &cobra.Command{
38 | Use: `version`,
39 | Short: `version`,
40 | Long: `view the current version of this software`,
41 | Run: versionCmdRun,
42 | }
43 |
44 | var versionCmdRun = func(cmd *cobra.Command, args []string) {
45 | fmt.Fprint(out, "changeme")
46 | fmt.Fprint(out, "\n")
47 | }
48 |
--------------------------------------------------------------------------------
/server/server_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package server
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/northwesternmutual/kanali/config"
27 | "github.com/spf13/viper"
28 | "github.com/stretchr/testify/assert"
29 | )
30 |
31 | func TestGetKanaliPort(t *testing.T) {
32 | assert.Equal(t, getKanaliPort(), 80)
33 |
34 | viper.Set(config.FlagServerPort.GetLong(), 12345)
35 | assert.Equal(t, getKanaliPort(), 12345)
36 |
37 | viper.Set(config.FlagServerPort.GetLong(), 0)
38 | viper.Set(config.FlagTLSCertFile.GetLong(), "hi")
39 | viper.Set(config.FlagTLSKeyFile.GetLong(), "bye")
40 | assert.Equal(t, getKanaliPort(), 443)
41 | }
42 |
--------------------------------------------------------------------------------
/metrics/metrics.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package metrics
22 |
23 | // Metric represent a single request metric
24 | type Metric struct {
25 | Name string
26 | Value interface{}
27 | Index bool
28 | }
29 |
30 | // Metrics represents a list of Metrics associated with a request
31 | type Metrics []Metric
32 |
33 | // Add appends a metric to the list of metrics
34 | func (m *Metrics) Add(metrics ...Metric) {
35 | for _, metric := range metrics {
36 | *m = append(*m, metric)
37 | }
38 | }
39 |
40 | // Get retrives a specific metric by name
41 | func (m *Metrics) Get(name string) *Metric {
42 | for _, metric := range *m {
43 | if metric.Name == name {
44 | return &metric
45 | }
46 | }
47 | return nil
48 | }
49 |
--------------------------------------------------------------------------------
/config/tracing.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package config
22 |
23 | func init() {
24 | Flags.Add(
25 | FlagTracingJaegerServerURL,
26 | FlagTracingJaegerAgentURL,
27 | )
28 | }
29 |
30 | var (
31 | // FlagTracingJaegerServerURL specifies the endpoint to the Jaeger server
32 | FlagTracingJaegerServerURL = Flag{
33 | Long: "tracing.jaeger_server_url",
34 | Short: "",
35 | Value: "jaeger-all-in-one-agent.default.svc.cluster.local",
36 | Usage: "Endpoint to the Jaeger server",
37 | }
38 | // FlagTracingJaegerAgentURL specifies the endpoint to the Jaeger agent
39 | FlagTracingJaegerAgentURL = Flag{
40 | Long: "tracing.jaeger_agent_url",
41 | Short: "",
42 | Value: "jaeger-all-in-one-agent.default.svc.cluster.local",
43 | Usage: "Endpoint to the Jaeger agent",
44 | }
45 | )
46 |
--------------------------------------------------------------------------------
/examples/exampleThree.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: v1
4 | kind: Namespace
5 | metadata:
6 | name: application
7 | labels:
8 | name: application
9 |
10 | ---
11 |
12 | kind: ConfigMap
13 | apiVersion: v1
14 | metadata:
15 | name: example-three
16 | namespace: application
17 | data:
18 | response: |-
19 | [{
20 | "route": "/health",
21 | "code": 200,
22 | "method": "GET",
23 | "body": {
24 | "msg": "all systems are up and running"
25 | }
26 | },
27 | {
28 | "route": "/accounts",
29 | "code": 200,
30 | "method": "GET",
31 | "body": [{
32 | "id": "1",
33 | "balance": "$500.00"
34 | },
35 | {
36 | "id": "2",
37 | "balance": "$1000.00"
38 | }]
39 | }]
40 |
41 | ---
42 |
43 | apiVersion: extensions/v1beta1
44 | kind: Deployment
45 | metadata:
46 | labels:
47 | app: example-three
48 | name: example-three
49 | namespace: application
50 | spec:
51 | replicas: 1
52 | selector:
53 | matchLabels:
54 | k8s-app: example-three
55 | template:
56 | metadata:
57 | labels:
58 | k8s-app: example-three
59 | spec:
60 | containers:
61 | - name: example-three
62 | imagePullPolicy: Always
63 | image: fbgrecojr/helloworld:golang
64 | ports:
65 | - containerPort: 8080
66 |
67 | ---
68 |
69 | apiVersion: v1
70 | kind: Service
71 | metadata:
72 | name: example-three
73 | namespace: application
74 | spec:
75 | selector:
76 | k8s-app: example-three
77 | ports:
78 | - name: http
79 | port: 8080
80 | type: ClusterIP
81 |
82 | ---
83 |
84 | apiVersion: "kanali.io/v1"
85 | kind: ApiProxy
86 | metadata:
87 | name: example-three
88 | namespace: application
89 | spec:
90 | path: /api/v1/example-three
91 | target: /
92 | mock:
93 | configMapName: example-three
94 | service:
95 | port: 8080
96 | name: example-three
97 |
--------------------------------------------------------------------------------
/handlers/logger.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package handlers
22 |
23 | import (
24 | "net/http"
25 | "strings"
26 |
27 | "github.com/Sirupsen/logrus"
28 | "github.com/northwesternmutual/kanali/utils"
29 | )
30 |
31 | // Logger creates a custom http.Handler that logs details around a request
32 | // along with creating contextual request metrics. When the request is complete
33 | // these metrics will be writtin to Influxdb
34 | func Logger(inner Handler) http.Handler {
35 |
36 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
37 |
38 | inner.serveHTTP(w, r)
39 |
40 | logrus.WithFields(logrus.Fields{
41 | "client ip": strings.Split(r.RemoteAddr, ":")[0],
42 | "method": r.Method,
43 | "uri": utils.ComputeURLPath(r.URL),
44 | }).Info("request details")
45 |
46 | })
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/config/plugins.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package config
22 |
23 | func init() {
24 | Flags.Add(
25 | FlagPluginsLocation,
26 | FlagPluginsAPIKeyDecriptionKeyFile,
27 | )
28 | }
29 |
30 | var (
31 | // FlagPluginsLocation sets the location of custom plugins shared object (.so) files
32 | FlagPluginsLocation = Flag{
33 | Long: "plugins.location",
34 | Short: "",
35 | Value: "/",
36 | Usage: "Location of custom plugins shared object (.so) files.",
37 | }
38 | // FlagPluginsAPIKeyDecriptionKeyFile set the location of the decryption RSA key file to be used to decrypt incoming API keys.
39 | FlagPluginsAPIKeyDecriptionKeyFile = Flag{
40 | Long: "plugins.apiKey.decryption_key_file",
41 | Short: "",
42 | Value: "",
43 | Usage: "Path to valid PEM-encoded private key that matches the public key used to encrypt API keys.",
44 | }
45 | )
46 |
--------------------------------------------------------------------------------
/tracer/jaeger_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package tracer
22 |
23 | import (
24 | "bytes"
25 | "testing"
26 |
27 | "github.com/Sirupsen/logrus"
28 | "github.com/stretchr/testify/assert"
29 | )
30 |
31 | func TestCustomLoggerError(t *testing.T) {
32 | logger := customLogger{}
33 |
34 | writerOne := new(bytes.Buffer)
35 | writerTwo := new(bytes.Buffer)
36 | logrus.SetOutput(writerOne)
37 | logger.Error("custom error message")
38 | logrus.SetOutput(writerTwo)
39 | logrus.Error("custom error message")
40 | assert.Equal(t, writerOne.String(), writerTwo.String())
41 |
42 | writerOne = new(bytes.Buffer)
43 | writerTwo = new(bytes.Buffer)
44 | logrus.SetOutput(writerOne)
45 | logger.Infof("custom %s message", "info")
46 | logrus.SetOutput(writerTwo)
47 | logrus.Infof("custom %s message", "info")
48 | assert.Equal(t, writerOne.String(), writerTwo.String())
49 | }
50 |
--------------------------------------------------------------------------------
/utils/errors.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package utils
22 |
23 | // Error is an interface that is used to intuitively handle HTTP errors.
24 | // It expands on the native error interface that the language provides
25 | type Error interface {
26 | error
27 | Status() int
28 | }
29 |
30 | // StatusError implements the Error interface and is used to handle HTTP specific errors
31 | type StatusError struct {
32 | Code int
33 | Err error
34 | }
35 |
36 | // JSONErr is used to assist in marshalling HTTP errors
37 | type JSONErr struct {
38 | Code int `json:"code"`
39 | Msg string `json:"msg"`
40 | }
41 |
42 | // Error will return the error message associated with the error
43 | func (se StatusError) Error() string {
44 | return se.Err.Error()
45 | }
46 |
47 | // Status returns the HTTP error code associated with the error
48 | func (se StatusError) Status() int {
49 | return se.Code
50 | }
51 |
--------------------------------------------------------------------------------
/helm/charts/grafana/templates/job.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.server.setDatasource.enabled -}}
2 | apiVersion: batch/v1
3 | kind: Job
4 | metadata:
5 | labels:
6 | app: {{ template "grafana.fullname" . }}
7 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
8 | component: "{{ .Values.server.name }}"
9 | heritage: "{{ .Release.Service }}"
10 | release: "{{ .Release.Name }}"
11 | name: {{ template "grafana.server.fullname" . }}-set-datasource
12 | spec:
13 | activeDeadlineSeconds: {{ default 300 .Values.server.setDatasource.activeDeadlineSeconds }}
14 | template:
15 | metadata:
16 | labels:
17 | app: {{ template "grafana.fullname" . }}
18 | component: "{{ .Values.server.name }}"
19 | release: "{{ .Release.Name }}"
20 | spec:
21 | containers:
22 | - name: {{ template "grafana.server.fullname" . }}-set-datasource
23 | image: "{{ .Values.server.setDatasource.image }}"
24 | env:
25 | - name: ADMIN_USER
26 | valueFrom:
27 | secretKeyRef:
28 | name: {{ template "grafana.server.fullname" . }}
29 | key: grafana-admin-user
30 | - name: ADMIN_PASSWORD
31 | valueFrom:
32 | secretKeyRef:
33 | name: {{ template "grafana.server.fullname" . }}
34 | key: grafana-admin-password
35 | args:
36 | - "http://$(ADMIN_USER):$(ADMIN_PASSWORD)@{{ template "grafana.fullname" . }}:{{ .Values.server.httpPort }}/api/datasources"
37 | - "-X"
38 | - POST
39 | - "-H"
40 | - "Content-Type: application/json;charset=UTF-8"
41 | - "--data-binary"
42 | {{- with .Values.server.setDatasource.datasource }}
43 | - "{\"name\":\"{{ .name }}\",\"type\":\"{{ .type }}\",\"url\":\"{{ .url }}\",\"database\":\"{{ .database }}\",\"jsonData\":{ {{ .jsonData }} },\"access\":\"{{ .access }}\",\"isDefault\":{{ .isDefault }}}"
44 | {{- end }}
45 | restartPolicy: {{ .Values.server.setDatasource.restartPolicy }}
46 | {{- end -}}
47 |
--------------------------------------------------------------------------------
/config/tls.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package config
22 |
23 | func init() {
24 | Flags.Add(
25 | FlagTLSCertFile,
26 | FlagTLSKeyFile,
27 | FlagTLSCaFile,
28 | )
29 | }
30 |
31 | var (
32 | // FlagTLSCertFile specifies the path to x509 certificate for HTTPS servers.
33 | FlagTLSCertFile = Flag{
34 | Long: "tls.cert_file",
35 | Short: "c",
36 | Value: "",
37 | Usage: "Path to x509 certificate for HTTPS servers.",
38 | }
39 | // FlagTLSKeyFile pecifies the path to x509 private key matching --tls-cert-file
40 | FlagTLSKeyFile = Flag{
41 | Long: "tls.key_file",
42 | Short: "k",
43 | Value: "",
44 | Usage: "Path to x509 private key matching --tls.cert_file.",
45 | }
46 | // FlagTLSCaFile specifies the path to x509 certificate authority bundle for mutual TLS
47 | FlagTLSCaFile = Flag{
48 | Long: "tls.ca_file",
49 | Short: "",
50 | Value: "",
51 | Usage: "Path to x509 certificate authority bundle for mutual TLS.",
52 | }
53 | )
54 |
--------------------------------------------------------------------------------
/steps/pluginsonrequest_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package steps
22 |
23 | import (
24 | "context"
25 | "testing"
26 |
27 | "github.com/northwesternmutual/kanali/spec"
28 | opentracing "github.com/opentracing/opentracing-go"
29 | "github.com/stretchr/testify/assert"
30 | )
31 |
32 | func TestPluginsOnRequestGetName(t *testing.T) {
33 | assert := assert.New(t)
34 | step := PluginsOnRequestStep{}
35 | assert.Equal(step.GetName(), "Plugin OnRequest", "step name is incorrect")
36 | }
37 |
38 | func TestDoOnRequest(t *testing.T) {
39 | assert.Equal(t, doOnRequest(context.Background(), nil, "name", spec.APIProxy{}, nil, opentracing.StartSpan("test span"), fakePanicPlugin{}).Error(), "OnRequest paniced")
40 | assert.Equal(t, doOnRequest(context.Background(), nil, "name", spec.APIProxy{}, nil, opentracing.StartSpan("test span"), fakeErrorPlugin{}).Error(), "error")
41 | assert.Nil(t, doOnRequest(context.Background(), nil, "name", spec.APIProxy{}, nil, opentracing.StartSpan("test span"), fakeSuccessPlugin{}))
42 | }
43 |
--------------------------------------------------------------------------------
/steps/pluginsonresponse_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package steps
22 |
23 | import (
24 | "context"
25 | "testing"
26 |
27 | "github.com/northwesternmutual/kanali/spec"
28 | opentracing "github.com/opentracing/opentracing-go"
29 | "github.com/stretchr/testify/assert"
30 | )
31 |
32 | func TestPluginsOnResponseGetName(t *testing.T) {
33 | step := PluginsOnResponseStep{}
34 | assert.Equal(t, step.GetName(), "Plugin OnResponse", "step name is incorrect")
35 | }
36 |
37 | func TestDoOnResponse(t *testing.T) {
38 | assert.Equal(t, doOnResponse(context.Background(), nil, "name", spec.APIProxy{}, nil, nil, opentracing.StartSpan("test span"), fakePanicPlugin{}).Error(), "OnResponse paniced")
39 | assert.Equal(t, doOnResponse(context.Background(), nil, "name", spec.APIProxy{}, nil, nil, opentracing.StartSpan("test span"), fakeErrorPlugin{}).Error(), "error")
40 | assert.Nil(t, doOnResponse(context.Background(), nil, "name", spec.APIProxy{}, nil, nil, opentracing.StartSpan("test span"), fakeSuccessPlugin{}))
41 | }
42 |
--------------------------------------------------------------------------------
/helm/charts/jaeger/templates/ui.yml:
--------------------------------------------------------------------------------
1 | # ---
2 | #
3 | # apiVersion: extensions/v1beta1
4 | # kind: Deployment
5 | # metadata:
6 | # name: jaeger-query
7 | # namespace: tracing
8 | # spec:
9 | # replicas: 1
10 | # selector:
11 | # name: jaeger-query
12 | # strategy:
13 | # type: Recreate
14 | # selector:
15 | # matchLabels:
16 | # name: jaeger-query
17 | # template:
18 | # metadata:
19 | # labels:
20 | # name: jaeger-query
21 | # jaeger-infra: query
22 | # spec:
23 | # containers:
24 | # - name: jaeger-query
25 | # image: registry.nmlv.nml.com/epitropos/jaegertracing/jaeger-query:latest
26 | # imagePullPolicy: Always
27 | # command:
28 | # - /go/bin/query-linux
29 | # args:
30 | # - -cassandra.connections-per-host=2
31 | # - -cassandra.keyspace=jaeger_v1_test
32 | # - -cassandra.max-retry-attempts=3
33 | # - -cassandra.port=9042
34 | # - -cassandra.proto-version=4
35 | # - -cassandra.servers=jaeger-cassandra.tracing.svc.cluster.local
36 | # - -cassandra.socket-keep-alive=0h0m0s
37 | # - -cassandra.timeout=0h1m0s
38 | # - -dependency-storage.data-frequency=24h0m0s
39 | # - -dependency-storage.type=cassandra
40 | # - -log-level=info
41 | # - -query.port=16686
42 | # - -query.prefix=api
43 | # - -query.static-files=jaeger-ui-build/build/
44 | # - -runtime-metrics-frequency=0h0m1s
45 | # - -span-storage.type=cassandra
46 | # ports:
47 | # - containerPort: 16686
48 | # protocol: TCP
49 | # resources: {}
50 | # readinessProbe:
51 | # httpGet:
52 | # path: /
53 | # port: 16686
54 | # initialDelaySeconds: 5
55 | #
56 | # ---
57 | #
58 | # apiVersion: v1
59 | # kind: Service
60 | # metadata:
61 | # name: jaeger-query
62 | # namespace: tracing
63 | # labels:
64 | # jaeger-infra: query
65 | # spec:
66 | # ports:
67 | # - name: query-https
68 | # port: 80
69 | # protocol: TCP
70 | # targetPort: 16686
71 | # selector:
72 | # name: jaeger-query
73 | # type: NodePort
74 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ALL_SRC := $(shell find . -name "*.go" | grep -v -e vendor \
2 | -e ".*/\..*" \
3 | -e ".*/_.*" \
4 | -e ".*/mocks.*")
5 |
6 | BINARY=$(shell echo $${PWD\#\#*/})
7 | FILES = $(shell go list ./... | grep -v /vendor/)
8 | PACKAGES := $(shell glide novendor)
9 |
10 | RACE=-race
11 | GOTEST=go test -v $(RACE)
12 | GOLINT=golint
13 | GOVET=go vet
14 | GOBUILD=go build $(RACE)
15 | GOFMT=gofmt
16 | ERRCHECK=errcheck -ignoretests
17 | FMT_LOG=fmt.log
18 | LINT_LOG=lint.log
19 |
20 | PASS=$(shell printf "\033[32mPASS\033[0m")
21 | FAIL=$(shell printf "\033[31mFAIL\033[0m")
22 | COLORIZE=sed ''/PASS/s//$(PASS)/'' | sed ''/FAIL/s//$(FAIL)/''
23 |
24 | .DEFAULT_GOAL: $(BINARY)
25 |
26 | $(BINARY): $(ALL_SRC) test fmt lint
27 |
28 | .PHONY: install
29 | install:
30 | glide --version || go get github.com/Masterminds/glide
31 | glide install
32 |
33 | .PHONY: build
34 | build:
35 | GOOS=`go env GOHOSTOS` GOARCH=`go env GOHOSTARCH` $(GOBUILD) -o $(BINARY)
36 |
37 | .PHONY: fmt
38 | fmt:
39 | $(GOFMT) -e -s -l -w $(ALL_SRC)
40 | ./scripts/updateLicenses.sh
41 |
42 | .PHONY: cover
43 | cover:
44 | ./scripts/cover.sh $(shell go list $(PACKAGES))
45 | go tool cover -html=cover.out -o cover.html
46 |
47 | .PHONY: test
48 | test:
49 | bash -c "set -e; set -o pipefail; $(GOTEST) $(PACKAGES) | $(COLORIZE)"
50 |
51 | .PHONY: lint
52 | lint:
53 | @$(GOVET) $(PACKAGES)
54 | @$(ERRCHECK) $(PACKAGES)
55 | @cat /dev/null > $(LINT_LOG)
56 | @$(foreach pkg, $(PACKAGES), $(GOLINT) $(pkg) >> $(LINT_LOG) || true;)
57 | @[ ! -s "$(LINT_LOG)" ] || (echo "Lint Failures" | cat - $(LINT_LOG) && false)
58 | @$(GOFMT) -e -s -l $(ALL_SRC) > $(FMT_LOG)
59 | @[ ! -s "$(FMT_LOG)" ] || (echo "Go Fmt Failures, run 'make fmt'" | cat - $(FMT_LOG) && false)
60 |
61 | .PHONY: install_ci
62 | install_ci: install
63 | go get github.com/wadey/gocovmerge
64 | go get github.com/mattn/goveralls
65 | go get golang.org/x/tools/cmd/cover
66 | go get github.com/golang/lint/golint
67 | go get github.com/kisielk/errcheck
68 |
69 | .PHONY: test_ci
70 | test_ci:
71 | @./scripts/cover.sh $(shell go list $(PACKAGES))
72 | make lint
73 |
74 | .PHONY: clean
75 | clean:
76 | if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi
--------------------------------------------------------------------------------
/examples/exampleSix.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: v1
4 | kind: Namespace
5 | metadata:
6 | name: application
7 | labels:
8 | name: application
9 |
10 | ---
11 |
12 | apiVersion: extensions/v1beta1
13 | kind: Deployment
14 | metadata:
15 | name: example-six
16 | namespace: application
17 | spec:
18 | replicas: 1
19 | selector:
20 | matchLabels:
21 | k8s-app: example-six
22 | template:
23 | metadata:
24 | labels:
25 | k8s-app: example-six
26 | spec:
27 | containers:
28 | - name: example-six
29 | imagePullPolicy: Always
30 | image: fbgrecojr/helloworld:golang
31 | ports:
32 | - containerPort: 8080
33 |
34 | ---
35 |
36 | apiVersion: v1
37 | kind: Service
38 | metadata:
39 | name: example-six
40 | namespace: application
41 | spec:
42 | selector:
43 | k8s-app: example-six
44 | ports:
45 | - name: http
46 | port: 8080
47 | type: ClusterIP
48 |
49 | ---
50 |
51 | apiVersion: kanali.io/v1
52 | kind: ApiProxy
53 | metadata:
54 | name: example-six
55 | namespace: application
56 | spec:
57 | path: /api/v1/example-six
58 | target: /
59 | service:
60 | port: 8080
61 | name: example-six
62 | plugins:
63 | - name: apiKey
64 | version: v1.2.0
65 |
66 | ---
67 |
68 | apiVersion: kanali.io/v1
69 | kind: ApiKey
70 | metadata:
71 | name: janes-apikey
72 | namespace: application
73 | spec:
74 | data: 2b06207cce17aeeee93dba801dcfc8055638fe6eb00b0e644c48161bdcd66da42f6951ebc3cca02302f90c0ed2f42ecf28673fe1873af704c023367e3a7113919b8b85bd62384f5261d08fe4af51174141ae2836ab628d1ed58f030fca12c4fe53e8f1c8f836c9026a635ca6d419fb873fdeb621b40cfe336bbbf0c5fc2c352d044ae0f8a59b489a62e468a5b7e090f42127a8ad7a796cb2f67dfd81756d232b19f2522cf911809747c61fbe7f051219fe763e173ab074ae332f25c63bc2d6a5e190ed413f7bf830c006789677a69b4855ce54f06fd2a68d38a5267cec571b0de59198b537212d422cfc188366ce54e5f81a3485f5ead872688efdcdb0a3549b
75 |
76 | ---
77 |
78 | apiVersion: kanali.io/v1
79 | kind: ApiKeyBinding
80 | metadata:
81 | name: example-six
82 | namespace: application
83 | spec:
84 | proxy: example-six
85 | keys:
86 | - name: janes-apikey
87 | defaultRule:
88 | granular:
89 | verbs:
90 | - GET
--------------------------------------------------------------------------------
/examples/exampleEight.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: v1
4 | kind: Namespace
5 | metadata:
6 | name: application
7 | labels:
8 | name: application
9 |
10 | ---
11 |
12 | apiVersion: extensions/v1beta1
13 | kind: Deployment
14 | metadata:
15 | name: example-eight
16 | namespace: application
17 | spec:
18 | replicas: 1
19 | selector:
20 | matchLabels:
21 | k8s-app: example-eight
22 | template:
23 | metadata:
24 | labels:
25 | k8s-app: example-eight
26 | spec:
27 | containers:
28 | - name: example-eight
29 | imagePullPolicy: Always
30 | image: fbgrecojr/helloworld:golang
31 | ports:
32 | - containerPort: 8080
33 |
34 | ---
35 |
36 | apiVersion: v1
37 | kind: Service
38 | metadata:
39 | name: example-eight
40 | namespace: application
41 | spec:
42 | selector:
43 | k8s-app: example-eight
44 | ports:
45 | - name: http
46 | port: 8080
47 | type: ClusterIP
48 |
49 | ---
50 |
51 | apiVersion: kanali.io/v1
52 | kind: ApiProxy
53 | metadata:
54 | name: example-eight
55 | namespace: application
56 | spec:
57 | path: /api/v1/example-eight
58 | target: /
59 | service:
60 | port: 8080
61 | name: example-eight
62 | plugins:
63 | - name: apiKey
64 | version: v1.2.0
65 |
66 | ---
67 |
68 | apiVersion: kanali.io/v1
69 | kind: ApiKey
70 | metadata:
71 | name: lonzos-apikey
72 | namespace: application
73 | spec:
74 | data: 2b06207cce17aeeee93dba801dcfc8055638fe6eb00b0e644c48161bdcd66da42f6951ebc3cca02302f90c0ed2f42ecf28673fe1873af704c023367e3a7113919b8b85bd62384f5261d08fe4af51174141ae2836ab628d1ed58f030fca12c4fe53e8f1c8f836c9026a635ca6d419fb873fdeb621b40cfe336bbbf0c5fc2c352d044ae0f8a59b489a62e468a5b7e090f42127a8ad7a796cb2f67dfd81756d232b19f2522cf911809747c61fbe7f051219fe763e173ab074ae332f25c63bc2d6a5e190ed413f7bf830c006789677a69b4855ce54f06fd2a68d38a5267cec571b0de59198b537212d422cfc188366ce54e5f81a3485f5ead872688efdcdb0a3549b
75 |
76 | ---
77 |
78 | apiVersion: kanali.io/v1
79 | kind: ApiKeyBinding
80 | metadata:
81 | name: example-eight
82 | namespace: application
83 | spec:
84 | proxy: example-eight
85 | keys:
86 | - name: lonzos-apikey
87 | quota: 4
88 | defaultRule:
89 | global: true
90 |
--------------------------------------------------------------------------------
/examples/exampleNine.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: v1
4 | kind: Namespace
5 | metadata:
6 | name: application
7 | labels:
8 | name: application
9 |
10 | ---
11 |
12 | apiVersion: extensions/v1beta1
13 | kind: Deployment
14 | metadata:
15 | name: example-nine
16 | namespace: application
17 | spec:
18 | replicas: 1
19 | selector:
20 | matchLabels:
21 | k8s-app: example-nine
22 | template:
23 | metadata:
24 | labels:
25 | k8s-app: example-nine
26 | spec:
27 | containers:
28 | - name: example-nine
29 | imagePullPolicy: Always
30 | image: fbgrecojr/helloworld:golang
31 | ports:
32 | - containerPort: 8080
33 |
34 | ---
35 |
36 | apiVersion: v1
37 | kind: Service
38 | metadata:
39 | name: example-nine
40 | namespace: application
41 | spec:
42 | selector:
43 | k8s-app: example-nine
44 | ports:
45 | - name: http
46 | port: 8080
47 | type: ClusterIP
48 |
49 | ---
50 |
51 | apiVersion: kanali.io/v1
52 | kind: ApiProxy
53 | metadata:
54 | name: example-nine
55 | namespace: application
56 | spec:
57 | path: /api/v1/example-nine
58 | target: /
59 | service:
60 | port: 8080
61 | name: example-nine
62 | plugins:
63 | - name: apiKey
64 | version: v1.2.0
65 |
66 | ---
67 |
68 | apiVersion: kanali.io/v1
69 | kind: ApiKey
70 | metadata:
71 | name: johns-apikey
72 | namespace: application
73 | spec:
74 | data: 2b06207cce17aeeee93dba801dcfc8055638fe6eb00b0e644c48161bdcd66da42f6951ebc3cca02302f90c0ed2f42ecf28673fe1873af704c023367e3a7113919b8b85bd62384f5261d08fe4af51174141ae2836ab628d1ed58f030fca12c4fe53e8f1c8f836c9026a635ca6d419fb873fdeb621b40cfe336bbbf0c5fc2c352d044ae0f8a59b489a62e468a5b7e090f42127a8ad7a796cb2f67dfd81756d232b19f2522cf911809747c61fbe7f051219fe763e173ab074ae332f25c63bc2d6a5e190ed413f7bf830c006789677a69b4855ce54f06fd2a68d38a5267cec571b0de59198b537212d422cfc188366ce54e5f81a3485f5ead872688efdcdb0a3549b
75 |
76 | ---
77 |
78 | apiVersion: kanali.io/v1
79 | kind: ApiKeyBinding
80 | metadata:
81 | name: example-nine
82 | namespace: application
83 | spec:
84 | proxy: example-nine
85 | keys:
86 | - name: johns-apikey
87 | rate:
88 | amount: 5
89 | unit: second
90 | defaultRule:
91 | global: true
92 |
--------------------------------------------------------------------------------
/examples/exampleSeven.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: v1
4 | kind: Namespace
5 | metadata:
6 | name: application
7 | labels:
8 | name: application
9 |
10 | ---
11 |
12 | apiVersion: extensions/v1beta1
13 | kind: Deployment
14 | metadata:
15 | name: example-seven
16 | namespace: application
17 | spec:
18 | replicas: 1
19 | selector:
20 | matchLabels:
21 | k8s-app: example-seven
22 | template:
23 | metadata:
24 | labels:
25 | k8s-app: example-seven
26 | spec:
27 | containers:
28 | - name: example-seven
29 | imagePullPolicy: Always
30 | image: fbgrecojr/helloworld:golang
31 | ports:
32 | - containerPort: 8080
33 |
34 | ---
35 |
36 | apiVersion: v1
37 | kind: Service
38 | metadata:
39 | name: example-seven
40 | namespace: application
41 | spec:
42 | selector:
43 | k8s-app: example-seven
44 | ports:
45 | - name: http
46 | port: 8080
47 | type: ClusterIP
48 |
49 | ---
50 |
51 | apiVersion: kanali.io/v1
52 | kind: ApiProxy
53 | metadata:
54 | name: example-seven
55 | namespace: application
56 | spec:
57 | path: /api/v1/example-seven
58 | target: /
59 | service:
60 | port: 8080
61 | name: example-seven
62 | plugins:
63 | - name: apiKey
64 | version: v1.2.0
65 |
66 | ---
67 |
68 | apiVersion: kanali.io/v1
69 | kind: ApiKey
70 | metadata:
71 | name: bobs-apikey
72 | namespace: application
73 | spec:
74 | data: 2b06207cce17aeeee93dba801dcfc8055638fe6eb00b0e644c48161bdcd66da42f6951ebc3cca02302f90c0ed2f42ecf28673fe1873af704c023367e3a7113919b8b85bd62384f5261d08fe4af51174141ae2836ab628d1ed58f030fca12c4fe53e8f1c8f836c9026a635ca6d419fb873fdeb621b40cfe336bbbf0c5fc2c352d044ae0f8a59b489a62e468a5b7e090f42127a8ad7a796cb2f67dfd81756d232b19f2522cf911809747c61fbe7f051219fe763e173ab074ae332f25c63bc2d6a5e190ed413f7bf830c006789677a69b4855ce54f06fd2a68d38a5267cec571b0de59198b537212d422cfc188366ce54e5f81a3485f5ead872688efdcdb0a3549b
75 |
76 | ---
77 |
78 | apiVersion: kanali.io/v1
79 | kind: ApiKeyBinding
80 | metadata:
81 | name: example-seven
82 | namespace: application
83 | spec:
84 | proxy: example-seven
85 | keys:
86 | - name: bobs-apikey
87 | subpaths:
88 | - path: /foo
89 | rule:
90 | granular:
91 | verbs:
92 | - GET
93 |
--------------------------------------------------------------------------------
/helm/charts/jaeger/templates/collector.yml:
--------------------------------------------------------------------------------
1 | # ---
2 | #
3 | # apiVersion: extensions/v1beta1
4 | # kind: Deployment
5 | # metadata:
6 | # name: jaeger-collector
7 | # namespace: tracing
8 | # spec:
9 | # replicas: 1
10 | # selector:
11 | # name: jaeger-collector
12 | # strategy:
13 | # type: Recreate
14 | # selector:
15 | # matchLabels:
16 | # name: jaeger-collector
17 | # template:
18 | # metadata:
19 | # labels:
20 | # name: jaeger-collector
21 | # jaeger-infra: collector
22 | # spec:
23 | # containers:
24 | # - name: jaeger-collector
25 | # image: registry.nmlv.nml.com/epitropos/jaegertracing/jaeger-collector:latest
26 | # command:
27 | # - /go/bin/collector-linux
28 | # args:
29 | # - -cassandra.connections-per-host=2
30 | # - -cassandra.keyspace=jaeger_v1_test
31 | # - -cassandra.max-retry-attempts=5
32 | # - -cassandra.port=9042
33 | # - -cassandra.proto-version=4
34 | # - -cassandra.servers=jaeger-cassandra.tracing.svc.cluster.local
35 | # - -cassandra.socket-keep-alive=0h0m0s
36 | # - -cassandra.timeout=0h1m0s
37 | # - -collector.http-port=14268
38 | # - -collector.num-workers=50
39 | # - -collector.port=14267
40 | # - -collector.queue-size=2000
41 | # - -collector.write-cache-ttl=12h0m0s
42 | # - -dependency-storage.data-frequency=24h0m0s
43 | # - -dependency-storage.type=cassandra
44 | # - -log-level=debug
45 | # - -runtime-metrics-frequency=0h0m1s
46 | # - -span-storage.type=cassandra
47 | # ports:
48 | # - containerPort: 14267
49 | # protocol: UDP
50 | # - containerPort: 14268
51 | # protocol: TCP
52 | # imagePullPolicy: Always
53 | #
54 | # ---
55 | #
56 | # apiVersion: v1
57 | # kind: Service
58 | # metadata:
59 | # name: jaeger-collector
60 | # namespace: tracing
61 | # labels:
62 | # jaeger-infra: collector
63 | # spec:
64 | # ports:
65 | # - name: port-one
66 | # port: 14267
67 | # protocol: TCP
68 | # targetPort: 14267
69 | # - name: port-two
70 | # port: 14268
71 | # protocol: TCP
72 | # targetPort: 14268
73 | # selector:
74 | # name: jaeger-collector
75 | # type: ClusterIP
76 |
--------------------------------------------------------------------------------
/flow/flow.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package flow
22 |
23 | import (
24 | "context"
25 | "net/http"
26 |
27 | "github.com/Sirupsen/logrus"
28 | "github.com/northwesternmutual/kanali/metrics"
29 | "github.com/northwesternmutual/kanali/spec"
30 | "github.com/northwesternmutual/kanali/tracer"
31 | "github.com/opentracing/opentracing-go"
32 | )
33 |
34 | // Flow is a list of steps
35 | type Flow []Step
36 |
37 | // Add appends a step to a flow
38 | func (f *Flow) Add(steps ...Step) {
39 | for _, step := range steps {
40 | *f = append(*f, step)
41 | }
42 | }
43 |
44 | // Play executes all step in a flow in the order they were added.
45 | func (f *Flow) Play(ctx context.Context, proxy *spec.APIProxy, metrics *metrics.Metrics, w http.ResponseWriter, r *http.Request, resp *http.Response, trace opentracing.Span) error {
46 | logrus.Debugf("flow with %d step about to play", len(*f))
47 | for _, step := range *f {
48 | logrus.Debugf("playing step %s", step.GetName())
49 | if err := step.Do(ctx, proxy, metrics, w, r, resp, trace); err != nil {
50 | trace.SetTag(tracer.Error, true)
51 | trace.LogKV(
52 | "event", "error",
53 | "error.message", err.Error(),
54 | )
55 | return err
56 | }
57 | }
58 | return nil
59 | }
60 |
--------------------------------------------------------------------------------
/helm/templates/decryptSecret.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: v1
4 | kind: Secret
5 | metadata:
6 | name: kanali-key-decription
7 | namespace: default
8 | type: Opaque
9 | data:
10 | key.pem: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ0KTUlJRW93SUJBQUtDQVFFQXFZZG5YMGplT1gwelp1VEcwekRKK3QxcXpBNjNNTXhZbGx3Y05kU0l1REN2VDZSbg0Kd1NnMG54K1BTV1lRUXFYQ043cThDU3NCZ3A2UU5uZUN4TDNBLzFKelY3dy9mTXlXTEl1dVNPbjdHaThJeis3RQ0KTWI5ZGJQemVqSGJ4NDRURHpqSW0rK3h3cHlSNTZlNlpxaThoK1hGZk5UeFExSVdzaVVRSnNFdk5PdXM5a205Mg0KZ1RaOWhKTlg4R2dmQ3Z1UDBCakRzWEdqUVZoVXU3dExPNGVjY1h2WmpuTExZck9NOXF0a2tFZjhlRC8xZGg1Kw0KbnZtZ0NsMlFJOVlIcStPdmxDTHRBYzJtMXR4UHh0dkN1cjUxUmpvUFVYa21DZ2xqWmRxYWRTS2NVUnUvRFhNRQ0KY0xGMTNzbVFsNkpxNGdHelFZOTE5UEMxRWpraEdLWkEvRUZ0VXdJREFRQUJBb0lCQURONnI1UktyMWlyd1Rraw0KalkvQ0NBT0t5d3h1QjRqazlKMnNHTkRyMmh4OGhDL2VEN2VpK3QrN0dLckVPSG5VbGZhUVdOczcyUGlPSitLeQ0KUmQ1eWRMSFRtcnp3cUNMQWlYVzdjTkFwWlJ2ZFhvS3QwWnY5cldRVUlZeHI3aVlWd2RQU2ZPNFJMV0JEL2xWZw0KSS85KzBvVkp2UXlRWlVjejFHSFdiRTdCcGUrVzB2a0RlRlh4bENQMzlVV216ZkNDaGh6Q0ZYVFRndmwyRUhkeA0KUU1Qbm4zZGhqZit1QnRaWlhVcGpvNmxGTklyV2xxblNnRTdrckpVZnpENVRWZ0cxcThBZkY1QmRZbUF0RW9JYQ0KbldRcm41MSsrc2VKUWNDaDBnNGJSV2Jqazc5UXAydWFub2VWWnBaZFJRU2FndVVuSmJPTFhrU0pBaXFBY3pvRA0KTVdxWFNXa0NnWUVBMlNwSk1NZE4zMHlqVFplSFBMYjcyd3VtN2wrWlo2V3J2OHp0NkFXUEEzVDdpNmRCUlNLaQ0KRDZ5Y0NMazZWNlNXT0VnbTJNcnVZeHd2ZDNsWTNYSGpKd21mcFdxUWpZcktwMXU2dytCOE5LaGNkQXgybGNuMw0KVXY3cnYxQS9FdDdRMWFiekw5ZS92dGVQMG81c1lEdmZER3hNRnQ5amd1NDNTL3JwSE9XVGUxMENnWUVBeDloYQ0KaUhIY1BmcHlzWlI1a1gwZU9UOHBYcE9HNlVmckV3Mzh5WnFJeUNHejRmeVpLUStidHRnMFdxSE1wV2UxbXc5UA0KcFlCcTJQdHpVdXlKVkdmdDl4VFA3T3Yxb2E3NDFjWUZwQmJZdFc0ZTgxQ1dQQmo0Y1pOQnpxMVkwZGc0SHcwcw0KYUdZQlE5TDg3a29NMGVsZmpObzIvSEpmVFZjOE9XVTYwRVdMc0c4Q2dZQmcyQlN2cGhHNkpRa21UdzdHS3F3Qw0KTVI0T2E1K1RzelAyWXNNdGwxMEJvNmVSemRLenJCQXRnVUpNT1o0ays0YnFMbkwwZHZyOFE5Ti9LaVJSRExySg0KNithLzg5Zm01eUFjcGpHUnJJaDNTeVYvc3hjbkVWdzBMTzZnOEg1UVFnRkxaaHBKR2FPdXpaNmJ2VnZqUm8vZg0Ka0dRV1J5U3ZmT0E0Qi9yeElnZzFHUUtCZ0NNL1ZxQndMSjlGMkFyWUhDVDhBMk9uYnoxK0diSjFlOUd0aXVObg0KL1M0SE83bmxHb0p5ZlUxZmpzUlplMFhGSi9QRVhKRGRPSHN5eG1GZTFNM3RVcnhja0Z2Q05sMmhCY1IybTdJWQ0KVVhxV2hLRDNtcmZZMDZEOGp3UEw4VGw1d0ZSQnQ0NW1SMXpXRHNSY2pTeE0xQXg4eEd2OEpERDQ3T2RXb212dg0KaURiREFvR0JBSy9XcHpvTjU5dnNhdytQY3hTOTNXZ0JBbkdlOXErbVB4TlBMajB4YWFraU5GbjdGQ2RpR1Y4eg0KNFduMHN1YTQ4djdRY0piS2NMMFpiWGt5NjFFWlYzSHFBeXpoV1o2alNTUW9NMzdTL25QcHE1RVBUZnM0RHZubA0KQkE3ZFdleUxGbk43ZVBWU2tMMVNFUzU4TXBNTWl1bnJVby9DaTZPamlPeU43eW5uVTZ0RQ0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0=
--------------------------------------------------------------------------------
/metrics/metrics_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package metrics
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/stretchr/testify/assert"
27 | )
28 |
29 | func TestAdd(t *testing.T) {
30 | f := &Metrics{}
31 | f.Add(Metric{"nameOne", "valueOne", false})
32 | f.Add(Metric{"nameTwo", "valueTwo", true}, Metric{"nameThree", "valueThree", false})
33 | assert.Equal(t, len(*f), 3)
34 | assert.False(t, (*f)[0].Index)
35 | assert.True(t, (*f)[1].Index)
36 | assert.False(t, (*f)[2].Index)
37 | assert.Equal(t, (*f)[0].Name, "nameOne")
38 | assert.Equal(t, (*f)[1].Name, "nameTwo")
39 | assert.Equal(t, (*f)[2].Name, "nameThree")
40 | assert.Equal(t, (*f)[0].Value, "valueOne")
41 | assert.Equal(t, (*f)[1].Value, "valueTwo")
42 | assert.Equal(t, (*f)[2].Value, "valueThree")
43 | }
44 |
45 | func TestGet(t *testing.T) {
46 | f := &Metrics{}
47 | f.Add(Metric{"nameOne", "valueOne", false})
48 | f.Add(Metric{"nameTwo", "valueTwo", true}, Metric{"nameThree", "valueThree", false})
49 | assert.Nil(t, f.Get("nameFour"))
50 | assert.Equal(t, f.Get("nameOne"), &Metric{"nameOne", "valueOne", false})
51 | assert.Equal(t, f.Get("nameTwo"), &Metric{"nameTwo", "valueTwo", true})
52 | assert.Equal(t, f.Get("nameThree"), &Metric{"nameThree", "valueThree", false})
53 | }
54 |
--------------------------------------------------------------------------------
/server/udp_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package server
22 |
23 | import (
24 | "net"
25 | "testing"
26 | "time"
27 |
28 | "github.com/northwesternmutual/kanali/config"
29 | "github.com/northwesternmutual/kanali/spec"
30 | "github.com/spf13/viper"
31 | "github.com/stretchr/testify/assert"
32 | )
33 |
34 | func TestStartUDPServer(t *testing.T) {
35 |
36 | assert := assert.New(t)
37 |
38 | viper.SetDefault(config.FlagServerPeerUDPPort.GetLong(), 10001)
39 | defer viper.Reset()
40 |
41 | go func() {
42 | if err := StartUDPServer(); err != nil {
43 | assert.Fail("upd server threw an error: ", err.Error())
44 | }
45 | }()
46 |
47 | time.Sleep(time.Millisecond * 100)
48 |
49 | serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:10001")
50 | if err != nil {
51 | assert.Fail("there was an error: ", err.Error())
52 | }
53 |
54 | conn, err := net.DialUDP("udp", nil, serverAddr)
55 | if err != nil {
56 | assert.Fail("there was an error: ", err.Error())
57 | }
58 |
59 | defer conn.Close()
60 |
61 | _, err = conn.Write([]byte("namespace-one,proxy-one,key-one"))
62 | if err != nil {
63 | assert.Fail("there was an error: ", err.Error())
64 | }
65 |
66 | time.Sleep(time.Millisecond * 100)
67 |
68 | assert.False(spec.TrafficStore.IsEmpty())
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/tracer/jaeger.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package tracer
22 |
23 | import (
24 | "fmt"
25 | "io"
26 | "time"
27 |
28 | "github.com/Sirupsen/logrus"
29 | "github.com/northwesternmutual/kanali/config"
30 | "github.com/opentracing/opentracing-go"
31 | "github.com/spf13/viper"
32 | jaegerConfig "github.com/uber/jaeger-client-go/config"
33 | )
34 |
35 | type customLogger struct{}
36 |
37 | func (l customLogger) Error(msg string) {
38 | logrus.Error(msg)
39 | }
40 |
41 | func (l customLogger) Infof(msg string, args ...interface{}) {
42 | logrus.Info(fmt.Sprintf(msg, args...))
43 | }
44 |
45 | // Jaeger creates a new opentracing compatible tracer
46 | func Jaeger() (opentracing.Tracer, io.Closer, error) {
47 |
48 | cfg := jaegerConfig.Configuration{
49 | Sampler: &jaegerConfig.SamplerConfig{
50 | Type: "const",
51 | SamplingServerURL: viper.GetString(config.FlagTracingJaegerServerURL.GetLong()),
52 | Param: 1,
53 | },
54 | Reporter: &jaegerConfig.ReporterConfig{
55 | LogSpans: true,
56 | BufferFlushInterval: 1 * time.Second,
57 | LocalAgentHostPort: fmt.Sprintf("%s:5775", viper.GetString(config.FlagTracingJaegerAgentURL.GetLong())),
58 | },
59 | }
60 |
61 | return cfg.New("kanali", jaegerConfig.Logger(customLogger{}))
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/helm/charts/grafana/README.md:
--------------------------------------------------------------------------------
1 | # Grafana Helm Chart
2 |
3 | * Installs the web dashboarding system [Grafana](http://grafana.org/)
4 |
5 | ## TL;DR;
6 |
7 | ```console
8 | $ helm install stable/grafana
9 | ```
10 |
11 | ## Installing the Chart
12 |
13 | To install the chart with the release name `my-release`:
14 |
15 | ```console
16 | $ helm install --name my-release stable/grafana
17 | ```
18 |
19 | ## Uninstalling the Chart
20 |
21 | To uninstall/delete the my-release deployment:
22 |
23 | ```console
24 | $ helm delete my-release
25 | ```
26 |
27 | The command removes all the Kubernetes components associated with the chart and deletes the release.
28 |
29 |
30 | ## Configuration
31 |
32 | | Parameter | Description | Default |
33 | |----------------------------------------|-------------------------------------|---------------------------------------------------|
34 | | `server.image` | Container image to run | grafana/grafana:latest |
35 | | `server.adminUser` | Admin user username | admin |
36 | | `server.adminPassword` | Admin user password | admin |
37 | | `server.persistentVolume.enabled` | Create a volume to store data | true |
38 | | `server.persistentVolume.size` | Size of persistent volume claim | 1Gi RW |
39 | | `server.persistentVolume.storageClass` | Type of persistent volume claim | `nil` (uses alpha storage class annotation) |
40 | | `server.persistentVolume.accessMode` | ReadWriteOnce or ReadOnly | [ReadWriteOnce] |
41 | | `server.persistentVolume.existingClaim`| Existing persistent volume claim | null |
42 | | `server.persistentVolume.subPath` | Subdirectory of pvc to mount | null |
43 | | `server.resources` | Server resource requests and limits | requests: {cpu: 100m, memory: 100Mi} |
44 | | `server.serviceType` | ClusterIP, NodePort, or LoadBalancer| ClusterIP |
45 | | `server.setDatasource.enabled` | Creates grafana datasource with job | false |
46 |
--------------------------------------------------------------------------------
/steps/writeresponse.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package steps
22 |
23 | import (
24 | "context"
25 | "io"
26 | "net/http"
27 | "strconv"
28 |
29 | "github.com/Sirupsen/logrus"
30 | "github.com/northwesternmutual/kanali/metrics"
31 | "github.com/northwesternmutual/kanali/spec"
32 | "github.com/northwesternmutual/kanali/tracer"
33 | "github.com/opentracing/opentracing-go"
34 | )
35 |
36 | // WriteResponseStep is factory that defines a step responsible for writing
37 | // an HTTP response
38 | type WriteResponseStep struct{}
39 |
40 | // GetName retruns the name of the WriteResponseStep step
41 | func (step WriteResponseStep) GetName() string {
42 | return "Write Response"
43 | }
44 |
45 | // Do executes the logic of the WriteResponseStep step
46 | func (step WriteResponseStep) Do(ctx context.Context, proxy *spec.APIProxy, m *metrics.Metrics, w http.ResponseWriter, r *http.Request, resp *http.Response, span opentracing.Span) error {
47 |
48 | for k, v := range resp.Header {
49 | for _, value := range v {
50 | w.Header().Set(k, value)
51 | }
52 | }
53 |
54 | m.Add(metrics.Metric{Name: "http_response_code", Value: strconv.Itoa(resp.StatusCode), Index: true})
55 |
56 | tracer.HydrateSpanFromResponse(resp, span)
57 |
58 | w.WriteHeader(resp.StatusCode)
59 |
60 | if _, err := io.Copy(w, resp.Body); err != nil {
61 | logrus.Warnf("error copying data to http response: %s", err.Error())
62 | }
63 |
64 | return nil
65 | }
66 |
--------------------------------------------------------------------------------
/handlers/logger_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package handlers
22 |
23 | import (
24 | "bytes"
25 | "fmt"
26 | "io/ioutil"
27 | "net"
28 | "net/http"
29 | "os"
30 | "strings"
31 | "testing"
32 |
33 | "github.com/Sirupsen/logrus"
34 | "github.com/stretchr/testify/assert"
35 | )
36 |
37 | func TestLogger(t *testing.T) {
38 | server := &http.Server{Addr: "127.0.0.1:40123", Handler: Logger(Handler{InfluxController: nil, H: IncomingRequest})}
39 | listener, _ := net.Listen("tcp4", "127.0.0.1:40123")
40 | go server.Serve(listener)
41 | defer server.Close()
42 |
43 | writer := new(bytes.Buffer)
44 | logrus.SetOutput(writer)
45 | resp, err := http.Get("http://127.0.0.1:40123/")
46 | logrus.SetOutput(os.Stdout)
47 | assert.Nil(t, err)
48 | assert.Equal(t, resp.Header.Get("Content-Type"), "application/json")
49 |
50 | body, _ := ioutil.ReadAll(resp.Body)
51 | assert.Equal(t, string(body), fmt.Sprintf("%s\n", `{"code":404,"msg":"proxy not found"}`))
52 | assert.Equal(t, resp.StatusCode, 404)
53 |
54 | logOutput := writer.String()
55 | assert.True(t, strings.Contains(logOutput, `msg="proxy not found"`))
56 | assert.True(t, strings.Contains(logOutput, `msg="request details"`))
57 | assert.True(t, strings.Contains(logOutput, `level=error`))
58 | assert.True(t, strings.Contains(logOutput, `client ip=127.0.0.1`))
59 | assert.True(t, strings.Contains(logOutput, `method=GET`))
60 | assert.True(t, strings.Contains(logOutput, `uri="/"`))
61 | }
62 |
--------------------------------------------------------------------------------
/helm/charts/grafana/templates/NOTES.txt:
--------------------------------------------------------------------------------
1 | 1. Get your '{{ .Values.server.adminUser }}' user password by running:
2 |
3 | kubectl get secret --namespace {{ .Release.Namespace }} {{ template "grafana.server.fullname" . }} -o jsonpath="{.data.grafana-admin-password}" | base64 --decode ; echo
4 |
5 | 2. The Grafana server can be accessed via port {{ .Values.server.httpPort }} on the following DNS name from within your cluster:
6 |
7 | {{ template "grafana.server.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
8 | {{ if .Values.server.ingress.enabled }}
9 | From outside the cluster, the server URL(s) are:
10 | {{- range .Values.server.ingress.hosts }}
11 | http://{{ . }}
12 | {{- end }}
13 | {{ else }}
14 | Get the Grafana URL to visit by running these commands in the same shell:
15 | {{ if contains "NodePort" .Values.server.serviceType -}}
16 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "grafana.server.fullname" . }})
17 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
18 | echo http://$NODE_IP:$NODE_PORT
19 | {{ else if contains "LoadBalancer" .Values.server.serviceType -}}
20 | NOTE: It may take a few minutes for the LoadBalancer IP to be available.
21 | You can watch the status of by running 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "grafana.server.fullname" . }}'
22 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "grafana.server.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
23 | echo http://$SERVICE_IP:{{ .Values.server.httpPort -}}
24 | {{ else if contains "ClusterIP" .Values.server.serviceType }}
25 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "grafana.fullname" . }},component={{ .Values.server.name }}" -o jsonpath="{.items[0].metadata.name}")
26 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 3000
27 | {{- end }}
28 | {{- end }}
29 |
30 | 3. Login with the password from step 1 and the username: {{ .Values.server.adminUser }}
31 |
32 | {{- if .Values.server.persistentVolume.enabled }}
33 | {{- else }}
34 | #################################################################################
35 | ###### WARNING: Persistence is disabled!!! You will lose your data when #####
36 | ###### the Grafana pod is terminated. #####
37 | #################################################################################
38 | {{- end }}
39 |
--------------------------------------------------------------------------------
/config/server.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package config
22 |
23 | func init() {
24 | Flags.Add(
25 | FlagServerPort,
26 | FlagServerBindAddress,
27 | FlagServerPeerUDPPort,
28 | FlagServerProxyProtocol,
29 | )
30 | }
31 |
32 | var (
33 | // FlagServerPort sets the port that Kanali will listen on for incoming requests
34 | FlagServerPort = Flag{
35 | Long: "server.port",
36 | Short: "p",
37 | Value: 0,
38 | Usage: "Sets the port that Kanali will listen on for incoming requests.",
39 | }
40 | // FlagServerBindAddress specifies the network address that Kanali will listen on for incoming requests
41 | FlagServerBindAddress = Flag{
42 | Long: "server.bind_address",
43 | Short: "b",
44 | Value: "0.0.0.0",
45 | Usage: "Network address that Kanali will listen on for incoming requests.",
46 | }
47 | // FlagServerPeerUDPPort sets the port that all Kanali instances will communicate to each other over
48 | FlagServerPeerUDPPort = Flag{
49 | Long: "server.peer_udp_port",
50 | Short: "",
51 | Value: 10001,
52 | Usage: "Sets the port that all Kanali instances will communicate to each other over.",
53 | }
54 | // FlagServerProxyProtocol maintains the integrity of the remote client IP address when incoming traffic to Kanali includes the Proxy Protocol header
55 | FlagServerProxyProtocol = Flag{
56 | Long: "server.proxy_protocol",
57 | Short: "",
58 | Value: false,
59 | Usage: "Maintain the integrity of the remote client IP address when incoming traffic to Kanali includes the Proxy Protocol header.",
60 | }
61 | )
62 |
--------------------------------------------------------------------------------
/steps/writeresponse_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package steps
22 |
23 | import (
24 | "bytes"
25 | "context"
26 | "io/ioutil"
27 | "net/http"
28 | "net/http/httptest"
29 | "testing"
30 |
31 | "github.com/northwesternmutual/kanali/metrics"
32 | opentracing "github.com/opentracing/opentracing-go"
33 | "github.com/stretchr/testify/assert"
34 | )
35 |
36 | func TestWriteResponseGetName(t *testing.T) {
37 | step := WriteResponseStep{}
38 | assert.Equal(t, step.GetName(), "Write Response", "step name is incorrect")
39 | }
40 |
41 | func TestWriteResponseDo(t *testing.T) {
42 | step := WriteResponseStep{}
43 | writer := httptest.NewRecorder()
44 | response := &httptest.ResponseRecorder{
45 | Code: 200,
46 | HeaderMap: http.Header{
47 | "One": []string{"two"},
48 | "Three": []string{"four"},
49 | },
50 | Body: bytes.NewBuffer([]byte("this is my mock response body")),
51 | }
52 | err := step.Do(context.Background(), nil, &metrics.Metrics{}, writer, nil, response.Result(), opentracing.StartSpan("test span"))
53 | defer writer.Result().Body.Close()
54 | assert.Nil(t, err)
55 | assert.Equal(t, writer.Result().StatusCode, 200)
56 | assert.Equal(t, writer.Result().Status, "OK")
57 | assert.Equal(t, len(writer.Result().Header), 2)
58 | assert.Equal(t, writer.Result().Header.Get("one"), "two")
59 | assert.Equal(t, writer.Result().Header.Get("three"), "four")
60 | bodyBytes, err := ioutil.ReadAll(writer.Result().Body)
61 | assert.Nil(t, err)
62 | assert.Equal(t, string(bodyBytes), "this is my mock response body")
63 | }
64 |
--------------------------------------------------------------------------------
/helm/charts/jaeger/templates/agent.yml:
--------------------------------------------------------------------------------
1 | # ---
2 | #
3 | # apiVersion: extensions/v1beta1
4 | # kind: Deployment
5 | # metadata:
6 | # name: jaeger-agent
7 | # namespace: tracing
8 | # spec:
9 | # replicas: 1
10 | # selector:
11 | # name: jaeger-agent
12 | # strategy:
13 | # type: Recreate
14 | # selector:
15 | # matchLabels:
16 | # name: jaeger-agent
17 | # template:
18 | # metadata:
19 | # labels:
20 | # name: jaeger-agent
21 | # jaeger-infra: agent
22 | # spec:
23 | # containers:
24 | # - name: agent
25 | # image: registry.nmlv.nml.com/epitropos/jaegertracing/jaeger-agent:latest
26 | # command:
27 | # - /go/bin/agent-linux
28 | # args:
29 | # - -collector.host-port=jaeger-collector.tracing.svc.cluster.local:14267
30 | # - -discovery.min-peers=3
31 | # - -http-server.host-port=:5778
32 | # - -processor.jaeger-binary.server-host-port=:6832
33 | # - -processor.jaeger-binary.server-max-packet-size=65000
34 | # - -processor.jaeger-binary.server-queue-size=1000
35 | # - -processor.jaeger-binary.workers=10
36 | # - -processor.jaeger-compact.server-host-port=:6831
37 | # - -processor.jaeger-compact.server-max-packet-size=65000
38 | # - -processor.jaeger-compact.server-queue-size=1000
39 | # - -processor.jaeger-compact.workers=10
40 | # - -processor.zipkin-compact.server-host-port=:5775
41 | # - -processor.zipkin-compact.server-max-packet-size=65000
42 | # - -processor.zipkin-compact.server-queue-size=1000
43 | # - -processor.zipkin-compact.workers=10
44 | # ports:
45 | # - containerPort: 5775
46 | # protocol: UDP
47 | # - containerPort: 6831
48 | # protocol: UDP
49 | # - containerPort: 6832
50 | # protocol: UDP
51 | # - containerPort: 5778
52 | # protocol: TCP
53 | # resources: {}
54 | # imagePullPolicy: Always
55 | # securityContext: {}
56 | #
57 | # ---
58 | #
59 | # apiVersion: v1
60 | # kind: Service
61 | # metadata:
62 | # name: jaeger-agent
63 | # namespace: tracing
64 | # labels:
65 | # jaeger-infra: agent
66 | # spec:
67 | # ports:
68 | # - name: agent-zipkin-thrift
69 | # port: 5775
70 | # protocol: UDP
71 | # targetPort: 5775
72 | # - name: agent-compact
73 | # port: 6831
74 | # protocol: UDP
75 | # targetPort: 6831
76 | # - name: agent-binary
77 | # port: 6832
78 | # protocol: UDP
79 | # targetPort: 6832
80 | # - name: agent-sampling
81 | # port: 5778
82 | # protocol: TCP
83 | # targetPort: 5778
84 | # selector:
85 | # name: jaeger-agent
86 | # type: ClusterIP
87 |
--------------------------------------------------------------------------------
/scripts/updateLicense.py:
--------------------------------------------------------------------------------
1 | from __future__ import (
2 | absolute_import, print_function, division, unicode_literals
3 | )
4 |
5 | import re
6 | import sys
7 | from datetime import datetime
8 |
9 | CURRENT_YEAR = datetime.today().year
10 |
11 | LICENSE_BLOB = """Copyright (c) %d Northwestern Mutual.
12 |
13 | Permission is hereby granted, free of charge, to any person obtaining a copy
14 | of this software and associated documentation files (the "Software"), to deal
15 | in the Software without restriction, including without limitation the rights
16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 | copies of the Software, and to permit persons to whom the Software is
18 | furnished to do so, subject to the following conditions:
19 |
20 | The above copyright notice and this permission notice shall be included in
21 | all copies or substantial portions of the Software.
22 |
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 | THE SOFTWARE.""" % CURRENT_YEAR
30 |
31 | LICENSE_BLOB_LINES_GO = [
32 | ('// ' + l).strip() + '\n' for l in LICENSE_BLOB.split('\n')
33 | ]
34 |
35 | COPYRIGHT_RE = re.compile(r'Copyright \(c\) (\d+)', re.I)
36 |
37 |
38 | def update_go_license(name):
39 | with open(name) as f:
40 | orig_lines = list(f)
41 | lines = list(orig_lines)
42 |
43 | found = False
44 | changed = False
45 | for i, line in enumerate(lines[:5]):
46 | m = COPYRIGHT_RE.search(line)
47 | if not m:
48 | continue
49 |
50 | found = True
51 | break
52 |
53 | if not found:
54 | if 'Code generated by' in lines[0]:
55 | lines[1:1] = ['\n'] + LICENSE_BLOB_LINES_GO
56 | else:
57 | lines[0:0] = LICENSE_BLOB_LINES_GO + ['\n']
58 | changed = True
59 |
60 | if changed:
61 | with open(name, 'w') as f:
62 | for line in lines:
63 | f.write(line)
64 |
65 |
66 | def main():
67 | if len(sys.argv) == 1:
68 | print('USAGE: %s FILE ...' % sys.argv[0])
69 | sys.exit(1)
70 |
71 | for name in sys.argv[1:]:
72 | if name.endswith('.go'):
73 | update_go_license(name)
74 | else:
75 | raise NotImplementedError('Unsupported file type: %s' % name)
76 |
77 |
78 | if __name__ == "__main__":
79 | main()
80 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 |
3 | language: go
4 |
5 | go:
6 | - 1.8.3
7 |
8 | env:
9 | global:
10 | - secure: kqPYySZHukKk79buA26c9staHoX7roMpp13aalARyDKoz8qdfgcCLMfBkAjI3OpIYRpqZHQlla67YBCrBgINA6ahoUm4AQF2nK7P7ll3ZM/VHwjxY6DY+wN7sSlMotBwu6qSkoCs34U8Vd1c6uCjNmDlGGgeTAY2WlLAnHNAe8FVNlYKX3fb2MOolKeLuZLh36VPbrF1409WyUFU79C20CMfN5X0sq13xOqwWJHjOaYF+wnEoOsFGt62lqGmBWXEwTOfFRKwVrYleDhR+LU0onW7k12KHydrAcI4j41mDfi0GUmLYPgCdyoffcyPNbdu6ZjmDEhF3ZSl9QXVoaSaEDIOinnbi9WJvmTRa1pE8rvB2VVz+pGB1V24aYMKZreSdkuCtFfJ1UxklW88312NcItPBVJbInY4Kqn/gMA9BwFMtzCD+pBpR06r83P3iOFs7zvzxTnuuBQ+TJUMw8PQBaRlgrDyrFY/9ORHgSGCSrquPg6nVMcxvHQoU4ugkC6fbSqnivq7TEYHTfKcUEVpLqqK+pdfB1qqCo2nAtfsViRPV2c5BXg5JyJT0uBIjukPnt7k/sJ3BOex1PAow0lgsURyy3JnRf8qAg4h28cZBBhTZCj3ovyC9TH9pcI/46EqmP7Yqs1L0UTReOG8+0cHT+d8wxECnWI8PjqN20rGWw8=
11 | - secure: BUFDeBBeZZ4yDuKFes7Myn1p78uYnQXnwNrgzQ48X7NBMSf5oaDL6UUH9efdiQwotF+IX8uPLU4mBabDFzZNoeb/nKEKnDMmXETjw/2x1zT1qMl3JjJbxcOM6ca3sbnOBFK4GYqgQjOR75NczVJWdZ/QsYghiPkTL+dn6nyLs7RbKXovUFu4RmM36h03R+/0LdkPPcYOa6BbAjs43BIHqsqmWDcQYs6VLVamaHQZjdVjUISP9ymK4NncXyfqDF2uRJPzitTNBwgDnkNwxBylBezrE+xIYtbGfZemVvwU6UIlQ5qHAZQgnvW8k3txmMgWg5tPxahSm0/TUUxtT32YO1aZwpjdhZmqAt0xkTQo3jJCxQS45QuTkgjaCTFXp5dZWegY+ftGWKRPTPnAVStTRephTjyBjxrPRIqNlNBOTGfMql1CE0wr/6kH3yrlx6jZbUbBEITODNG7CZ0VWxBVYRPy/EkhlaGhoAbM4HvrVpFh34ezlfK0RiWKLi4ycYAq3B78HO61UlwcZ8npMxPUkTVd4IYDPBQuGPuMEAHEYULHNn6lmKmrp2xKaBqsE2jPJH0VQqidYsH0R0osWZbNhyyxD044Yy5q+QIvLS9v1uBobyxK4hb1jkOKcWxPZX+rOPaYT8NardW3LykDPtO9IxJb/r47NtREgamuq02RzRk=
12 |
13 | before_install:
14 | - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
15 | - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu
16 | $(lsb_release -cs) stable"
17 | - sudo apt-get update
18 | - sudo apt-get -y install docker-ce
19 | - docker --version
20 |
21 | install:
22 | - make install_ci
23 |
24 | script:
25 | - make test_ci
26 | - travis_retry goveralls -coverprofile=cover.out -service=travis-ci || true
27 |
28 | after_success:
29 | - bash ./travis/build-docker-images.sh
30 |
31 | notifications:
32 | slack:
33 | rooms:
34 | secure: Qkh1Ucluku5Q3mimju0VtMLaIr2ugzIYLf/wlyqIO60IZd1zYMnegl0FY7PwrbOAJfTxTADbaP5egPzDbKt71EAQnuu1s5vHzxac1kdvMtEFpUyJVxfJ/EqumUDdVOgb0QGKRbu252Y2HzPDT0de0xFvVuZR15mo1ntTI5D1UyCIzXA2ypO8RN4xS4pEppTMWx19QoDi8L8JuOKA5gpybaKk+aIv81eSLQMEheTln9pFza02ZoOY99b0Gv06CcUAyuXTXjPONM+M8HnlyeNxcdghBt6ovAl59YfpvLruDq30+zYTkUTHeaMfKoT1gyGuukZsU3sv1SeR4NCJ3l11H5wezREp4W56XJpZjert0x2nABNQui/JfRfm0Ofs7LJ/HDYXkIcAd0lyPMjjySq+27LMxpaUTO6EHYDoDbZq6egmjx02zJYhSil/14Y6b2PS1zfa3ZEe0qSHP5fIArfll5NP5bdLWZgzObBaqhiLPQXOoEg4BKkwOGfa0/MlqlxjqV7ObGwD8g8Tj2UBKmzX0j7iBOwo9McLaz2pkCJYLFbdZ6mlyQxbAQbCGr7fVwUMlAUNvZttQdZ6kisj8WDw1UqI3XlYMy0ca3IRclk7S28AtkqJaqv5dooqFCeHMz3CubQNb8TwHqDo5bb5QLX+u6ly1eNY2zokwokic5WOl6k=
35 |
--------------------------------------------------------------------------------
/steps/mockplugins_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | // package
22 | package steps
23 |
24 | import (
25 | "context"
26 | "errors"
27 | "net/http"
28 |
29 | "github.com/northwesternmutual/kanali/metrics"
30 | "github.com/northwesternmutual/kanali/spec"
31 | "github.com/opentracing/opentracing-go"
32 | )
33 |
34 | type fakePanicPlugin struct{}
35 |
36 | func (plugin fakePanicPlugin) OnRequest(ctx context.Context, m *metrics.Metrics, p spec.APIProxy, r *http.Request, span opentracing.Span) error {
37 | panic("intentional")
38 | }
39 |
40 | func (plugin fakePanicPlugin) OnResponse(ctx context.Context, m *metrics.Metrics, p spec.APIProxy, r *http.Request, resp *http.Response, span opentracing.Span) error {
41 | panic("intentional")
42 | }
43 |
44 | type fakeSuccessPlugin struct{}
45 |
46 | func (plugin fakeSuccessPlugin) OnRequest(ctx context.Context, m *metrics.Metrics, p spec.APIProxy, r *http.Request, span opentracing.Span) error {
47 | return nil
48 | }
49 |
50 | func (plugin fakeSuccessPlugin) OnResponse(ctx context.Context, m *metrics.Metrics, p spec.APIProxy, r *http.Request, resp *http.Response, span opentracing.Span) error {
51 | return nil
52 | }
53 |
54 | type fakeErrorPlugin struct{}
55 |
56 | func (plugin fakeErrorPlugin) OnRequest(ctx context.Context, m *metrics.Metrics, p spec.APIProxy, r *http.Request, span opentracing.Span) error {
57 | return errors.New("error")
58 | }
59 |
60 | func (plugin fakeErrorPlugin) OnResponse(ctx context.Context, m *metrics.Metrics, p spec.APIProxy, r *http.Request, resp *http.Response, span opentracing.Span) error {
61 | return errors.New("error")
62 | }
63 |
--------------------------------------------------------------------------------
/helm/charts/influxdb/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Deployment
3 | metadata:
4 | name: {{ template "fullname" . }}
5 | labels:
6 | app: {{ template "fullname" . }}
7 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
8 | release: "{{ .Release.Name }}"
9 | heritage: "{{ .Release.Service }}"
10 | spec:
11 | replicas: 1
12 | template:
13 | metadata:
14 | labels:
15 | app: {{ template "fullname" . }}
16 | spec:
17 | containers:
18 | - name: {{ template "fullname" . }}
19 | image: "{{ .Values.image.repo }}:{{ .Values.image.tag }}"
20 | imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
21 | resources:
22 | {{ toYaml .Values.resources | indent 10 }}
23 | ports:
24 | - name: api
25 | containerPort: {{ .Values.config.http.bind_address }}
26 | {{- if .Values.config.admin.enabled -}}
27 | - name: admin
28 | containerPort: {{ .Values.config.admin.bind_address }}
29 | {{- end }}
30 | {{- if .Values.config.graphite.enabled -}}
31 | - name: graphite
32 | containerPort: {{ .Values.config.graphite.bind_address }}
33 | {{- end }}
34 | {{- if .Values.config.collectd.enabled -}}
35 | - name: collectd
36 | containerPort: {{ .Values.config.collectd.bind_address }}
37 | {{- end }}
38 | {{- if .Values.config.udp.enabled -}}
39 | - name: udp
40 | containerPort: {{ .Values.config.udp.bind_address }}
41 | {{- end }}
42 | {{- if .Values.config.opentsdb.enabled -}}
43 | - name: opentsdb
44 | containerPort: {{ .Values.config.opentsdb.bind_address }}
45 | {{- end }}
46 | livenessProbe:
47 | httpGet:
48 | path: /ping
49 | port: api
50 | initialDelaySeconds: 30
51 | timeoutSeconds: 5
52 | readinessProbe:
53 | httpGet:
54 | path: /ping
55 | port: api
56 | initialDelaySeconds: 5
57 | timeoutSeconds: 1
58 | volumeMounts:
59 | - name: data
60 | mountPath: {{ .Values.config.storage_directory }}
61 | - name: config
62 | mountPath: /etc/influxdb
63 | volumes:
64 | - name: data
65 | {{- if .Values.persistence.enabled }}
66 | {{- if not (empty .Values.persistence.name) }}
67 | persistentVolumeClaim:
68 | claimName: {{ .Values.persistence.name }}
69 | {{- else }}
70 | persistentVolumeClaim:
71 | claimName: {{ template "fullname" . }}
72 | {{- end }}
73 | {{- else }}
74 | emptyDir: {}
75 | {{- end }}
76 | - name: config
77 | configMap:
78 | name: {{ template "fullname" . }}
79 |
--------------------------------------------------------------------------------
/flow/flow_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package flow
22 |
23 | import (
24 | "context"
25 | "errors"
26 | "net/http"
27 | "testing"
28 |
29 | "github.com/northwesternmutual/kanali/metrics"
30 | "github.com/northwesternmutual/kanali/spec"
31 | opentracing "github.com/opentracing/opentracing-go"
32 | "github.com/stretchr/testify/assert"
33 | )
34 |
35 | type mockStep struct{}
36 |
37 | func (s mockStep) GetName() string {
38 | return "mock step"
39 | }
40 |
41 | func (s mockStep) Do(ctx context.Context, proxy *spec.APIProxy, m *metrics.Metrics, w http.ResponseWriter, r *http.Request, resp *http.Response, trace opentracing.Span) error {
42 | return nil
43 | }
44 |
45 | type mockErrorStep struct{}
46 |
47 | func (s mockErrorStep) GetName() string {
48 | return "mock error step"
49 | }
50 |
51 | func (s mockErrorStep) Do(ctx context.Context, proxy *spec.APIProxy, m *metrics.Metrics, w http.ResponseWriter, r *http.Request, resp *http.Response, trace opentracing.Span) error {
52 | return errors.New("forced error")
53 | }
54 |
55 | func TestAdd(t *testing.T) {
56 | f := &Flow{}
57 | f.Add(mockStep{}, mockStep{})
58 | f.Add(mockStep{})
59 | assert.Equal(t, len(*f), 3)
60 | for _, step := range *f {
61 | assert.Equal(t, step.GetName(), "mock step")
62 | assert.Nil(t, step.Do(context.Background(), nil, nil, nil, nil, nil, opentracing.StartSpan("test span")))
63 | }
64 | }
65 |
66 | func TestPlay(t *testing.T) {
67 | f := &Flow{}
68 | f.Add(mockStep{})
69 | assert.Nil(t, f.Play(context.Background(), nil, nil, nil, nil, nil, opentracing.StartSpan("test span")))
70 | f.Add(mockErrorStep{})
71 | assert.Error(t, f.Play(context.Background(), nil, nil, nil, nil, nil, opentracing.StartSpan("test span")))
72 | }
73 |
--------------------------------------------------------------------------------
/docs/apikeybinding.md:
--------------------------------------------------------------------------------
1 | # ApiKeyBinding
2 |
3 | | Field | Required | Description |
4 | | ----- | -------- | ----------- |
5 | | apiVersion
*string* | `true` | APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. |
6 | | kind
*string* | `true` | Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. |
7 | | metadata
*[ObjectMeta](https://kubernetes.io/docs/api-reference/v1.6/#objectmeta-v1-meta)* | `true` | Standard object's metadata. |
8 | | spec
*[ApiKeyBindingSpec](#apikeybindingspec)* | `true` | Defines an ApiKeyBinding |
9 |
10 | # ApiKeyBindingSpec
11 |
12 | | Field | Required | Description |
13 | | ----- | -------- | ----------- |
14 | | proxy
*string* | `true` | The name of the `ApiProxy` that this binding applies to. |
15 | | keys
*[Key](#key) array* | `true` | List of `ApiKey`s that belong to this binding. |
16 |
17 | # Key
18 |
19 | | Field | Required | Description |
20 | | ----- | -------- | ----------- |
21 | | name
*string* | `true` | Name of the `ApiKey` |
22 | | quota
*integer* | `false` | Number of requests that this `ApiKey` is granted. |
23 | | rate
*[Rate](#rate)* | `false` | The rate limiting policy for this `ApiKey` |
24 | | defaultRule
*[Rule](#rule)* | `false` | The default rule this `ApiKey` has for fine grained access. Default is `false` |
25 | | subpaths
*[Path](#path) array* | `false` | Defines find grained authorization based on subpath. If not defined, falls back to the `defaultRule` for any subpath |
26 |
27 | # Rate
28 |
29 | | Field | Required | Description |
30 | | ----- | -------- | ----------- |
31 | | amount
*integer* | `true` | Scalar value for the defined `unit` |
32 | | unit
*string* | `true` | Unit of rate limit. Valid values are `second`, `minute`, `hour` |
33 |
34 | # Rule
35 |
36 | | Field | Required | Description |
37 | | ----- | -------- | ----------- |
38 | | global
*boolean* | If undefined, *granular* must be defined. | If true, access to all HTTP methods is granted. |
39 | | granular
*[Granular](#granular)* | If undefined, *global* must be defined. | Defines granular rules |
40 |
41 | # Granular
42 |
43 | | Field | Required | Description |
44 | | ----- | -------- | ----------- |
45 | | verbs
*string array* | `true` | List of http verbs that this `ApiKeyBinding` has access to. |
46 |
47 | # Path
48 |
49 | | Field | Required | Description |
50 | | ----- | -------- | ----------- |
51 | | path
*string* | `true` | The subpath |
52 | | rule
*[Rule](#rule)* | `true` | The rules defined for this subpath |
--------------------------------------------------------------------------------
/controller/controller.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package controller
22 |
23 | import (
24 | "errors"
25 |
26 | "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
27 | "k8s.io/kubernetes/pkg/client/restclient"
28 | "k8s.io/kubernetes/pkg/kubectl/cmd/util"
29 | )
30 |
31 | // Controller is an exported struct
32 | // that holds all of the information
33 | // we will need throughout our program
34 | // to talk to the kubernetes api server.
35 | // this includes a rest client and well
36 | // a clientset that gives us access to the libs
37 | type Controller struct {
38 | RestClient *restclient.RESTClient
39 | ClientSet internalclientset.Interface
40 | MasterHost string
41 | }
42 |
43 | // New creates a new kubernetes controller
44 | // the controller is secure and uses the
45 | // cluster's kubeconfig file to construct
46 | // permissions
47 | func New() (*Controller, error) {
48 |
49 | f := util.NewFactory(nil)
50 |
51 | // ClientSet gives you back an internal, generated clientset
52 | clientSet, err := f.ClientSet()
53 | if err != nil {
54 | return nil, err
55 | }
56 |
57 | // Returns a RESTClient for accessing Kubernetes resources or an error.
58 | restClient, err := f.RESTClient()
59 | if err != nil {
60 | return nil, err
61 | }
62 |
63 | controller := &Controller{
64 | RestClient: restClient,
65 | ClientSet: clientSet,
66 | }
67 |
68 | // Returns a client.Config for accessing the Kubernetes server.
69 | k8sConfig, err := f.ClientConfig()
70 | if err != nil {
71 | return nil, err
72 | } else if k8sConfig == nil {
73 | return nil, errors.New("received nil k8sConfig, please check if k8s cluster is available")
74 | } else {
75 | controller.MasterHost = k8sConfig.Host
76 | }
77 |
78 | // return our newly created controller
79 | return controller, nil
80 | }
81 |
--------------------------------------------------------------------------------
/handlers/incoming.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package handlers
22 |
23 | import (
24 | "context"
25 | "net/http"
26 |
27 | "github.com/northwesternmutual/kanali/config"
28 | "github.com/northwesternmutual/kanali/flow"
29 | "github.com/northwesternmutual/kanali/metrics"
30 | "github.com/northwesternmutual/kanali/spec"
31 | "github.com/northwesternmutual/kanali/steps"
32 | "github.com/northwesternmutual/kanali/utils"
33 | "github.com/opentracing/opentracing-go"
34 | "github.com/spf13/viper"
35 | )
36 |
37 | // IncomingRequest orchestrates the logic that occurs for every incoming request
38 | func IncomingRequest(ctx context.Context, proxy *spec.APIProxy, m *metrics.Metrics, w http.ResponseWriter, r *http.Request, trace opentracing.Span) error {
39 |
40 | // this is a handler to our future proxy pass response
41 | // maybe there's a better way to do this... seems misplaced
42 | futureResponse := &http.Response{}
43 |
44 | f := &flow.Flow{}
45 |
46 | f.Add(
47 | steps.ValidateProxyStep{},
48 | steps.PluginsOnRequestStep{},
49 | )
50 | if viper.GetBool(config.FlagProxyEnableMockResponses.GetLong()) && mockIsDefined(utils.ComputeURLPath(r.URL)) {
51 | f.Add(steps.MockServiceStep{})
52 | } else {
53 | f.Add(steps.ProxyPassStep{})
54 | }
55 |
56 | f.Add(
57 | steps.PluginsOnResponseStep{},
58 | steps.WriteResponseStep{},
59 | )
60 |
61 | err := f.Play(ctx, proxy, m, w, r, futureResponse, trace)
62 |
63 | return err
64 |
65 | }
66 |
67 | func mockIsDefined(path string) bool {
68 |
69 | untypedProxy, err := spec.ProxyStore.Get(path)
70 | if err != nil || untypedProxy == nil {
71 | return false
72 | }
73 |
74 | proxy, _ := untypedProxy.(spec.APIProxy)
75 |
76 | if proxy.Spec.Mock != nil {
77 | return proxy.Spec.Mock.ConfigMapName != ""
78 | }
79 |
80 | return false
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/steps/pluginsonrequest.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package steps
22 |
23 | import (
24 | "context"
25 | "errors"
26 | "fmt"
27 | "net/http"
28 |
29 | "github.com/Sirupsen/logrus"
30 | "github.com/northwesternmutual/kanali/metrics"
31 | "github.com/northwesternmutual/kanali/plugins"
32 | "github.com/northwesternmutual/kanali/spec"
33 | "github.com/opentracing/opentracing-go"
34 | )
35 |
36 | // PluginsOnRequestStep is factory that defines a step responsible for
37 | // executing the on request lifecycle hook for all the defined plugins
38 | type PluginsOnRequestStep struct{}
39 |
40 | // GetName retruns the name of the PluginsOnRequestStep step
41 | func (step PluginsOnRequestStep) GetName() string {
42 | return "Plugin OnRequest"
43 | }
44 |
45 | // Do executes the logic of the PluginsOnRequestStep step
46 | func (step PluginsOnRequestStep) Do(ctx context.Context, proxy *spec.APIProxy, m *metrics.Metrics, w http.ResponseWriter, r *http.Request, resp *http.Response, trace opentracing.Span) error {
47 |
48 | for _, plugin := range proxy.Spec.Plugins {
49 | p, err := plugins.GetPlugin(plugin)
50 | if err != nil {
51 | return err
52 | }
53 | if err := doOnRequest(ctx, m, plugin.Name, *proxy, r, trace, *p); err != nil {
54 | return err
55 | }
56 | }
57 | return nil
58 | }
59 |
60 | func doOnRequest(ctx context.Context, m *metrics.Metrics, name string, proxy spec.APIProxy, req *http.Request, span opentracing.Span, p plugins.Plugin) (e error) {
61 | defer func() {
62 | if r := recover(); r != nil {
63 | logrus.Errorf("OnRequest paniced: %v", r)
64 | e = errors.New("OnRequest paniced")
65 | }
66 | }()
67 |
68 | sp := opentracing.StartSpan(fmt.Sprintf("PLUGIN: ON_REQUEST: %s", name), opentracing.ChildOf(span.Context()))
69 | defer sp.Finish()
70 |
71 | return p.OnRequest(ctx, m, proxy, req, sp)
72 | }
73 |
--------------------------------------------------------------------------------
/docs/apikey.md:
--------------------------------------------------------------------------------
1 | The `ApiKey` spec represents an rsa encrypted API key.
2 |
3 | *NOTE:* while you can generate this spec yourself, it is recommended that you generate an `ApiKey` spec using the [`kanalictl`](https://github.com/northwesternmutual/kanalictl) tool. See example below for usage details.
4 |
5 | Here are some useful commands to generate an rsa key pair:
6 |
7 | ```sh
8 | # generate private key - using 4096 bit long modulus
9 | $ openssl genrsa -out private.pem 4096
10 | # calculate public key
11 | $ openssl rsa -in private.pem -pubout -out public.pem
12 | # use the kanalictl tool to generate ApiKey spec
13 | $ kanalictl generate apikey -k public.pem -o apikey.yml -n my-test-api-key
14 | Here is your api key (you will only see this once): ksAR0xqSKjh9UGSBvhP2IxDDC9Ckou0S
15 | Corresponding Kubernetes config written to apikey.yml
16 | $ cat apikey.yml
17 | apiVersion: kanali.io/v1
18 | kind: ApiKey
19 | metadata:
20 | creationTimestamp: null
21 | name: my-test-api-key
22 | namespace: default
23 | spec:
24 | data: 2778ac7127f97212dbc27cb9a8b7fb5a51f49bcefc8a23eb26fd9e3b8673faa9dc3e98597dcc62d1dcc01a5c054b28268c30b206c5e5296e058fded2458905382b15ba30eef7596ae46248b0958442e03e38ec1097a96a9fe6420fb671a06ed7782deadd0bb35f9ef1debb5693a34d20108647364834939a12f8a9959864c52f3df4d0cebbae60a27facf0b75bbae5e91077c2e013179810a7cdca77bca6c8d1a48acc3e6b3af72119f4886cc9c483063b5e42f660095d4e3f69c35a6511c9ecbe59c5893eb176c208c6d00c0eda2315416e856fd264ab886ee18527f6cc0c5311953a79ad2c1695780d322bb5d6cacac61d808bfbca531614084d7caac6a11f310127adb319ba53fcd91835d0bcf318f85242563ec555e2d3c0cefbff31585ec6a631f893a5dd57725002b4e9ac5d68ac4ba849d9f3314968ea63d1f8520060cf800fcded379a1353b6f018f431e5206018b9a5c81d52c13069a7621ca6b02de302ad830279f9963c957ef73a6e170f17883eefac405bae03796fcbb02e07b7ff1b691bd320a8a72a35203898664206ac386f730787160f94739459d11ab3b0019648414c6a4b9bcc7121a17a42aa8bd2e3e7a64234f9e78503833dd208c8a4a948b51491a0a4fec15f17a213c0ae4a5d87d002b8047f9aa235c9f32b052301e499d64d7650a1cb3a201f7342028d6b5f50e0f4ab7d3d3b3c4bbb410aa81e04
25 | ```
26 |
27 | # ApiKey
28 |
29 | | Field | Required | Description |
30 | | ----- | -------- | ----------- |
31 | | apiVersion
*string* | `true` | APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. |
32 | | kind
*string* | `true` | Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. |
33 | | metadata
*[ObjectMeta](https://kubernetes.io/docs/api-reference/v1.6/#objectmeta-v1-meta)* | `true` | Standard object's metadata. |
34 | | spec
*[ApiKeySpec](#apikeyspec)* | `true` | Defines an ApiKey |
35 |
36 | # ApiKeySpec
37 |
38 | | Field | Required | Description |
39 | | ----- | -------- | ----------- |
40 | | data
*string* | `true` | encrypted api key |
--------------------------------------------------------------------------------
/logo/name_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
32 |
--------------------------------------------------------------------------------
/config/flag.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package config
22 |
23 | import (
24 | "time"
25 |
26 | "github.com/spf13/cobra"
27 | "github.com/spf13/viper"
28 | )
29 |
30 | // Flag is a simplified representation of a configuration item.
31 | type Flag struct {
32 | Long string
33 | Short string
34 | Value interface{}
35 | Usage string
36 | }
37 |
38 | type flags []Flag
39 |
40 | // Flags is the aggregate set of flags that Kanali has available to configure.
41 | var Flags = &flags{}
42 |
43 | func (f *flags) Add(a ...Flag) {
44 | for _, curr := range a {
45 | *f = append(*f, curr)
46 | }
47 | }
48 |
49 | // GetLong returns the name of the flag
50 | func (f Flag) GetLong() string {
51 | return f.Long
52 | }
53 |
54 | // GetShort returns the Short name of the flag
55 | func (f Flag) GetShort() string {
56 | return f.Short
57 | }
58 |
59 | // GetUsage returns the flag's description.
60 | func (f Flag) GetUsage() string {
61 | return f.Usage
62 | }
63 |
64 | func (f flags) AddAll(cmd *cobra.Command) error {
65 | for _, currFlag := range f {
66 | switch v := currFlag.Value.(type) {
67 | case int:
68 | cmd.Flags().IntP(currFlag.Long, currFlag.Short, v, currFlag.Usage)
69 | case bool:
70 | cmd.Flags().BoolP(currFlag.Long, currFlag.Short, v, currFlag.Usage)
71 | case string:
72 | cmd.Flags().StringP(currFlag.Long, currFlag.Short, v, currFlag.Usage)
73 | case time.Duration:
74 | cmd.Flags().DurationP(currFlag.Long, currFlag.Short, v, currFlag.Usage)
75 | case []string:
76 | cmd.Flags().StringSliceP(currFlag.Long, currFlag.Short, v, currFlag.Usage)
77 | default:
78 | viper.SetDefault(currFlag.Long, currFlag.Value)
79 | continue
80 | }
81 | viper.SetDefault(currFlag.Long, currFlag.Value)
82 | if err := viper.BindPFlag(currFlag.Long, cmd.Flags().Lookup(currFlag.Long)); err != nil {
83 | return err
84 | }
85 | }
86 |
87 | return nil
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/steps/pluginsonresponse.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package steps
22 |
23 | import (
24 | "context"
25 | "errors"
26 | "fmt"
27 | "net/http"
28 |
29 | "github.com/Sirupsen/logrus"
30 | "github.com/northwesternmutual/kanali/metrics"
31 | "github.com/northwesternmutual/kanali/plugins"
32 | "github.com/northwesternmutual/kanali/spec"
33 | "github.com/opentracing/opentracing-go"
34 | )
35 |
36 | // PluginsOnResponseStep is factory that defines a step responsible for
37 | // executing the on response lifecycle hook for all the defined plugins
38 | type PluginsOnResponseStep struct{}
39 |
40 | // GetName retruns the name of the PluginsOnResponseStep step
41 | func (step PluginsOnResponseStep) GetName() string {
42 | return "Plugin OnResponse"
43 | }
44 |
45 | // Do executes the logic of the PluginsOnResponseStep step
46 | func (step PluginsOnResponseStep) Do(ctx context.Context, proxy *spec.APIProxy, m *metrics.Metrics, w http.ResponseWriter, r *http.Request, resp *http.Response, trace opentracing.Span) error {
47 |
48 | for _, plugin := range proxy.Spec.Plugins {
49 | p, err := plugins.GetPlugin(plugin)
50 | if err != nil {
51 | return err
52 | }
53 | if err := doOnResponse(ctx, m, plugin.Name, *proxy, r, resp, trace, *p); err != nil {
54 | return err
55 | }
56 | }
57 |
58 | return nil
59 | }
60 |
61 | func doOnResponse(ctx context.Context, m *metrics.Metrics, name string, proxy spec.APIProxy, req *http.Request, resp *http.Response, span opentracing.Span, p plugins.Plugin) (e error) {
62 | defer func() {
63 | if r := recover(); r != nil {
64 | logrus.Errorf("OnResponse paniced: %v", r)
65 | e = errors.New("OnResponse paniced")
66 | }
67 | }()
68 |
69 | sp := opentracing.StartSpan(fmt.Sprintf("PLUGIN: ON_RESPONSE: %s", name), opentracing.ChildOf(span.Context()))
70 | defer sp.Finish()
71 |
72 | return p.OnResponse(ctx, m, proxy, req, resp, sp)
73 | }
74 |
--------------------------------------------------------------------------------
/config/flag_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package config
22 |
23 | import (
24 | "testing"
25 | "time"
26 |
27 | "github.com/spf13/cobra"
28 | "github.com/spf13/viper"
29 | "github.com/stretchr/testify/assert"
30 | )
31 |
32 | func TestGetters(t *testing.T) {
33 | f := Flag{
34 | Long: "test",
35 | Short: "t",
36 | Value: "hello world",
37 | Usage: "for testing",
38 | }
39 | assert.Equal(t, f.GetLong(), "test")
40 | assert.Equal(t, f.GetShort(), "t")
41 | assert.Equal(t, f.GetUsage(), "for testing")
42 | }
43 |
44 | func TestAddAll(t *testing.T) {
45 | cmd := &cobra.Command{}
46 | d, _ := time.ParseDuration("0h0m0s")
47 | f := flags{
48 | Flag{
49 | Long: "int",
50 | Short: "i",
51 | Value: 1,
52 | Usage: "for testing",
53 | },
54 | Flag{
55 | Long: "bool",
56 | Short: "b",
57 | Value: true,
58 | Usage: "for testing",
59 | },
60 | Flag{
61 | Long: "string",
62 | Short: "s",
63 | Value: "hello world",
64 | Usage: "for testing",
65 | },
66 | Flag{
67 | Long: "duration",
68 | Short: "d",
69 | Value: d,
70 | Usage: "for testing",
71 | },
72 | Flag{
73 | Long: "slice",
74 | Short: "p",
75 | Value: []string{"foo"},
76 | Usage: "for testing",
77 | },
78 | }
79 | assert.Nil(t, f.AddAll(cmd))
80 | cobraValOne, _ := cmd.Flags().GetInt("int")
81 | cobraValTwo, _ := cmd.Flags().GetBool("bool")
82 | cobraValThree, _ := cmd.Flags().GetString("string")
83 | cobraValFour, _ := cmd.Flags().GetDuration("duration")
84 | cobraValFive, _ := cmd.Flags().GetStringSlice("slice")
85 | assert.Equal(t, viper.GetString("string"), "hello world")
86 | assert.Equal(t, viper.GetInt("int"), 1)
87 | assert.True(t, viper.GetBool("bool"))
88 | assert.Equal(t, viper.GetDuration("duration"), d)
89 | assert.Equal(t, cobraValOne, 1)
90 | assert.True(t, cobraValTwo)
91 | assert.Equal(t, cobraValThree, "hello world")
92 | assert.Equal(t, cobraValFour, d)
93 | assert.Equal(t, cobraValFive, []string{"foo"})
94 | }
95 |
--------------------------------------------------------------------------------
/logo/name_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
35 |
--------------------------------------------------------------------------------
/tracer/tags.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package tracer
22 |
23 | const (
24 | // Error is the opentracing tag name that represents an error
25 | Error = "error"
26 |
27 | // KanaliProxyName is the opentracing tag name that represents an APIProxy name
28 | KanaliProxyName = "kanali.proxy.name"
29 | // KanaliProxyNamespace is the opentracing tag name that represents an APIProxy namespace
30 | KanaliProxyNamespace = "kanali.proxy.namespace"
31 |
32 | // HTTPRequest is the opentracing tag name that represents the existence on an HTTP request
33 | HTTPRequest = "http.request"
34 |
35 | // HTTPRequestMethod is the opentracing tag name that represents an HTTP request method
36 | HTTPRequestMethod = "http.request.method"
37 | // HTTPRequestBody is the opentracing tag name that represents an HTTP request body
38 | HTTPRequestBody = "http.request.body"
39 | // HTTPRequestHeaders is the opentracing tag name that represents an HTTP request headers
40 | HTTPRequestHeaders = "http.request.headers"
41 |
42 | // HTTPRequestURLPath is the opentracing tag name that represents an HTTP URL path
43 | HTTPRequestURLPath = "http.request.url.path"
44 | // HTTPRequestURLHost is the opentracing tag name that represents an HTTP URL host
45 | HTTPRequestURLHost = "http.request.url.host"
46 | // HTTPRequestURLQuery is the opentracing tag name that represents an HTTP URL query
47 | HTTPRequestURLQuery = "http.request.url.query"
48 |
49 | // HTTPResponse is the opentracing tag name that represents the existence on an HTTP response
50 | HTTPResponse = "http.response"
51 |
52 | // HTTPResponseBody is the opentracing tag name that represents an HTTP response body
53 | HTTPResponseBody = "http.response.body"
54 | // HTTPResponseHeaders is the opentracing tag name that represents an HTTP response headers
55 | HTTPResponseHeaders = "http.response.headers"
56 |
57 | // HTTPResponseStatusCode is the opentracing tag name that represents an HTTP response status code
58 | HTTPResponseStatusCode = "http.response.status.code"
59 | )
60 |
--------------------------------------------------------------------------------
/steps/validateproxy.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package steps
22 |
23 | import (
24 | "context"
25 | "errors"
26 | "net/http"
27 |
28 | "github.com/Sirupsen/logrus"
29 | "github.com/northwesternmutual/kanali/metrics"
30 | "github.com/northwesternmutual/kanali/spec"
31 | "github.com/northwesternmutual/kanali/tracer"
32 | "github.com/northwesternmutual/kanali/utils"
33 | "github.com/opentracing/opentracing-go"
34 | )
35 |
36 | // ValidateProxyStep is factory that defines a step responsible for
37 | // validating that an incoming request matches a proxy that Kanali
38 | // has stored in memory
39 | type ValidateProxyStep struct{}
40 |
41 | // GetName retruns the name of the ValidateProxyStep step
42 | func (step ValidateProxyStep) GetName() string {
43 | return "Validate Proxy"
44 | }
45 |
46 | // Do executes the logic of the ValidateProxyStep step
47 | func (step ValidateProxyStep) Do(ctx context.Context, proxy *spec.APIProxy, m *metrics.Metrics, w http.ResponseWriter, r *http.Request, resp *http.Response, trace opentracing.Span) error {
48 |
49 | untypedProxy, err := spec.ProxyStore.Get(utils.ComputeURLPath(r.URL))
50 | if err != nil || untypedProxy == nil {
51 | if err != nil {
52 | logrus.Error(err.Error())
53 | }
54 |
55 | trace.SetTag(tracer.KanaliProxyName, "unknown")
56 | trace.SetTag(tracer.KanaliProxyNamespace, "unknown")
57 |
58 | m.Add(
59 | metrics.Metric{Name: "proxy_name", Value: "unknown", Index: true},
60 | metrics.Metric{Name: "proxy_namespace", Value: "unknown", Index: true},
61 | )
62 |
63 | return utils.StatusError{Code: http.StatusNotFound, Err: errors.New("proxy not found")}
64 | }
65 |
66 | typedProxy, _ := untypedProxy.(spec.APIProxy)
67 |
68 | *proxy = typedProxy
69 |
70 | trace.SetTag(tracer.KanaliProxyName, proxy.ObjectMeta.Name)
71 | trace.SetTag(tracer.KanaliProxyNamespace, proxy.ObjectMeta.Namespace)
72 |
73 | m.Add(
74 | metrics.Metric{Name: "proxy_name", Value: proxy.ObjectMeta.Name, Index: true},
75 | metrics.Metric{Name: "proxy_namespace", Value: proxy.ObjectMeta.Namespace, Index: true},
76 | )
77 |
78 | return nil
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/helm/charts/jaeger/templates/kubernetes.yml:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2017 The Jaeger Authors
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | # in compliance with the License. You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software distributed under the License
10 | # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | # or implied. See the License for the specific language governing permissions and limitations under
12 | # the License.
13 | #
14 |
15 | # This template uses Jaeger with in-memory storage with limited functionality
16 | # Do not use this in production environment!
17 | #
18 | # kubectl create -f jaeger-all-in-one-template.yml
19 | # kubectl delete pod,service,deployment -l jaeger-infra
20 | apiVersion: v1
21 | kind: List
22 | items:
23 | - apiVersion: extensions/v1beta1
24 | kind: Deployment
25 | metadata:
26 | name: jaeger-all-in-one
27 | namespace: kube-system
28 | spec:
29 | replicas: 1
30 | selector:
31 | name: jaeger-all-in-one
32 | strategy:
33 | type: Recreate
34 | selector:
35 | matchLabels:
36 | name: jaeger-all-in-one
37 | template:
38 | metadata:
39 | labels:
40 | name: jaeger-all-in-one
41 | jaeger-infra: all-in-one
42 | spec:
43 | containers:
44 | - name: jaeger-all-in-one
45 | image: jaegertracing/all-in-one
46 | ports:
47 | - containerPort: 5775
48 | protocol: UDP
49 | - containerPort: 6831
50 | protocol: UDP
51 | - containerPort: 6832
52 | protocol: UDP
53 | - containerPort: 16686
54 | protocol: TCP
55 | resources: {}
56 | imagePullPolicy: Always
57 | readinessProbe:
58 | httpGet:
59 | path: "/"
60 | port: 16686
61 | initialDelaySeconds: 5
62 | securityContext: {}
63 | - apiVersion: v1
64 | kind: Service
65 | metadata:
66 | name: jaeger-all-in-one
67 | namespace: kube-system
68 | labels:
69 | jaeger-infra: all-in-one
70 | spec:
71 | ports:
72 | - name: query-http
73 | port: 80
74 | protocol: TCP
75 | targetPort: 16686
76 | selector:
77 | name: jaeger-all-in-one
78 | type: NodePort
79 | - apiVersion: v1
80 | kind: Service
81 | metadata:
82 | name: jaeger-agent
83 | namespace: kube-system
84 | labels:
85 | jaeger-infra: all-in-one
86 | spec:
87 | ports:
88 | - name: agent-zipkin-thrift
89 | port: 5775
90 | protocol: UDP
91 | targetPort: 5775
92 | - name: agent-compact
93 | port: 6831
94 | protocol: UDP
95 | targetPort: 6831
96 | - name: agent-binary
97 | port: 6832
98 | protocol: UDP
99 | targetPort: 6832
100 | selector:
101 | name: jaeger-all-in-one
102 | type: ClusterIP
--------------------------------------------------------------------------------
/helm/charts/influxdb/README.md:
--------------------------------------------------------------------------------
1 | # InfluxDB
2 |
3 | ## An Open-Source Time Series Database
4 |
5 | [InfluxDB](https://github.com/influxdata/influxdb) is an open source time series database built by the folks over at [InfluxData](https://influxdata.com) with no external dependencies. It's useful for recording metrics, events, and performing analytics.
6 |
7 | ## QuickStart
8 |
9 | ```bash
10 | $ helm install stable/influxdb --name foo --namespace bar
11 | ```
12 |
13 | ## Introduction
14 |
15 | This chart bootstraps an InfluxDB deployment and service on a Kubernetes cluster using the Helm Package manager.
16 |
17 | ## Prerequisites
18 |
19 | - Kubernetes 1.4+
20 | - PV provisioner support in the underlying infrastructure (optional)
21 |
22 | ## Installing the Chart
23 |
24 | To install the chart with the release name `my-release`:
25 |
26 | ```bash
27 | $ helm install --name my-release stable/influxdb
28 | ```
29 |
30 | The command deploys InfluxDB on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation.
31 |
32 | > **Tip**: List all releases using `helm list`
33 |
34 | ## Uninstalling the Chart
35 |
36 | To uninstall/delete the `my-release` deployment:
37 |
38 | ```bash
39 | $ helm delete my-release --purge
40 | ```
41 |
42 | The command removes all the Kubernetes components associated with the chart and deletes the release.
43 |
44 | ## Configuration
45 |
46 | The default configuration values for this chart are listed in `values.yaml`.
47 |
48 | The [full image documentation](https://hub.docker.com/_/influxdb/) contains more information about running InfluxDB in docker.
49 |
50 | Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
51 |
52 | ```bash
53 | $ helm install --name my-release \
54 | --set persistence.enabled=true,persistence.size=200Gi \
55 | stable/influxdb
56 | ```
57 |
58 | The above command enables persistence and changes the size of the requested data volume to 200GB.
59 |
60 | Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example,
61 |
62 | ```bash
63 | $ helm install --name my-release -f values.yaml stable/influxdb
64 | ```
65 |
66 | > **Tip**: You can use the default [values.yaml](values.yaml)
67 |
68 | ## Persistence
69 |
70 | The [InfluxDB](https://hub.docker.com/_/influxdb/) image stores data in the `/var/lib/influxdb` directory in the container.
71 |
72 | The chart mounts a [Persistent Volume](kubernetes.io/docs/user-guide/persistent-volumes/) volume at this location. The volume is created using dynamic volume provisioning.
73 |
74 | ## Starting with authentication
75 |
76 | In `values.yaml` change `.Values.config.http.auth_enabled` to `true`.
77 |
78 | Influxdb requires also a user to be set in order for authentication to be enforced. See more details [here](https://docs.influxdata.com/influxdb/v1.2/query_language/authentication_and_authorization/#set-up-authentication).
79 |
80 | To handle this setup on startup, a job can be enabled in `values.yaml` by setting `.Values.setDefaultUser.enabled` to `true`.
81 |
82 | Make sure to uncomment or configure the job settings after enabling it. If a password is not set, a random password will be generated.
83 |
--------------------------------------------------------------------------------
/plugins/plugin.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package plugins
22 |
23 | import (
24 | "context"
25 | "fmt"
26 | "net/http"
27 | pluginPkg "plugin"
28 |
29 | "github.com/northwesternmutual/kanali/config"
30 | "github.com/northwesternmutual/kanali/metrics"
31 | "github.com/northwesternmutual/kanali/spec"
32 | "github.com/northwesternmutual/kanali/utils"
33 | "github.com/opentracing/opentracing-go"
34 | "github.com/spf13/viper"
35 | )
36 |
37 | const pluginSymbolName = "Plugin"
38 |
39 | // Plugin is an interface that is used for every Plugin used by Kanali.
40 | // If external plugins are developed, they also must conform to this interface.
41 | type Plugin interface {
42 | OnRequest(ctx context.Context, m *metrics.Metrics, proxy spec.APIProxy, req *http.Request, span opentracing.Span) error
43 | OnResponse(ctx context.Context, m *metrics.Metrics, proxy spec.APIProxy, req *http.Request, resp *http.Response, span opentracing.Span) error
44 | }
45 |
46 | // GetPlugin will use the Go plugin package and extract
47 | // the plugin
48 | func GetPlugin(plugin spec.Plugin) (*Plugin, error) {
49 | path, err := utils.GetAbsPath(viper.GetString(config.FlagPluginsLocation.GetLong()))
50 | if err != nil {
51 | return nil, utils.StatusError{Code: http.StatusInternalServerError, Err: fmt.Errorf("file path %s could not be found", viper.GetString(config.FlagPluginsLocation.GetLong()))}
52 | }
53 |
54 | plug, err := pluginPkg.Open(fmt.Sprintf("%s/%s.so",
55 | path,
56 | plugin.GetFileName(),
57 | ))
58 | if err != nil {
59 | return nil, utils.StatusError{
60 | Code: http.StatusInternalServerError,
61 | Err: fmt.Errorf("could not open plugin %s: %s", plugin.Name, err.Error()),
62 | }
63 | }
64 |
65 | symPlug, err := plug.Lookup(pluginSymbolName)
66 | if err != nil {
67 | return nil, utils.StatusError{
68 | Code: http.StatusInternalServerError,
69 | Err: err,
70 | }
71 | }
72 |
73 | var p Plugin
74 | p, ok := symPlug.(Plugin)
75 | if !ok {
76 | return nil, utils.StatusError{
77 | Code: http.StatusInternalServerError,
78 | Err: fmt.Errorf("plugin %s must implement the Plugin interface", plugin.Name),
79 | }
80 | }
81 |
82 | return &p, nil
83 | }
84 |
--------------------------------------------------------------------------------
/config/analytics.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package config
22 |
23 | func init() {
24 | Flags.Add(
25 | FlagAnalyticsInfluxAddr,
26 | FlagAnalyticsInfluxDb,
27 | FlagAnalyticsInfluxUsername,
28 | FlagAnalyticsInfluxPassword,
29 | FlagAnalyticsInfluxBufferSize,
30 | FlagAnalyticsInfluxMeasurement,
31 | )
32 | }
33 |
34 | var (
35 | // FlagAnalyticsInfluxAddr specifies the Influxdb address. Address should be of the form 'http://host:port' or 'http://[ipv6-host%zone]:port'
36 | FlagAnalyticsInfluxAddr = Flag{
37 | Long: "analytics.influx_addr",
38 | Short: "",
39 | Value: "http://monitoring-influxdb.kube-system.svc.cluster.local:8086",
40 | Usage: "InfluxDB address. Address should be of the form 'http://host:port' or 'http://[ipv6-host%zone]:port'.",
41 | }
42 | // FlagAnalyticsInfluxDb specifies the name of the InfluxDB database
43 | FlagAnalyticsInfluxDb = Flag{
44 | Long: "analytics.influx_db",
45 | Short: "",
46 | Value: "k8s",
47 | Usage: "InfluxDB database name",
48 | }
49 | // FlagAnalyticsInfluxUsername specifies the InfluxDB username
50 | FlagAnalyticsInfluxUsername = Flag{
51 | Long: "analytics.influx_username",
52 | Short: "",
53 | Value: "",
54 | Usage: "InfluxDB username",
55 | }
56 | // FlagAnalyticsInfluxPassword specifies the InfluxDB password
57 | FlagAnalyticsInfluxPassword = Flag{
58 | Long: "analytics.influx_password",
59 | Short: "",
60 | Value: "",
61 | Usage: "InfluxDB password",
62 | }
63 | // FlagAnalyticsInfluxBufferSize specifies the InfluxDB buffer size.
64 | // Request metrics will be written to InfluxDB when this buffer is full.
65 | FlagAnalyticsInfluxBufferSize = Flag{
66 | Long: "analytics.influx_buffer_size",
67 | Short: "",
68 | Value: 10,
69 | Usage: "InfluxDB buffer size. Request metrics will be written to InfluxDB when this buffer is full.",
70 | }
71 | // FlagAnalyticsInfluxMeasurement specifies the InfluxDB measurement to be used for Kanali request metrics.
72 | FlagAnalyticsInfluxMeasurement = Flag{
73 | Long: "analytics.influx_measurement",
74 | Short: "",
75 | Value: "request_details",
76 | Usage: " InfluxDB measurement to be used for Kanali request metrics.",
77 | }
78 | )
79 |
--------------------------------------------------------------------------------
/controller/tpr.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package controller
22 |
23 | import (
24 | "fmt"
25 | "net/http"
26 |
27 | "github.com/Sirupsen/logrus"
28 | "k8s.io/kubernetes/pkg/api"
29 | "k8s.io/kubernetes/pkg/api/errors"
30 | "k8s.io/kubernetes/pkg/api/unversioned"
31 | "k8s.io/kubernetes/pkg/apis/extensions"
32 | )
33 |
34 | // tpr is an internal struct that stores
35 | // information about a Kubernetes ThirdPartyResource
36 | type tpr struct {
37 | Name string
38 | Version string
39 | Description string
40 | }
41 |
42 | // CreateTPRs will create the two tprs that kanali uses
43 | // ApiProxy an ApiKey
44 | func (c *Controller) CreateTPRs() error {
45 |
46 | logrus.Debug("creating TPRs")
47 |
48 | if err := c.doCreateTPRs(&tpr{
49 | Name: "api-proxy.kanali.io",
50 | Version: "v1",
51 | Description: "api proxy TPR",
52 | }, &tpr{
53 | Name: "api-key.kanali.io",
54 | Version: "v1",
55 | Description: "api key TPR",
56 | }, &tpr{
57 | Name: "api-key-binding.kanali.io",
58 | Version: "v1",
59 | Description: "api key binding TPR",
60 | }); err != nil {
61 | if !isKubernetesResourceAlreadyExistError(err) {
62 | return fmt.Errorf("Fail to create TPR: %v", err)
63 | }
64 | }
65 |
66 | return nil
67 |
68 | }
69 |
70 | // doCreateTPRs is a helper function that takes a
71 | // list of tprs and adds each of them to our cluster
72 | func (c *Controller) doCreateTPRs(tprs ...*tpr) error {
73 |
74 | for _, tpr := range tprs {
75 | if _, err := c.ClientSet.Extensions().ThirdPartyResources().Create(&extensions.ThirdPartyResource{
76 | ObjectMeta: api.ObjectMeta{
77 | Name: tpr.Name,
78 | },
79 | Versions: []extensions.APIVersion{
80 | {
81 | Name: tpr.Version,
82 | },
83 | },
84 | Description: tpr.Description,
85 | }); err != nil {
86 | return err
87 | }
88 |
89 | }
90 |
91 | return nil
92 |
93 | }
94 |
95 | func isKubernetesResourceAlreadyExistError(err error) bool {
96 | se, ok := err.(*errors.StatusError)
97 | if !ok {
98 | return false
99 | } else if se.Status().Code == http.StatusConflict && se.Status().Reason == unversioned.StatusReasonAlreadyExists {
100 | return true
101 | } else {
102 | return false
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/controller/tpr_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package controller
22 |
23 | import (
24 | "errors"
25 | "net/http"
26 | "testing"
27 |
28 | "github.com/stretchr/testify/assert"
29 | "k8s.io/kubernetes/pkg/api"
30 | e "k8s.io/kubernetes/pkg/api/errors"
31 | "k8s.io/kubernetes/pkg/api/unversioned"
32 | "k8s.io/kubernetes/pkg/apis/extensions"
33 | "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
34 | )
35 |
36 | func TestCreateTPRs(t *testing.T) {
37 | ctlr := Controller{
38 | RestClient: nil,
39 | ClientSet: fake.NewSimpleClientset(),
40 | MasterHost: "foo.bar.com",
41 | }
42 |
43 | err := ctlr.CreateTPRs()
44 | assert.Nil(t, err)
45 | err = ctlr.CreateTPRs()
46 | assert.Nil(t, err)
47 | resource, _ := ctlr.ClientSet.Extensions().ThirdPartyResources().Get("api-proxy.kanali.io")
48 | assert.Equal(t, resource, &extensions.ThirdPartyResource{
49 | ObjectMeta: api.ObjectMeta{
50 | Name: "api-proxy.kanali.io",
51 | },
52 | Versions: []extensions.APIVersion{
53 | {
54 | Name: "v1",
55 | },
56 | },
57 | Description: "api proxy TPR",
58 | })
59 | resource, _ = ctlr.ClientSet.Extensions().ThirdPartyResources().Get("api-key.kanali.io")
60 | assert.Equal(t, resource, &extensions.ThirdPartyResource{
61 | ObjectMeta: api.ObjectMeta{
62 | Name: "api-key.kanali.io",
63 | },
64 | Versions: []extensions.APIVersion{
65 | {
66 | Name: "v1",
67 | },
68 | },
69 | Description: "api key TPR",
70 | })
71 | resource, _ = ctlr.ClientSet.Extensions().ThirdPartyResources().Get("api-key-binding.kanali.io")
72 | assert.Equal(t, resource, &extensions.ThirdPartyResource{
73 | ObjectMeta: api.ObjectMeta{
74 | Name: "api-key-binding.kanali.io",
75 | },
76 | Versions: []extensions.APIVersion{
77 | {
78 | Name: "v1",
79 | },
80 | },
81 | Description: "api key binding TPR",
82 | })
83 | }
84 |
85 | func TestIsKubernetesResourceAlreadyExistError(t *testing.T) {
86 | assert.False(t, isKubernetesResourceAlreadyExistError(errors.New("test error")))
87 | se := e.StatusError{
88 | ErrStatus: unversioned.Status{
89 | Code: http.StatusConflict,
90 | Reason: unversioned.StatusReasonAlreadyExists,
91 | },
92 | }
93 | assert.True(t, isKubernetesResourceAlreadyExistError(&se))
94 | se.ErrStatus.Code = http.StatusNotFound
95 | assert.False(t, isKubernetesResourceAlreadyExistError(&se))
96 | }
97 |
--------------------------------------------------------------------------------
/helm/charts/grafana/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Deployment
3 | metadata:
4 | labels:
5 | app: {{ template "grafana.fullname" . }}
6 | chart: "{{.Chart.Name}}-{{.Chart.Version}}"
7 | component: "{{ .Values.server.name }}"
8 | heritage: "{{ .Release.Service }}"
9 | release: "{{ .Release.Name }}"
10 | name: {{ template "grafana.server.fullname" . }}
11 | spec:
12 | replicas: 1
13 | template:
14 | metadata:
15 | annotations:
16 | {{- range $key, $value := .Values.server.annotations }}
17 | {{ $key }}: {{ $value }}
18 | {{- end }}
19 | labels:
20 | app: {{ template "grafana.fullname" . }}
21 | component: "{{ .Values.server.name }}"
22 | release: "{{ .Release.Name }}"
23 | spec:
24 | nodeSelector:
25 | {{ toYaml .Values.server.nodeSelector | indent 12 }}
26 | containers:
27 | - name: {{ template "grafana.name" . }}
28 | image: "{{ .Values.server.image }}"
29 | imagePullPolicy: {{ default "Always" .Values.server.imagePullPolicy }}
30 | env:
31 | - name: GF_SECURITY_ADMIN_USER
32 | valueFrom:
33 | secretKeyRef:
34 | name: {{ template "grafana.server.fullname" . }}
35 | key: grafana-admin-user
36 | - name: GF_SECURITY_ADMIN_PASSWORD
37 | valueFrom:
38 | secretKeyRef:
39 | name: {{ template "grafana.server.fullname" . }}
40 | key: grafana-admin-password
41 | {{- if .Values.server.installPlugins }}
42 | - name: GF_INSTALL_PLUGINS
43 | valueFrom:
44 | configMapKeyRef:
45 | name: {{ template "grafana.server.fullname" . }}-config
46 | key: grafana-install-plugins
47 | {{- end }}
48 | ports:
49 | - containerPort: 3000
50 | readinessProbe:
51 | httpGet:
52 | path: /login
53 | port: 3000
54 | initialDelaySeconds: 30
55 | timeoutSeconds: 30
56 | resources:
57 | {{ toYaml .Values.server.resources | indent 12 }}
58 | volumeMounts:
59 | - name: config-volume
60 | mountPath: {{ default "/etc/grafana" .Values.server.configLocalPath | quote }}
61 | - name: dashboard-volume
62 | mountPath: {{ default "/var/lib/grafana/dashboards" .Values.server.dashboardLocalPath | quote }}
63 | - name: storage-volume
64 | mountPath: {{ default "/var/lib/grafana/data" .Values.server.storageLocalPath | quote }}
65 | subPath: "{{ .Values.server.persistentVolume.subPath }}"
66 | terminationGracePeriodSeconds: {{ default 300 .Values.server.terminationGracePeriodSeconds }}
67 | volumes:
68 | - name: config-volume
69 | configMap:
70 | name: {{ template "grafana.server.fullname" . }}-config
71 | - name: dashboard-volume
72 | configMap:
73 | name: {{ template "grafana.server.fullname" . }}-dashs
74 | - name: storage-volume
75 | {{- if .Values.server.persistentVolume.enabled }}
76 | persistentVolumeClaim:
77 | claimName: {{ if .Values.server.persistentVolume.existingClaim }}{{ .Values.server.persistentVolume.existingClaim }}{{- else }}{{ template "grafana.server.fullname" . }}{{- end }}
78 | {{- else }}
79 | emptyDir: {}
80 | {{- end -}}
81 |
--------------------------------------------------------------------------------
/utils/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package utils
22 |
23 | import (
24 | "bytes"
25 | "net/url"
26 | "path/filepath"
27 | "regexp"
28 | "strings"
29 |
30 | "k8s.io/kubernetes/pkg/api"
31 | )
32 |
33 | // ComputeTargetPath calcuates the target or destination path based on the incoming path,
34 | // desired target path prefix and the assicated proxy
35 | func ComputeTargetPath(proxyPath, proxyTarget, requestPath string) string {
36 |
37 | proxyPath = NormalizeURLPath(proxyPath)
38 | proxyTarget = NormalizeURLPath(proxyTarget)
39 | requestPath = NormalizeURLPath(requestPath)
40 |
41 | var buffer bytes.Buffer
42 |
43 | if len(strings.SplitAfter(requestPath, proxyPath)) == 0 {
44 | buffer.WriteString("/")
45 | } else if proxyTarget != "/" {
46 | buffer.WriteString(proxyTarget)
47 | }
48 |
49 | buffer.WriteString(strings.SplitAfter(requestPath, proxyPath)[1])
50 |
51 | if len(buffer.Bytes()) == 0 {
52 | return "/"
53 | }
54 |
55 | return buffer.String()
56 | }
57 |
58 | // GetAbsPath returns the absolute path given any path
59 | // the returned path is in a form that Kanali prefers
60 | func GetAbsPath(path string) (string, error) {
61 |
62 | p, err := filepath.Abs(path)
63 | if err != nil {
64 | return "", err
65 | }
66 |
67 | if p[len(p)-1] == '/' {
68 | if len(p) < 2 {
69 | return "", nil
70 | }
71 | return p[:len(p)-2], nil
72 | }
73 |
74 | return p, nil
75 |
76 | }
77 |
78 | // CompareObjectMeta will loosly determine whether two ObjectMeta objects are equal.
79 | // It does this by comparing the name and namespace
80 | func CompareObjectMeta(c1, c2 api.ObjectMeta) bool {
81 | return c1.Namespace == c2.Namespace && c1.Name == c2.Name
82 | }
83 |
84 | // ComputeURLPath will correct a URL path that might be valid but not ideally formatted
85 | func ComputeURLPath(u *url.URL) string {
86 | return NormalizeURLPath(u.EscapedPath())
87 | }
88 |
89 | // NormalizeURLPath will normalize any string treating it like a URL path.
90 | func NormalizeURLPath(path string) string {
91 | if len(path) < 1 {
92 | return "/"
93 | }
94 |
95 | path = regexp.MustCompile(`/{2,}`).ReplaceAllString(path, "/")
96 |
97 | if strings.HasSuffix(path, "/") {
98 | path = path[:len(path)-1]
99 | }
100 |
101 | if len(path) < 1 {
102 | return "/"
103 | }
104 |
105 | if path[0] != '/' {
106 | path = "/" + path
107 | }
108 |
109 | return path
110 | }
111 |
--------------------------------------------------------------------------------
/config/proxy.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package config
22 |
23 | func init() {
24 | Flags.Add(
25 | FlagProxyEnableClusterIP,
26 | FlagProxyHeaderMaskValue,
27 | FlagProxyEnableMockResponses,
28 | FlagProxyUpstreamTimeout,
29 | FlagProxyMaskHeaderKeys,
30 | FlagProxyTLSCommonNameValidation,
31 | FlagProxyDefaultHeaderValues,
32 | )
33 | }
34 |
35 | var (
36 | // FlagProxyEnableClusterIP enables to use of cluster ip as opposed to Kubernetes DNS for upstream routing
37 | FlagProxyEnableClusterIP = Flag{
38 | Long: "proxy.enable_cluster_ip",
39 | Short: "",
40 | Value: false,
41 | Usage: "Enables to use of cluster ip as opposed to Kubernetes DNS for upstream routing.",
42 | }
43 | // FlagProxyHeaderMaskValue sets the Value to be used when omitting header Values
44 | FlagProxyHeaderMaskValue = Flag{
45 | Long: "proxy.header_mask_Value",
46 | Short: "",
47 | Value: "omitted",
48 | Usage: "Sets the Value to be used when omitting header Values.",
49 | }
50 | // FlagProxyEnableMockResponses enables Kanali's mock responses feature. Read the documentation for more information
51 | FlagProxyEnableMockResponses = Flag{
52 | Long: "proxy.enable_mock_responses",
53 | Short: "",
54 | Value: false,
55 | Usage: "Enables Kanali's mock responses feature. Read the documentation for more information.",
56 | }
57 | // FlagProxyUpstreamTimeout sets the length of upstream timeout
58 | FlagProxyUpstreamTimeout = Flag{
59 | Long: "proxy.upstream_timeout",
60 | Short: "",
61 | Value: "0h0m10s",
62 | Usage: "Set length of upstream timeout. Defaults to none",
63 | }
64 | // FlagProxyMaskHeaderKeys specifies which headers to mask.
65 | FlagProxyMaskHeaderKeys = Flag{
66 | Long: "proxy.mask_header_keys",
67 | Short: "",
68 | Value: []string{},
69 | Usage: "Specify which headers to mask",
70 | }
71 | // FlagProxyTLSCommonNameValidation determins whether common name validation occurs as part of an SSL handshake
72 | FlagProxyTLSCommonNameValidation = Flag{
73 | Long: "proxy.tls_common_name_validation",
74 | Short: "",
75 | Value: true,
76 | Usage: "Should common name validate as part of an SSL handshake.",
77 | }
78 | // FlagProxyDefaultHeaderValues specifies the default values for HTTP headers to be used in dynamic service discovery
79 | FlagProxyDefaultHeaderValues = Flag{
80 | Long: "proxy.default_header_values",
81 | Short: "",
82 | Value: map[string]string{},
83 | Usage: "Specifies the default values for HTTP headers to be used in dynamic service discovery.",
84 | }
85 | )
86 |
--------------------------------------------------------------------------------
/helm/templates/tlsSecret.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | apiVersion: v1
4 | kind: Secret
5 | metadata:
6 | name: kanali
7 | namespace: default
8 | type: kubernetes.io/tls
9 | data:
10 | tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDempDQ0FiYWdBd0lCQWdJSkFLWXh2bGVwaFZyVU1BMEdDU3FHU0liM0RRRUJDd1VBTUJZeEZEQVNCZ05WDQpCQU1NQzJWNFlXMXdiR1V1WTI5dE1CNFhEVEUzTURRek1ESXlOREUxT1ZvWERURTVNRFF6TURJeU5ERTFPVm93DQpGakVVTUJJR0ExVUVBd3dMWlhoaGJYQnNaUzVqYjIwd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3DQpnZ0VLQW9JQkFRRDVuM1NwUTI5T1dkNUEvbXJFeHh5OGN5ek42WTRTcEFrVjZ4SmsyKzRMRFg5RnZxbGo3NVhqDQpjd3VnY0U1bmhHOHZaWWJMSkRwTFd1cWFUUUtHMGpmN3lNK3pSQ0FzR0dNQ1NPQUczNmZIYThmMU5ZUzh2elVHDQpmVUtLRzJ5OFpWYU9aaHJOOERld1RkOGJIRFJCakllcTVQMjhaZHdqdDJDSSsvMHdBbTlHT1RQa25JMnpWUDdSDQpROVBEcnYxTU55Z0RtRnUyUmx6dFdtbTAxM0x3Z0dEVXRWOXJmd0FnbVpFc29iMUVsK3VWeFlRREZZMGx6a3BRDQpVdGNpR0tMV283TUNUV3ZmY1ZHc0hKelZWL29ydDZERlN3TlJOY0swZWh1ZzduaHlOYUttTzFlRE1QKzgzR285DQpFeXArMlVJYXovYjJtQ3QrT3E4c1ZLN0pZZWttV05hakFnTUJBQUdqSHpBZE1Cc0dBMVVkRVFRVU1CS0hCTUNvDQpZMlNIQk1Db1kyV0hCTUNvWTJZd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLdEtBMDJVK1BEQVQ0TnNGMjlXDQpOazYzTFkzM2RmSFdPSUs1QjFDMzhpTEdJQU9YT1A5cWlBZHJ5NHFIN3RDOUJtb3FYcVdSZnJ2S2NFdTA3bDc4DQp5N1lGK1RQTUtNY0ZnaXRDbmQydVFTSk52VUM2aDBSaVg4NjI3SWZDaUlVNG5oWXVvOEk3TkZaOWlCdXNac2xyDQo1S2dEODdyTUxvL3UrQitobkpEZjRUSStpNnhuMjd0Z3dqeWlrbGJRUFlXZ1VFK3FNYVVaZGVEVTBmT1lEN09xDQpoay8zdkJaQlkxcGhHeS9GckZLYnJEazA1ellNT05JSDlvUjJIOUpRdnF6VzVOQ2Z6M3dpZGpqZFZ4aUloMzlWDQp5eW5rQ2VpN1VCa0MvZnRPcDd1dTRjdXoxN1I5aFc3ZjhDZHNtcWZidkZhcVkzVHVzTnlzL0lFRmdyN3p5RHc4DQo0Y289DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t
11 | tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ0KTUlJRW93SUJBQUtDQVFFQStaOTBxVU52VGxuZVFQNXF4TWNjdkhNc3plbU9FcVFKRmVzU1pOdnVDdzEvUmI2cA0KWSsrVjQzTUxvSEJPWjRSdkwyV0d5eVE2UzFycW1rMENodEkzKzhqUHMwUWdMQmhqQWtqZ0J0K254MnZIOVRXRQ0Kdkw4MUJuMUNpaHRzdkdWV2ptWWF6ZkEzc0UzZkd4dzBRWXlIcXVUOXZHWGNJN2RnaVB2OU1BSnZSamt6NUp5Tg0KczFUKzBVUFR3Njc5VERjb0E1aGJ0a1pjN1ZwcHROZHk4SUJnMUxWZmEzOEFJSm1STEtHOVJKZnJsY1dFQXhXTg0KSmM1S1VGTFhJaGlpMXFPekFrMXIzM0ZSckJ5YzFWZjZLN2VneFVzRFVUWEN0SG9ib081NGNqV2lwanRYZ3pELw0Kdk54cVBSTXFmdGxDR3MvMjlwZ3JmanF2TEZTdXlXSHBKbGpXb3dJREFRQUJBb0lCQUVOQTQ5U0t0NTFiZHhiNQ0KdE5ocGNPT1JBRnhGOWFJdUVjaVcrZkMwbEhEajVRdHNjQVRkMHZ0aHpwc2VSdkY2NjkzUU03M2RkOXMvbG4rVw0KQ2YwNi9CeFpJU1NDVVV5d0VWVFhQNHg2aTZDZCtGU25ZNmphdHpXVlgrMEhzSWNkb25GaEx3MlhEOW52VVJIag0KeW14eXFVYXd4WEFSM1hxOStFTlA2UW9iVGRHVUphQlE1MlZJN1B0dm8wV3hmM0dyUWs0Z3ZpZ2FHM0JDNUI3OQ0KTW1TYnhLNXdOOURycm1meGZ1MjFSVU00Y005ZG1iU2ZvNTEzVU9Vdm1Ya3d3c3F3M01GVTRLd0dUVi8xdXN1Uw0KbkJLZTJkcWhKMThOd1dNaGpuWlJWMEcrTTRLSkttcnV3cXRlVWFENDl5ZmZSUXpLRDRhazBDK2tqZnpNRWRTQg0KdXZSSTJ0RUNnWUVBL2ZBb2RrbnhFN2l0WmVNMnoxeVJKWE5CVnNVcFJ5NjY0QWxaNXBRSjRSQjhmZ1NLa0Y4NQ0KZUw5VmZ5enFSVldQQkxCaVhRdG9EMTVxL2JGcDZxUlNDWXd0b0M0R3J3VEtFUERvUmNyVEorelVCeXpMZTYzSg0KV0w0ZE1SUG5JcUZCL3E3SkVGc3dMbmNzbDd6R1RtaHdrclJkaEoyeE51djd2Z3RHNEpaRU84c0NnWUVBKzZaVA0KOEI0MS8ycnVHeG1nT09pNDliM1Y1RHhJMVdVYXpHaUF0Qk5ORmxEZm8zWnE0bkdtU05XZ1NuQW5COGo0cFU1Tg0KU2ZzK09VRit5Si84MmlqMVBmZDhNYzZ3ZTd4ZmJMZFhFNmpHOGp3S1ZVcGUwVGFGbHpRdm1RSWFvcUdUdHBOWg0KUVAzaE9iMHhaZTVjVytXSlJmSldZOVJWUUdJdm83dVRJZUNCcFlrQ2dZRUF4TlVWbC9MaWtlWFJTaXVmdllYRA0KNENLQlgrKzllamFIbGNiSno0ZXFUTEVKdm1ob3UxV0VaOHJ2UzMrV0s1NFJHSkpiL0VFdUxOT0QzUmRhd1EwVA0KcGVEcE1NTGNYV2M1OVgyMm5QcUZSK296dzBmK2hlU0VNR3hVbGtrV0hPcWdDL2lSVTBOTGlvakhvT29yVUhWMQ0KNU5FM3QrYS9pWkhMZFZpcVhNVTlLSmNDZ1lBWjZENjkrcTQrZEdpOCszOW1QSGRHUFZ2MjJrbjVSaVpqSXVNVg0KSnVPSng1dXVmWE4xaXBPKzdkZEpzcEFpR2d1WElSK04zVUxEckQxOE5CUlk5VnlDRzZkNmpUZllGVVdSc0xKVA0KUU0zeWhFSGdFLzc4OU9yOTdRNTFaeVVNMXl1WTRVU1FEMU1QbWEyck84WGdaQm9rekZVZWcrNmU2VHpVVTJ4TA0KVVl5bldRS0JnQWg0UnN3dXBqMzVsakUyY0dLa2ZpODBCTHR4b3ZvMzZVbmJtTWY1eFZQaTJLRmtxSWZUelZRbw0KQ3hEWHQ4R1ZWOTViTTZQOUNDODhNWks3MERkZXFZdWtxaG9DRDRIUzFVaUloaitEcEhzMU16MmxJVUZ0QWhTQg0KMGFwc2JDejloTGdMakNEbkFORzFsVGJSR0FVblQvb1o4azJSUytwMldxak9SS2t0ZXhiMA0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0=
--------------------------------------------------------------------------------
/steps/mockservice.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package steps
22 |
23 | import (
24 | "fmt"
25 | "bytes"
26 | "context"
27 | "encoding/json"
28 | "errors"
29 | "strconv"
30 | "net/http"
31 | "net/http/httptest"
32 |
33 | "github.com/northwesternmutual/kanali/metrics"
34 | "github.com/northwesternmutual/kanali/spec"
35 | "github.com/northwesternmutual/kanali/utils"
36 | "github.com/opentracing/opentracing-go"
37 | )
38 |
39 | // MockServiceStep is factory that defines a step responsible for
40 | // discovering a mock response for the incoming request
41 | type MockServiceStep struct{}
42 |
43 | // GetName retruns the name of the MockServiceStep step
44 | func (step MockServiceStep) GetName() string {
45 | return "Mock Service"
46 | }
47 |
48 | // Do executes the logic of the MockServiceStep step
49 | func (step MockServiceStep) Do(ctx context.Context, proxy *spec.APIProxy, m *metrics.Metrics, w http.ResponseWriter, r *http.Request, resp *http.Response, trace opentracing.Span) error {
50 |
51 | targetPath := utils.ComputeTargetPath(proxy.Spec.Path, proxy.Spec.Target, utils.ComputeURLPath(r.URL))
52 |
53 | untypedMr, err := spec.MockResponseStore.Get(proxy.ObjectMeta.Namespace, proxy.Spec.Mock.ConfigMapName, targetPath, r.Method)
54 | if err != nil {
55 | return &utils.StatusError{Code: http.StatusInternalServerError, Err: fmt.Errorf("error retrieving mock response: %s", err.Error())}
56 | }
57 | if untypedMr == nil {
58 | return &utils.StatusError{Code: http.StatusNotFound, Err: errors.New("no mock response found")}
59 | }
60 | mr, ok := untypedMr.(spec.Route)
61 | if !ok {
62 | return &utils.StatusError{Code: http.StatusNotFound, Err: errors.New("no mock response found")}
63 | }
64 |
65 | mockBodyData, err := json.Marshal(mr.Body)
66 | if err != nil {
67 | return &utils.StatusError{Code: http.StatusInternalServerError, Err: fmt.Errorf("the configmap %s in the namespace %s is not formated correctly. while data was found for the incoming route, it was not valid json",
68 | proxy.Spec.Mock.ConfigMapName,
69 | proxy.ObjectMeta.Namespace,
70 | )}
71 | }
72 |
73 | // create new upstream header object
74 | upstreamHeaders := http.Header{}
75 |
76 | // currently, we are enforcing a json response
77 | upstreamHeaders.Add("Content-Type", "application/json")
78 |
79 | // create a fake response
80 | responseRecorder := &httptest.ResponseRecorder{
81 | Code: mr.Code,
82 | Body: bytes.NewBuffer(mockBodyData),
83 | HeaderMap: upstreamHeaders,
84 | }
85 |
86 | m.Add(metrics.Metric{Name: "http_response_code", Value: strconv.Itoa(mr.Code), Index: true})
87 |
88 | *resp = *responseRecorder.Result()
89 |
90 | return nil
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/utils/utils_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package utils
22 |
23 | import (
24 | "testing"
25 |
26 | "github.com/stretchr/testify/assert"
27 | "k8s.io/kubernetes/pkg/api"
28 | )
29 |
30 | func TestComputeTargetPath(t *testing.T) {
31 | assert.Equal(t, "/", NormalizeURLPath(ComputeTargetPath("/foo/bar", "", "/foo/bar")))
32 | assert.Equal(t, "/", NormalizeURLPath(ComputeTargetPath("/foo/bar", "/", "/foo/bar")))
33 | assert.Equal(t, "/foo", NormalizeURLPath(ComputeTargetPath("/foo/bar", "/foo", "/foo/bar")))
34 | assert.Equal(t, "/foo/bar", NormalizeURLPath(ComputeTargetPath("/foo/bar", "/foo", "/foo/bar/bar")))
35 | assert.Equal(t, "/bar", NormalizeURLPath(ComputeTargetPath("/foo/bar", "", "/foo/bar/bar")))
36 | assert.Equal(t, "/accounts", NormalizeURLPath(ComputeTargetPath("/api/v1/example-two", "/", "/api/v1/example-two/accounts")))
37 | assert.Equal(t, "/accounts", NormalizeURLPath(ComputeTargetPath("/api/v1/example-two", "/", "/api/v1/example-two/accounts/")))
38 | assert.Equal(t, "/accounts", NormalizeURLPath(ComputeTargetPath("/api/v1/example-two", "", "/api/v1/example-two/accounts/")))
39 | assert.Equal(t, "/accounts", NormalizeURLPath(ComputeTargetPath("/api/v1/example-two/", "/", "/api/v1/example-two/accounts/")))
40 | assert.Equal(t, "/accounts", NormalizeURLPath(ComputeTargetPath("/api/v1/example-two/", "", "/api/v1/example-two/accounts/")))
41 | assert.Equal(t, "/accounts", NormalizeURLPath(ComputeTargetPath("/api/v1/example-two/", "", "/api/v1/example-two/accounts")))
42 | assert.Equal(t, "/", NormalizeURLPath(ComputeTargetPath("/", "", "/")))
43 | assert.Equal(t, "/", NormalizeURLPath(ComputeTargetPath("/", "/", "/")))
44 | }
45 |
46 | func TestAbsPath(t *testing.T) {
47 | p, _ := GetAbsPath("/")
48 | assert.Equal(t, "", p)
49 | p, _ = GetAbsPath("/foo/")
50 | assert.Equal(t, "/foo", p)
51 | p, _ = GetAbsPath("//")
52 | assert.Equal(t, "", p)
53 | }
54 |
55 | func TestCompareObjectMeta(t *testing.T) {
56 | c1 := api.ObjectMeta{
57 | Name: "foo",
58 | Namespace: "bar",
59 | }
60 | c2 := api.ObjectMeta{
61 | Name: "bar",
62 | Namespace: "foo",
63 | }
64 | c3 := api.ObjectMeta{
65 | Name: "foo",
66 | Namespace: "car",
67 | }
68 | c4 := api.ObjectMeta{
69 | Name: "bar",
70 | Namespace: "car",
71 | }
72 |
73 | assert.True(t, CompareObjectMeta(c1, c1))
74 | assert.False(t, CompareObjectMeta(c1, c2))
75 | assert.False(t, CompareObjectMeta(c1, c3))
76 | assert.False(t, CompareObjectMeta(c3, c4))
77 | }
78 |
79 | func TestNormalizeURLPath(t *testing.T) {
80 | assert.Equal(t, "/foo/bar", NormalizeURLPath("foo////bar"))
81 | assert.Equal(t, "/foo", NormalizeURLPath("foo"))
82 | assert.Equal(t, "/foo", NormalizeURLPath("foo////"))
83 | assert.Equal(t, "/foo/bar", NormalizeURLPath("///foo////bar//"))
84 | assert.Equal(t, "/", NormalizeURLPath(""))
85 | assert.Equal(t, "/", NormalizeURLPath("////"))
86 | assert.Equal(t, "/https%3A%2F%2Fgoogle.com", NormalizeURLPath("/////https%3A%2F%2Fgoogle.com"))
87 | }
88 |
--------------------------------------------------------------------------------
/steps/validateproxy_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Northwestern Mutual.
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | // THE SOFTWARE.
20 |
21 | package steps
22 |
23 | import (
24 | "context"
25 | "errors"
26 | "net/http"
27 | "net/url"
28 | "testing"
29 |
30 | "github.com/northwesternmutual/kanali/metrics"
31 | "github.com/northwesternmutual/kanali/spec"
32 | "github.com/northwesternmutual/kanali/utils"
33 | "github.com/opentracing/opentracing-go"
34 | "github.com/stretchr/testify/assert"
35 | "k8s.io/kubernetes/pkg/api"
36 | "k8s.io/kubernetes/pkg/api/unversioned"
37 | )
38 |
39 | func TestValidateProxyGetName(t *testing.T) {
40 | assert := assert.New(t)
41 | step := ValidateProxyStep{}
42 | assert.Equal(step.GetName(), "Validate Proxy", "step name is incorrect")
43 | }
44 |
45 | func TestValidateProxy(t *testing.T) {
46 | assert := assert.New(t)
47 | step := ValidateProxyStep{}
48 |
49 | proxyStore := spec.ProxyStore
50 | proxyList := getTestAPIProxyListForValidateProxy()
51 | proxyStore.Clear()
52 | proxyStore.Set(proxyList.Proxies[0])
53 | proxyStore.Set(proxyList.Proxies[1])
54 |
55 | urlOne, _ := url.Parse("https://www.foo.bar.com/api/v1/accounts/one/two")
56 | urlTwo, _ := url.Parse("https://www.foo.bar.com/api/v1/field/one/two")
57 | urlThree, _ := url.Parse("https://www.foo.bar.com/")
58 | urlFour, _ := url.Parse("https://www.foo.bar.com/foo/bar")
59 |
60 | proxy := &spec.APIProxy{}
61 |
62 | assert.Nil(step.Do(context.Background(), proxy, &metrics.Metrics{}, nil, &http.Request{URL: urlOne}, nil, opentracing.StartSpan("test span")), "expected proxy to be found")
63 | assert.Equal(*proxy, proxyList.Proxies[0])
64 | assert.Nil(step.Do(context.Background(), proxy, &metrics.Metrics{}, nil, &http.Request{URL: urlTwo}, nil, opentracing.StartSpan("test span")), "expected proxy to be found")
65 | assert.Equal(*proxy, proxyList.Proxies[1])
66 | assert.Equal(utils.StatusError{Code: http.StatusNotFound, Err: errors.New("proxy not found")}, step.Do(context.Background(), nil, &metrics.Metrics{}, nil, &http.Request{URL: urlThree}, nil, opentracing.StartSpan("test span")), "expected proxy to not exist")
67 | assert.Equal(utils.StatusError{Code: http.StatusNotFound, Err: errors.New("proxy not found")}, step.Do(context.Background(), nil, &metrics.Metrics{}, nil, &http.Request{URL: urlFour}, nil, opentracing.StartSpan("test span")), "expected proxy to not exist")
68 | }
69 |
70 | func getTestAPIProxyListForValidateProxy() *spec.APIProxyList {
71 |
72 | return &spec.APIProxyList{
73 | TypeMeta: unversioned.TypeMeta{},
74 | ListMeta: unversioned.ListMeta{},
75 | Proxies: []spec.APIProxy{
76 | {
77 | TypeMeta: unversioned.TypeMeta{},
78 | ObjectMeta: api.ObjectMeta{
79 | Name: "exampleAPIProxyOne",
80 | Namespace: "foo",
81 | },
82 | Spec: spec.APIProxySpec{
83 | Path: "/api/v1/accounts",
84 | Target: "/",
85 | Service: spec.Service{
86 | Namespace: "foo",
87 | },
88 | },
89 | },
90 | {
91 | TypeMeta: unversioned.TypeMeta{},
92 | ObjectMeta: api.ObjectMeta{
93 | Name: "exampleAPIProxyTwo",
94 | Namespace: "foo",
95 | },
96 | Spec: spec.APIProxySpec{
97 | Path: "/api/v1/field",
98 | Target: "/",
99 | Service: spec.Service{
100 | Namespace: "foo",
101 | },
102 | },
103 | },
104 | },
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------