Not creating app deployments (#78)
* Allowing users to scale an existing deployment Fixes https://github.com/kedacore/http-add-on/issues/35 Signed-off-by: Aaron Schlesinger <aaron@ecomaz.net> * checking custom deployment info Signed-off-by: Aaron Schlesinger <aaron@ecomaz.net> * Adding tests for new deployment logic Also generalizing test "infrastructure" code Signed-off-by: Aaron Schlesinger <aaron@ecomaz.net> * regenerating code Signed-off-by: Aaron Schlesinger <aaron@ecomaz.net> * Remove functionality to auto-create deployments and services Users provide a scale target ref, which is the name of the deployment to scale and the service to route to. They are required to have already deployed these things already Signed-off-by: Aaron Schlesinger <aaron@ecomaz.net> * Adding more docs Signed-off-by: Aaron Schlesinger <aaron@ecomaz.net> * regenerating with 0.5.0 controller-gen Signed-off-by: Aaron Schlesinger <aaron@ecomaz.net> * fixing compile err Signed-off-by: Aaron Schlesinger <aaron@ecomaz.net>
This commit is contained in:
parent
a0260907a1
commit
fd4cdfe718
6
Makefile
6
Makefile
|
|
@ -86,6 +86,12 @@ helm-upgrade-operator:
|
|||
.PHONY: helm-delete-operator
|
||||
helm-delete-operator:
|
||||
helm delete -n ${NAMESPACE} kedahttp
|
||||
|
||||
.PHONY: generate-operator
|
||||
generate-operator:
|
||||
cd operator && \
|
||||
make manifests && \
|
||||
cp config/crd/bases/http.keda.sh_httpscaledobjects.yaml ../charts/keda-http-operator/crds/httpscaledobjects.http.keda.sh.yaml
|
||||
|
||||
#####
|
||||
# universal targets
|
||||
|
|
|
|||
|
|
@ -16,119 +16,130 @@ spec:
|
|||
singular: httpscaledobject
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: HTTPScaledObject is the Schema for the scaledobjects API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: '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. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: '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. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: HTTPScaledObjectSpec defines the desired state of HTTPScaledObject
|
||||
properties:
|
||||
app_image:
|
||||
description: The image this application will use.
|
||||
type: string
|
||||
app_name:
|
||||
description: (optional) The name of the application to be created.
|
||||
type: string
|
||||
max_replicas:
|
||||
description: Maximum amount of replicas to have in the deployment (Default 100)
|
||||
format: int32
|
||||
type: integer
|
||||
min_replicas:
|
||||
description: Minimum amount of replicas to have in the deployment (Default 0)
|
||||
format: int32
|
||||
type: integer
|
||||
port:
|
||||
description: The port this application will serve on.
|
||||
format: int32
|
||||
type: integer
|
||||
required:
|
||||
- app_image
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: HTTPScaledObject is the Schema for the scaledobjects API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: '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. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: '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. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: HTTPScaledObjectSpec defines the desired state of HTTPScaledObject
|
||||
properties:
|
||||
replicas:
|
||||
description: (optional) Replica information
|
||||
properties:
|
||||
max:
|
||||
description: Maximum amount of replicas to have in the deployment (Default 100)
|
||||
format: int32
|
||||
type: integer
|
||||
min:
|
||||
description: Minimum amount of replicas to have in the deployment (Default 0)
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
scaleTargetRef:
|
||||
description: The name of the deployment to route HTTP requests to (and to autoscale). Either this or Image must be set
|
||||
properties:
|
||||
deployment:
|
||||
description: The name of the deployment to scale according to HTTP traffic
|
||||
type: string
|
||||
port:
|
||||
description: The port to route to
|
||||
format: int32
|
||||
type: integer
|
||||
service:
|
||||
description: The name of the service to route to
|
||||
type: string
|
||||
required:
|
||||
- deployment
|
||||
- port
|
||||
type: object
|
||||
status:
|
||||
description: HTTPScaledObjectStatus defines the observed state of HTTPScaledObject
|
||||
properties:
|
||||
conditions:
|
||||
description: List of auditable conditions of the operator
|
||||
items:
|
||||
description: Condition to store the condition state
|
||||
properties:
|
||||
message:
|
||||
description: A human readable message indicating details about the transition.
|
||||
type: string
|
||||
reason:
|
||||
description: The reason for the condition's last transition.
|
||||
enum:
|
||||
- ErrorCreatingExternalScaler
|
||||
- ErrorCreatingExternalScalerService
|
||||
- CreatedExternalScaler
|
||||
- ErrorCreatingAppDeployment
|
||||
- AppDeploymentCreated
|
||||
- ErrorCreatingAppService
|
||||
- AppServiceCreated
|
||||
- ErrorCreatingScaledObject
|
||||
- ScaledObjectCreated
|
||||
- ErrorCreatingInterceptor
|
||||
- ErrorCreatingInterceptorAdminService
|
||||
- ErrorCreatingInterceptorProxyService
|
||||
- InterceptorCreated
|
||||
- TerminatingResources
|
||||
- AppDeploymentTerminationError
|
||||
- AppDeploymentTerminated
|
||||
- InterceptorDeploymentTerminated
|
||||
- InterceptorDeploymentTerminationError
|
||||
- InterceptorAdminServiceTerminationError
|
||||
- InterceptorAdminServiceTerminated
|
||||
- InterceptorProxyServiceTerminationError
|
||||
- InterceptorProxyServiceTerminated
|
||||
- ExternalScalerDeploymentTerminationError
|
||||
- ExternalScalerDeploymentTerminated
|
||||
- ExternalScalerServiceTerminationError
|
||||
- ExternalScalerServiceTerminated
|
||||
- AppServiceTerminationError
|
||||
- AppServiceTerminated
|
||||
- ScaledObjectTerminated
|
||||
- ScaledObjectTerminationError
|
||||
- PendingCreation
|
||||
- HTTPScaledObjectIsReady
|
||||
type: string
|
||||
status:
|
||||
description: Status of the condition, one of True, False, Unknown.
|
||||
type: string
|
||||
timestamp:
|
||||
description: Timestamp of the condition
|
||||
type: string
|
||||
type:
|
||||
description: Type of condition
|
||||
enum:
|
||||
- Created
|
||||
- Error
|
||||
- Pending
|
||||
- Unknown
|
||||
- Terminating
|
||||
- Terminated
|
||||
- Ready
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: { }
|
||||
- service
|
||||
type: object
|
||||
required:
|
||||
- scaleTargetRef
|
||||
type: object
|
||||
status:
|
||||
description: HTTPScaledObjectStatus defines the observed state of HTTPScaledObject
|
||||
properties:
|
||||
conditions:
|
||||
description: List of auditable conditions of the operator
|
||||
items:
|
||||
description: Condition to store the condition state
|
||||
properties:
|
||||
message:
|
||||
description: A human readable message indicating details about the transition.
|
||||
type: string
|
||||
reason:
|
||||
description: The reason for the condition's last transition.
|
||||
enum:
|
||||
- ErrorCreatingExternalScaler
|
||||
- ErrorCreatingExternalScalerService
|
||||
- CreatedExternalScaler
|
||||
- ErrorCreatingAppDeployment
|
||||
- AppDeploymentCreated
|
||||
- ErrorCreatingAppService
|
||||
- AppServiceCreated
|
||||
- ErrorCreatingScaledObject
|
||||
- ScaledObjectCreated
|
||||
- ErrorCreatingInterceptor
|
||||
- ErrorCreatingInterceptorAdminService
|
||||
- ErrorCreatingInterceptorProxyService
|
||||
- InterceptorCreated
|
||||
- TerminatingResources
|
||||
- AppDeploymentTerminationError
|
||||
- AppDeploymentTerminated
|
||||
- InterceptorDeploymentTerminated
|
||||
- InterceptorDeploymentTerminationError
|
||||
- InterceptorAdminServiceTerminationError
|
||||
- InterceptorAdminServiceTerminated
|
||||
- InterceptorProxyServiceTerminationError
|
||||
- InterceptorProxyServiceTerminated
|
||||
- ExternalScalerDeploymentTerminationError
|
||||
- ExternalScalerDeploymentTerminated
|
||||
- ExternalScalerServiceTerminationError
|
||||
- ExternalScalerServiceTerminated
|
||||
- AppServiceTerminationError
|
||||
- AppServiceTerminated
|
||||
- ScaledObjectTerminated
|
||||
- ScaledObjectTerminationError
|
||||
- PendingCreation
|
||||
- HTTPScaledObjectIsReady
|
||||
type: string
|
||||
status:
|
||||
description: Status of the condition, one of True, False, Unknown.
|
||||
type: string
|
||||
timestamp:
|
||||
description: Timestamp of the condition
|
||||
type: string
|
||||
type:
|
||||
description: Type of condition
|
||||
enum:
|
||||
- Created
|
||||
- Error
|
||||
- Pending
|
||||
- Unknown
|
||||
- Terminating
|
||||
- Terminated
|
||||
- Ready
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
apiVersion: v2
|
||||
name: xkcd
|
||||
description: A Helm chart for Kubernetes
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
# to be deployed.
|
||||
#
|
||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "1.16.0"
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range $host := .Values.ingress.hosts }}
|
||||
{{- range .paths }}
|
||||
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "xkcd.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "xkcd.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "xkcd.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "xkcd.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
|
||||
{{- end }}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "xkcd.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "xkcd.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "xkcd.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "xkcd.labels" -}}
|
||||
helm.sh/chart: {{ include "xkcd.chart" . }}
|
||||
{{ include "xkcd.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "xkcd.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "xkcd.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "xkcd.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "xkcd.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "xkcd.fullname" . }}
|
||||
labels:
|
||||
{{- include "xkcd.labels" . | nindent 4 }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "xkcd.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "xkcd.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "xkcd.serviceAccountName" . }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
{{- if .Values.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2beta1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "xkcd.fullname" . }}
|
||||
labels:
|
||||
{{- include "xkcd.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "xkcd.fullname" . }}
|
||||
minReplicas: {{ .Values.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "xkcd.fullname" . }}
|
||||
labels:
|
||||
{{- include "xkcd.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "xkcd.selectorLabels" . | nindent 4 }}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{{- if .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "xkcd.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "xkcd.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: "{{ include "xkcd.fullname" . }}-test-connection"
|
||||
labels:
|
||||
{{- include "xkcd.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
spec:
|
||||
containers:
|
||||
- name: wget
|
||||
image: busybox
|
||||
command: ['wget']
|
||||
args: ['{{ include "xkcd.fullname" . }}:{{ .Values.service.port }}']
|
||||
restartPolicy: Never
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: arschles/xkcd
|
||||
pullPolicy: Always
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
podAnnotations: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext: {}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
# runAsNonRoot: true
|
||||
# runAsUser: 1000
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8080
|
||||
|
||||
|
|
@ -31,20 +31,22 @@ git clone https://github.com/kedacore/http-add-on.git
|
|||
cd http-add-on
|
||||
```
|
||||
|
||||
Next, install the HTTP add on:
|
||||
Next, install the HTTP add on. The below command will install the add on if it doesn't already exist:
|
||||
|
||||
```shell
|
||||
make helm-upgrade-operator
|
||||
```
|
||||
|
||||
>The above command will install KEDA HTTP if it doesn't already exist.
|
||||
>Installing the HTTP add on won't affect any running workloads in your cluster. You'll need to install an `HTTPScaledObject` for each individual `Deployment` you want to scale. For more on how to do that, please see the [walkthrough](./walkthrough.md).
|
||||
|
||||
There are two environment variables in the above command that you can set to customize how it behaves:
|
||||
There are a few environment variables in the above command that you can set to customize how it behaves:
|
||||
|
||||
- `NAMESPACE` - which Kubernetes namespace to install KEDA-HTTP. This should be the same as where you installed KEDA itself (required)
|
||||
- `OPERATOR_DOCKER_IMG` - the name of the operator's Docker image (optional - falls back to a sensible default)
|
||||
- `SCALER_DOCKER_IMG` - the name of the scaler's Docker image (optional - falls back to a sensible default)
|
||||
- `INTERCEPTOR_DOCKER_IMG` - the name of the interceptor's Docker image (optional - falls back to a sensible default)
|
||||
- `OPERATOR_DOCKER_IMG` - the name of the operator's Docker image (optional - falls back to the latest release)
|
||||
- `SCALER_DOCKER_IMG` - the name of the scaler's Docker image (optional - falls back to the latest release)
|
||||
- `INTERCEPTOR_DOCKER_IMG` - the name of the interceptor's Docker image (optional - falls back to the latest release)
|
||||
|
||||
>I recommend using [direnv](https://direnv.net/) to store your environment variables
|
||||
|
||||
### If You're Installing into a [Microk8s](https://microk8s.io) Cluster
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
# The `HTTPScaledObject`
|
||||
|
||||
Each `HTTPScaledObject` looks approximately like the below:
|
||||
|
||||
```yaml
|
||||
kind: HTTPScaledObject
|
||||
apiVersion: http.keda.sh/v1alpha1
|
||||
metadata:
|
||||
name: xkcd
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
deployment: xkcd
|
||||
service: xkcd
|
||||
port: 8080
|
||||
```
|
||||
|
||||
This document is a narrated reference guide for the `HTTPScaledObject`, and we'll focus on the `spec` field.
|
||||
|
||||
## `scaleTargetRef`
|
||||
|
||||
This is the primary and most important part of the `spec` because it describes (1) what `Deployment` to scale and (2) where and how to route traffic.
|
||||
|
||||
### `deployment`
|
||||
|
||||
This is the name of the `Deployment` to scale. It must exist in the same namespace as this `HTTPScaledObject` and shouldn't be managed by any other autoscaling system. This means that there should not be any `ScaledObject` already created for this `Deployment`. The HTTP add on will manage a `ScaledObject` internally.
|
||||
|
||||
### `service`
|
||||
|
||||
This is the name of the service to route traffic to. The add on will create autoscaling and routing components that route to this `Service`. It must exist in the same namespace as this `HTTPScaledObject` and should route to the same `Deployment` as you entered in the `deployment` field.
|
||||
|
||||
### `port`
|
||||
|
||||
This is the port to route to on the service that you specified in the `service` field. It should be exposed on the service and should route to a valid `containerPort` on the `Deployment` you gave in the `deployment` field.
|
||||
|
|
@ -1,17 +1,43 @@
|
|||
# Getting Started With The HTTP Add On
|
||||
|
||||
One of the primary goals of this project is a simple common-case developer experience. After you've installed KEDA and the HTTP Add On (this project), this document will show you how to get started with an example app.
|
||||
After you've installed KEDA and the HTTP Add On (this project, we'll call it the "add on" for short), this document will show you how to get started with an example app.
|
||||
|
||||
>If you haven't installed KEDA and this project, please do so first. Follow instructions [install.md](./install.md) to complete your installation.
|
||||
If you haven't installed KEDA and the HTTP Add On (this project), please do so first. Follow instructions [install.md](./install.md) to complete your installation. Before you continue, make sure that you have your `NAMESPACE` environment variable set to the same value as it was when you installed.
|
||||
|
||||
## Submitting an `HTTPScaledObject`
|
||||
## Creating An Application
|
||||
|
||||
You interact with the operator via a CRD called `HTTPScaledObject`. To get an example app up and running, read the notes below and then run the subsequent command from the root of this repository.
|
||||
You'll need to install a `Deployment` and `Service` first. You'll tell the add on to begin scaling it up and down after this step. Use this [Helm](https://helm.sh) command to create the resources you need:
|
||||
|
||||
- Make sure that your `NAMESPACE` environment variable is set to the same value as what you [install](./install.md)ed with
|
||||
- I recommend using [direnv](https://direnv.net/) to store your environment variables
|
||||
- This command will install a simple server that is exposed to the internet using a `LoadBalancer` `Service`. _Do not use this for a production deployment_. Support for `Ingress` is forthcoming in [issue #33](https://github.com/kedacore/http-add-on/issues/33)
|
||||
```shell
|
||||
helm install xkcd ./charts/xkcd -n ${NAMESPACE}
|
||||
```
|
||||
|
||||
>To remove the app, run `helm delete xkcd -n ${NAMESPACE}`
|
||||
|
||||
## Creating an `HTTPScaledObject`
|
||||
|
||||
You interact with the operator via a CRD called `HTTPScaledObject`. This CRD object points the To get an example app up and running, read the notes below and then run the subsequent command from the root of this repository.
|
||||
|
||||
```shell
|
||||
kubectl create -f -n $NAMESPACE examples/httpscaledobject.yaml
|
||||
```
|
||||
|
||||
>If you'd like to learn more about this object, please see the [`HTTPScaledObject` reference](./ref/http_scaled_object.md).
|
||||
|
||||
## Testing Your Installation
|
||||
|
||||
You've now installed a web application and activated autoscaling by creating an `HTTPScaledObject` for it. For autoscaling to work properly, HTTP traffic needs to route through the `Service` that the add on has set up. You can use `kubectl port-forward` to quickly test things out:
|
||||
|
||||
```shell
|
||||
k port-forward svc/xkcd-interceptor-proxy -n ${NAMESPACE} 8080:80
|
||||
```
|
||||
|
||||
### Routing to the Right `Service`
|
||||
|
||||
As said above, you need to route your HTTP traffic to the `Service` that the add on has created. If you have existing systems - like an ingress controller - you'll need to anticipate the name of these created `Service`s. Each one will be named consistently like so, in the same namespace as the `HTTPScaledObject` and your application (i.e. `$NAMESPACE`):
|
||||
|
||||
```shell
|
||||
<deployment name>-interceptor-proxy
|
||||
```
|
||||
|
||||
>The service will always be a `ClusterIP` type and will be created in the same namespace as the `HTTPScaledObject` you created.
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ apiVersion: http.keda.sh/v1alpha1
|
|||
metadata:
|
||||
name: xkcd
|
||||
spec:
|
||||
app_name: xkcd
|
||||
app_image: arschles/xkcd
|
||||
port: 8080
|
||||
scaleTargetRef:
|
||||
deployment: xkcd
|
||||
service: xkcd
|
||||
port: 8080
|
||||
replicas:
|
||||
min: 5
|
||||
max: 10
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -7,6 +7,7 @@ require (
|
|||
github.com/golang/protobuf v1.4.3
|
||||
github.com/labstack/echo/v4 v4.2.1
|
||||
github.com/magefile/mage v1.11.0
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/onsi/ginkgo v1.15.2
|
||||
github.com/onsi/gomega v1.11.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
|
|
@ -14,6 +15,7 @@ require (
|
|||
google.golang.org/grpc v1.33.2
|
||||
google.golang.org/protobuf v1.25.0
|
||||
k8s.io/api v0.20.2
|
||||
k8s.io/apiextensions-apiserver v0.20.2 // indirect
|
||||
k8s.io/apimachinery v0.20.2
|
||||
k8s.io/client-go v0.20.2
|
||||
sigs.k8s.io/controller-runtime v0.8.1
|
||||
|
|
|
|||
19
go.sum
19
go.sum
|
|
@ -1,6 +1,5 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
|
|
@ -50,7 +49,6 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
|
|||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
|
|
@ -256,8 +254,9 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
|
|||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
|
|
@ -288,7 +287,6 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m
|
|||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
|
|
@ -298,7 +296,6 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB
|
|||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
|
||||
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.15.2 h1:l77YT15o814C2qVL47NOyjV/6RbaP7kKdrvZnxQ3Org=
|
||||
github.com/onsi/ginkgo v1.15.2/go.mod h1:Dd6YFfwBW84ETqqtL0CPyPXillHgY6XhQH3uuCCTr/o=
|
||||
|
|
@ -306,7 +303,6 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV
|
|||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
|
||||
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug=
|
||||
github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg=
|
||||
|
|
@ -376,7 +372,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
|
|
@ -487,7 +482,6 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
|
|||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
|
|
@ -543,7 +537,6 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
|
||||
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -558,7 +551,6 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb
|
|||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
|
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
|
@ -602,7 +594,6 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK
|
|||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8=
|
||||
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
|
|
@ -694,7 +685,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
|
@ -713,16 +703,19 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
|||
k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
|
||||
k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw=
|
||||
k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8=
|
||||
k8s.io/apiextensions-apiserver v0.20.1 h1:ZrXQeslal+6zKM/HjDXLzThlz/vPSxrfK3OqL8txgVQ=
|
||||
k8s.io/apiextensions-apiserver v0.20.1/go.mod h1:ntnrZV+6a3dB504qwC5PN/Yg9PBiDNt1EVqbW2kORVk=
|
||||
k8s.io/apiextensions-apiserver v0.20.2 h1:rfrMWQ87lhd8EzQWRnbQ4gXrniL/yTRBgYH1x1+BLlo=
|
||||
k8s.io/apiextensions-apiserver v0.20.2/go.mod h1:F6TXp389Xntt+LUq3vw6HFOLttPa0V8821ogLGwb6Zs=
|
||||
k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
|
||||
k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg=
|
||||
k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
|
||||
k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
|
||||
k8s.io/apiserver v0.20.2/go.mod h1:2nKd93WyMhZx4Hp3RfgH2K5PhwyTrprrkWYnI7id7jA=
|
||||
k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
|
||||
k8s.io/client-go v0.20.2 h1:uuf+iIAbfnCSw8IGAv/Rg0giM+2bOzHLOsbbrwrdhNQ=
|
||||
k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE=
|
||||
k8s.io/code-generator v0.20.1/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
|
||||
k8s.io/code-generator v0.20.2/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
|
||||
k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
|
||||
k8s.io/component-base v0.20.2 h1:LMmu5I0pLtwjpp5009KLuMGFqSc2S2isGw8t1hpYKLE=
|
||||
k8s.io/component-base v0.20.2/go.mod h1:pzFtCiwe/ASD0iV7ySMu8SYVJjCapNM9bjvk7ptpKh0=
|
||||
|
|
|
|||
|
|
@ -113,18 +113,23 @@ type ReplicaStruct struct {
|
|||
|
||||
// HTTPScaledObjectSpec defines the desired state of HTTPScaledObject
|
||||
type HTTPScaledObjectSpec struct {
|
||||
// (optional) The name of the application to be created.
|
||||
AppName string `json:"app_name,omitempty"`
|
||||
// The image this application will use.
|
||||
Image string `json:"app_image"`
|
||||
// The port this application will serve on.
|
||||
Port int32 `json:"port"`
|
||||
// The name of the deployment to route HTTP requests to (and to autoscale). Either this
|
||||
// or Image must be set
|
||||
ScaleTargetRef *ScaleTargetRef `json:"scaleTargetRef"`
|
||||
// (optional) Replica information
|
||||
//+optional
|
||||
Replicas ReplicaStruct `json:"replicas,omitempty"`
|
||||
}
|
||||
|
||||
// TODO: Add ingress configurations
|
||||
// ScaleTargetRef contains all the details about an HTTP application to scale and route to
|
||||
type ScaleTargetRef struct {
|
||||
// The name of the deployment to scale according to HTTP traffic
|
||||
Deployment string `json:"deployment"`
|
||||
// The name of the service to route to
|
||||
Service string `json:"service"`
|
||||
// The port to route to
|
||||
Port int32 `json:"port"`
|
||||
}
|
||||
|
||||
// HTTPScaledObjectStatus defines the observed state of HTTPScaledObject
|
||||
type HTTPScaledObjectStatus struct {
|
||||
|
|
|
|||
|
|
@ -32,16 +32,6 @@ spec:
|
|||
spec:
|
||||
description: HTTPScaledObjectSpec defines the desired state of HTTPScaledObject
|
||||
properties:
|
||||
app_image:
|
||||
description: The image this application will use.
|
||||
type: string
|
||||
app_name:
|
||||
description: (optional) The name of the application to be created.
|
||||
type: string
|
||||
port:
|
||||
description: The port this application will serve on.
|
||||
format: int32
|
||||
type: integer
|
||||
replicas:
|
||||
description: (optional) Replica information
|
||||
properties:
|
||||
|
|
@ -54,9 +44,26 @@ spec:
|
|||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
scaleTargetRef:
|
||||
description: The name of the deployment to route HTTP requests to (and to autoscale). Either this or Image must be set
|
||||
properties:
|
||||
deployment:
|
||||
description: The name of the deployment to scale according to HTTP traffic
|
||||
type: string
|
||||
port:
|
||||
description: The port to route to
|
||||
format: int32
|
||||
type: integer
|
||||
service:
|
||||
description: The name of the service to route to
|
||||
type: string
|
||||
required:
|
||||
- deployment
|
||||
- port
|
||||
- service
|
||||
type: object
|
||||
required:
|
||||
- app_image
|
||||
- port
|
||||
- scaleTargetRef
|
||||
type: object
|
||||
status:
|
||||
description: HTTPScaledObjectStatus defines the observed state of HTTPScaledObject
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ apiVersion: rbac.authorization.k8s.io/v1
|
|||
kind: ClusterRole
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: keda-http-manager-role
|
||||
name: keda-http-addon-manager-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
|
|
|
|||
|
|
@ -1,36 +1,57 @@
|
|||
package config
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kedacore/http-add-on/operator/api/v1alpha1"
|
||||
)
|
||||
|
||||
// DeploymentName is a convenience function for
|
||||
// a.HTTPScaledObject.Spec.ScaleTargetRef.Deployment
|
||||
func DeploymentName(httpso v1alpha1.HTTPScaledObject) string {
|
||||
return httpso.Spec.ScaleTargetRef.Deployment
|
||||
}
|
||||
|
||||
// AppInfo contains configuration for the Interceptor and External Scaler, and holds
|
||||
// data about the name and namespace of the scale target.
|
||||
type AppInfo struct {
|
||||
Name string
|
||||
Port int32
|
||||
Image string
|
||||
Namespace string
|
||||
InterceptorConfig Interceptor
|
||||
ExternalScalerConfig ExternalScaler
|
||||
}
|
||||
|
||||
// ExternalScalerServiceName is a convenience method to get the name of the external scaler
|
||||
// service in Kubernetes
|
||||
func (a AppInfo) ExternalScalerServiceName() string {
|
||||
return fmt.Sprintf("%s-external-scaler", a.Name)
|
||||
}
|
||||
|
||||
// ExternalScalerDeploymentName is a convenience method to get the name of the external scaler
|
||||
// deployment in Kubernetes
|
||||
func (a AppInfo) ExternalScalerDeploymentName() string {
|
||||
return fmt.Sprintf("%s-external-scaler", a.Name)
|
||||
}
|
||||
|
||||
// InterceptorAdminServiceName is a convenience method to get the name of the interceptor
|
||||
// service for the admin endpoints in Kubernetes
|
||||
func (a AppInfo) InterceptorAdminServiceName() string {
|
||||
return fmt.Sprintf("%s-interceptor-admin", a.Name)
|
||||
}
|
||||
|
||||
// InterceptorProxyServiceName is a convenience method to get the name of the interceptor
|
||||
// service for the proxy in Kubernetes
|
||||
func (a AppInfo) InterceptorProxyServiceName() string {
|
||||
return fmt.Sprintf("%s-interceptor-proxy", a.Name)
|
||||
}
|
||||
|
||||
// InterceptorDeploymentName is a convenience method to get the name of the interceptor
|
||||
// deployment in Kubernetes
|
||||
func (a AppInfo) InterceptorDeploymentName() string {
|
||||
return fmt.Sprintf("%s-interceptor", a.Name)
|
||||
}
|
||||
|
||||
// ScaledObjectName is a convenience method to get the name of the scaled object in Kubernetes
|
||||
func (a AppInfo) ScaledObjectName() string {
|
||||
return fmt.Sprintf("%s-scaled-object", a.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,52 +1,36 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
logrtest "github.com/go-logr/logr/testing"
|
||||
"github.com/kedacore/http-add-on/operator/api/v1alpha1"
|
||||
"github.com/kedacore/http-add-on/operator/controllers/config"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
)
|
||||
|
||||
var _ = Describe("ExternalScaler", func() {
|
||||
Context("Creating the external scaler", func() {
|
||||
var testInfra *commonTestInfra
|
||||
BeforeEach(func() {
|
||||
testInfra = newCommonTestInfra("testns", "testapp")
|
||||
})
|
||||
It("Should properly create the Deployment and Service", func() {
|
||||
const name = "testapp"
|
||||
const namespace = "testns"
|
||||
ctx := context.Background()
|
||||
cl := fake.NewFakeClient()
|
||||
cfg := config.AppInfo{
|
||||
Name: name,
|
||||
Port: 8081,
|
||||
Image: "arschles/testimg",
|
||||
Namespace: namespace,
|
||||
}
|
||||
logger := logrtest.NullLogger{}
|
||||
httpso := &v1alpha1.HTTPScaledObject{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
},
|
||||
Spec: v1alpha1.HTTPScaledObjectSpec{
|
||||
AppName: name,
|
||||
Image: "arschles/testapp",
|
||||
Port: 8081,
|
||||
},
|
||||
}
|
||||
err := createExternalScaler(ctx, cfg, cl, logger, httpso)
|
||||
err := createExternalScaler(
|
||||
testInfra.ctx,
|
||||
testInfra.cfg,
|
||||
testInfra.cl,
|
||||
testInfra.logger,
|
||||
&testInfra.httpso,
|
||||
)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
// // make sure that httpso has the right conditions on it
|
||||
Expect(len(httpso.Status.Conditions)).To(Equal(1))
|
||||
cond1 := httpso.Status.Conditions[0]
|
||||
Expect(len(testInfra.httpso.Status.Conditions)).To(Equal(1))
|
||||
cond1 := testInfra.httpso.Status.Conditions[0]
|
||||
cond1ts, err := time.Parse(time.RFC3339, cond1.Timestamp)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(time.Now().Sub(cond1ts) >= 0).To(BeTrue())
|
||||
|
|
@ -56,17 +40,17 @@ var _ = Describe("ExternalScaler", func() {
|
|||
|
||||
// check that the external scaler deployment was created
|
||||
deployment := new(appsv1.Deployment)
|
||||
err = cl.Get(ctx, client.ObjectKey{
|
||||
Name: cfg.ExternalScalerDeploymentName(),
|
||||
Namespace: cfg.Namespace,
|
||||
err = testInfra.cl.Get(testInfra.ctx, client.ObjectKey{
|
||||
Name: testInfra.cfg.ExternalScalerDeploymentName(),
|
||||
Namespace: testInfra.cfg.Namespace,
|
||||
}, deployment)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
// check that the external scaler service was created
|
||||
service := new(corev1.Service)
|
||||
err = cl.Get(ctx, client.ObjectKey{
|
||||
Name: cfg.ExternalScalerServiceName(),
|
||||
Namespace: cfg.Namespace,
|
||||
err = testInfra.cl.Get(testInfra.ctx, client.ObjectKey{
|
||||
Name: testInfra.cfg.ExternalScalerServiceName(),
|
||||
Namespace: testInfra.cfg.Namespace,
|
||||
}, service)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
|
|
|
|||
|
|
@ -76,14 +76,8 @@ func (rec *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.R
|
|||
}, err
|
||||
}
|
||||
|
||||
appName := httpso.Spec.AppName
|
||||
image := httpso.Spec.Image
|
||||
port := httpso.Spec.Port
|
||||
|
||||
appInfo := config.AppInfo{
|
||||
Name: appName,
|
||||
Port: port,
|
||||
Image: image,
|
||||
Name: httpso.Spec.ScaleTargetRef.Deployment,
|
||||
Namespace: req.Namespace,
|
||||
InterceptorConfig: rec.InterceptorConfig,
|
||||
ExternalScalerConfig: rec.ExternalScalerConfig,
|
||||
|
|
@ -114,7 +108,13 @@ func (rec *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.R
|
|||
}
|
||||
|
||||
// httpso is updated now
|
||||
logger.Info("Reconciling HTTPScaledObject", "Namespace", req.Namespace, "App Name", appName, "image", image, "port", port)
|
||||
logger.Info(
|
||||
"Reconciling HTTPScaledObject",
|
||||
"Namespace",
|
||||
req.Namespace,
|
||||
"DeploymentName",
|
||||
appInfo.Name,
|
||||
)
|
||||
|
||||
// Create required app objects for the application defined by the CRD
|
||||
if err := rec.createOrUpdateApplicationResources(
|
||||
|
|
|
|||
|
|
@ -206,11 +206,6 @@ func (rec *HTTPScaledObjectReconciler) createOrUpdateApplicationResources(
|
|||
// set initial statuses
|
||||
httpso.AddCondition(*v1alpha1.CreateCondition(v1alpha1.Pending, v1.ConditionUnknown, v1alpha1.PendingCreation).SetMessage("Identified HTTPScaledObject creation signal"))
|
||||
|
||||
// CREATING THE USER APPLICATION
|
||||
if err := createUserApp(ctx, appInfo, rec.Client, logger, httpso); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// CREATING INTERNAL ADD-ON OBJECTS
|
||||
// Creating the dedicated interceptor
|
||||
if err := createInterceptor(ctx, appInfo, rec.Client, logger, httpso); err != nil {
|
||||
|
|
@ -231,7 +226,5 @@ func (rec *HTTPScaledObjectReconciler) createOrUpdateApplicationResources(
|
|||
|
||||
}
|
||||
|
||||
// TODO: Create a new ingress resource that will point to the interceptor
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ func createInterceptor(
|
|||
},
|
||||
{
|
||||
Name: "KEDA_HTTP_APP_SERVICE_PORT",
|
||||
Value: fmt.Sprintf("%d", appInfo.Port),
|
||||
Value: fmt.Sprintf("%d", httpso.Spec.ScaleTargetRef.Port),
|
||||
},
|
||||
{
|
||||
Name: "KEDA_HTTP_PROXY_PORT",
|
||||
|
|
@ -77,7 +77,7 @@ func createInterceptor(
|
|||
appInfo.Namespace,
|
||||
appInfo.InterceptorProxyServiceName(),
|
||||
publicPorts,
|
||||
corev1.ServiceTypeLoadBalancer,
|
||||
corev1.ServiceTypeClusterIP,
|
||||
k8s.Labels(appInfo.InterceptorDeploymentName()),
|
||||
)
|
||||
adminPorts := []corev1.ServicePort{
|
||||
|
|
|
|||
|
|
@ -30,10 +30,12 @@ func createScaledObject(
|
|||
|
||||
logger.Info("Creating scaled object", "external_scaler", externalScalerHostname)
|
||||
|
||||
deploymentName := httpso.Spec.ScaleTargetRef.Deployment
|
||||
|
||||
coreScaledObject := k8s.NewScaledObject(
|
||||
appInfo.Namespace,
|
||||
appInfo.ScaledObjectName(),
|
||||
appInfo.Name,
|
||||
deploymentName,
|
||||
externalScalerHostname,
|
||||
httpso.Spec.Replicas.Min,
|
||||
httpso.Spec.Replicas.Max,
|
||||
|
|
|
|||
|
|
@ -1,59 +1,39 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
logrtest "github.com/go-logr/logr/testing"
|
||||
"github.com/kedacore/http-add-on/operator/api/v1alpha1"
|
||||
"github.com/kedacore/http-add-on/operator/controllers/config"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
)
|
||||
|
||||
var _ = Describe("UserApp", func() {
|
||||
Context("Creating a ScaledObject", func() {
|
||||
var testInfra *commonTestInfra
|
||||
BeforeEach(func() {
|
||||
testInfra = newCommonTestInfra("testns", "testapp")
|
||||
})
|
||||
It("Should properly create the ScaledObject for the user app", func() {
|
||||
const name = "testapp"
|
||||
const namespace = "testns"
|
||||
ctx := context.Background()
|
||||
cl := fake.NewFakeClient()
|
||||
cfg := config.AppInfo{
|
||||
Name: name,
|
||||
Port: 8081,
|
||||
Image: "arschles/testimg",
|
||||
Namespace: namespace,
|
||||
}
|
||||
logger := logrtest.NullLogger{}
|
||||
httpso := &v1alpha1.HTTPScaledObject{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
},
|
||||
Spec: v1alpha1.HTTPScaledObjectSpec{
|
||||
AppName: name,
|
||||
Image: "arschles/testapp",
|
||||
Port: 8081,
|
||||
Replicas: v1alpha1.ReplicaStruct{
|
||||
Min: 5,
|
||||
Max: 10,
|
||||
},
|
||||
},
|
||||
}
|
||||
err := createScaledObject(ctx, cfg, cl, logger, httpso)
|
||||
err := createScaledObject(
|
||||
testInfra.ctx,
|
||||
testInfra.cfg,
|
||||
testInfra.cl,
|
||||
testInfra.logger,
|
||||
&testInfra.httpso,
|
||||
)
|
||||
Expect(err).To(BeNil())
|
||||
// make sure that httpso has the right conditions on it
|
||||
Expect(len(httpso.Status.Conditions)).To(Equal(1))
|
||||
Expect(len(testInfra.httpso.Status.Conditions)).To(Equal(1))
|
||||
|
||||
cond1 := httpso.Status.Conditions[0]
|
||||
cond1 := testInfra.httpso.Status.Conditions[0]
|
||||
cond1ts, err := time.Parse(time.RFC3339, cond1.Timestamp)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(time.Now().Sub(cond1ts) >= 0).To(BeTrue())
|
||||
Expect(time.Since(cond1ts) >= 0).To(BeTrue())
|
||||
Expect(cond1.Type).To(Equal(v1alpha1.Created))
|
||||
Expect(cond1.Status).To(Equal(metav1.ConditionTrue))
|
||||
Expect(cond1.Reason).To(Equal(v1alpha1.ScaledObjectCreated))
|
||||
|
|
@ -65,23 +45,23 @@ var _ = Describe("UserApp", func() {
|
|||
Kind: "ScaledObject",
|
||||
Version: "v1alpha1",
|
||||
})
|
||||
err = cl.Get(ctx, client.ObjectKey{
|
||||
Namespace: cfg.Namespace,
|
||||
Name: cfg.ScaledObjectName(),
|
||||
err = testInfra.cl.Get(testInfra.ctx, client.ObjectKey{
|
||||
Namespace: testInfra.cfg.Namespace,
|
||||
Name: testInfra.cfg.ScaledObjectName(),
|
||||
}, u)
|
||||
Expect(err).To(BeNil())
|
||||
metadataIface, found := u.Object["metadata"]
|
||||
metadata, ok := metadataIface.(map[string]interface{})
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(metadata["namespace"]).To(Equal(namespace))
|
||||
Expect(metadata["name"]).To(Equal(cfg.ScaledObjectName()))
|
||||
Expect(metadata["namespace"]).To(Equal(testInfra.ns))
|
||||
Expect(metadata["name"]).To(Equal(testInfra.cfg.ScaledObjectName()))
|
||||
specIFace, found := u.Object["spec"]
|
||||
spec, ok := specIFace.(map[string]interface{})
|
||||
Expect(found).To(BeTrue())
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(spec["minReplicaCount"]).To(BeNumerically("==", httpso.Spec.Replicas.Min))
|
||||
Expect(spec["maxReplicaCount"]).To(BeNumerically("==", httpso.Spec.Replicas.Max))
|
||||
Expect(spec["minReplicaCount"]).To(BeNumerically("==", testInfra.httpso.Spec.Replicas.Min))
|
||||
Expect(spec["maxReplicaCount"]).To(BeNumerically("==", testInfra.httpso.Spec.Replicas.Max))
|
||||
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -16,16 +16,24 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
logrtest "github.com/go-logr/logr/testing"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
|
||||
"github.com/kedacore/http-add-on/operator/api/v1alpha1"
|
||||
httpv1alpha1 "github.com/kedacore/http-add-on/operator/api/v1alpha1"
|
||||
"github.com/kedacore/http-add-on/operator/controllers/config"
|
||||
// +kubebuilder:scaffold:imports
|
||||
)
|
||||
|
||||
|
|
@ -44,6 +52,53 @@ func TestAPIs(t *testing.T) {
|
|||
[]Reporter{printer.NewlineReporter{}})
|
||||
}
|
||||
|
||||
type commonTestInfra struct {
|
||||
ns string
|
||||
appName string
|
||||
ctx context.Context
|
||||
cl client.Client
|
||||
cfg config.AppInfo
|
||||
logger logr.Logger
|
||||
httpso v1alpha1.HTTPScaledObject
|
||||
}
|
||||
|
||||
func newCommonTestInfra(namespace, appName string) *commonTestInfra {
|
||||
ctx := context.Background()
|
||||
cl := fake.NewFakeClient()
|
||||
cfg := config.AppInfo{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
}
|
||||
logger := logrtest.NullLogger{}
|
||||
httpso := v1alpha1.HTTPScaledObject{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: appName,
|
||||
},
|
||||
Spec: v1alpha1.HTTPScaledObjectSpec{
|
||||
ScaleTargetRef: &v1alpha1.ScaleTargetRef{
|
||||
Deployment: appName,
|
||||
Service: appName,
|
||||
Port: 8081,
|
||||
},
|
||||
Replicas: httpv1alpha1.ReplicaStruct{
|
||||
Min: 0,
|
||||
Max: 20,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return &commonTestInfra{
|
||||
ns: namespace,
|
||||
appName: appName,
|
||||
ctx: ctx,
|
||||
cl: cl,
|
||||
cfg: cfg,
|
||||
logger: logger,
|
||||
httpso: httpso,
|
||||
}
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
// The commented code in this function connects to a test Kubernetes cluster.
|
||||
// We don't currently have tests that exercise functionality that needs a cluster,
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/kedacore/http-add-on/operator/api/v1alpha1"
|
||||
"github.com/kedacore/http-add-on/operator/controllers/config"
|
||||
"github.com/kedacore/http-add-on/pkg/k8s"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
func createUserApp(
|
||||
ctx context.Context,
|
||||
appInfo config.AppInfo,
|
||||
cl client.Client,
|
||||
logger logr.Logger,
|
||||
httpso *v1alpha1.HTTPScaledObject,
|
||||
) error {
|
||||
deployment := k8s.NewDeployment(
|
||||
appInfo.Namespace,
|
||||
appInfo.Name,
|
||||
appInfo.Image,
|
||||
[]int32{appInfo.Port},
|
||||
[]corev1.EnvVar{},
|
||||
k8s.Labels(appInfo.Name),
|
||||
)
|
||||
logger.Info("Creating app deployment", "deployment", *deployment)
|
||||
if err := cl.Create(ctx, deployment); err != nil {
|
||||
if errors.IsAlreadyExists(err) {
|
||||
logger.Info("User app deployment already exists, moving on")
|
||||
} else {
|
||||
logger.Error(err, "Creating deployment")
|
||||
condition := v1alpha1.CreateCondition(v1alpha1.Error, v1.ConditionFalse, v1alpha1.ErrorCreatingAppDeployment).SetMessage(err.Error())
|
||||
httpso.AddCondition(*condition)
|
||||
return err
|
||||
}
|
||||
}
|
||||
httpso.AddCondition(*v1alpha1.CreateCondition(v1alpha1.Created, v1.ConditionTrue, v1alpha1.AppDeploymentCreated).SetMessage("App deployment created"))
|
||||
|
||||
servicePorts := []corev1.ServicePort{
|
||||
k8s.NewTCPServicePort("http", 8080, appInfo.Port),
|
||||
}
|
||||
service := k8s.NewService(
|
||||
appInfo.Namespace,
|
||||
appInfo.Name,
|
||||
servicePorts,
|
||||
corev1.ServiceTypeClusterIP,
|
||||
k8s.Labels(appInfo.Name),
|
||||
)
|
||||
if err := cl.Create(ctx, service); err != nil {
|
||||
if errors.IsAlreadyExists(err) {
|
||||
logger.Info("User app service already exists, moving on")
|
||||
} else {
|
||||
logger.Error(err, "Creating service")
|
||||
condition := v1alpha1.CreateCondition(v1alpha1.Error, v1.ConditionFalse, v1alpha1.ErrorCreatingAppService).SetMessage(err.Error())
|
||||
httpso.AddCondition(*condition)
|
||||
return err
|
||||
}
|
||||
}
|
||||
httpso.AddCondition(*v1alpha1.CreateCondition(v1alpha1.Created, v1.ConditionTrue, v1alpha1.AppServiceCreated).SetMessage("App service created"))
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
logrtest "github.com/go-logr/logr/testing"
|
||||
"github.com/kedacore/http-add-on/operator/api/v1alpha1"
|
||||
"github.com/kedacore/http-add-on/operator/controllers/config"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
)
|
||||
|
||||
var _ = Describe("UserApp", func() {
|
||||
Context("Creating a user app", func() {
|
||||
It("Should properly create a deployment and a service", func() {
|
||||
ctx := context.Background()
|
||||
cl := fake.NewFakeClient()
|
||||
cfg := config.AppInfo{
|
||||
Name: "testapp",
|
||||
Port: 8081,
|
||||
Image: "arschles/testimg",
|
||||
Namespace: "testns",
|
||||
}
|
||||
logger := logrtest.NullLogger{}
|
||||
httpso := &v1alpha1.HTTPScaledObject{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "testns",
|
||||
Name: "testapp",
|
||||
},
|
||||
Spec: v1alpha1.HTTPScaledObjectSpec{
|
||||
AppName: "testname",
|
||||
Image: "arschles/testapp",
|
||||
Port: 8081,
|
||||
},
|
||||
}
|
||||
err := createUserApp(ctx, cfg, cl, logger, httpso)
|
||||
Expect(err).To(BeNil())
|
||||
// make sure that httpso has the right conditions on it
|
||||
Expect(len(httpso.Status.Conditions)).To(Equal(2))
|
||||
|
||||
cond1 := httpso.Status.Conditions[0]
|
||||
cond1ts, err := time.Parse(time.RFC3339, cond1.Timestamp)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(time.Now().Sub(cond1ts) >= 0).To(BeTrue())
|
||||
Expect(cond1.Type).To(Equal(v1alpha1.Created))
|
||||
Expect(cond1.Status).To(Equal(metav1.ConditionTrue))
|
||||
Expect(cond1.Reason).To(Equal(v1alpha1.AppDeploymentCreated))
|
||||
|
||||
cond2 := httpso.Status.Conditions[1]
|
||||
cond2ts, err := time.Parse(time.RFC3339, cond2.Timestamp)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(time.Now().Sub(cond2ts) >= 0).To(BeTrue())
|
||||
Expect(cond2.Type).To(Equal(v1alpha1.Created))
|
||||
Expect(cond2.Status).To(Equal(metav1.ConditionTrue))
|
||||
Expect(cond2.Reason).To(Equal(v1alpha1.AppServiceCreated))
|
||||
|
||||
// check the deployment that was created
|
||||
deployment := &appsv1.Deployment{}
|
||||
err = cl.Get(ctx, client.ObjectKey{
|
||||
Name: cfg.Name,
|
||||
Namespace: cfg.Namespace,
|
||||
}, deployment)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(deployment.Name).To(Equal(cfg.Name))
|
||||
Expect(len(deployment.Spec.Template.Spec.Containers)).To(Equal(1))
|
||||
Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(Equal(cfg.Image))
|
||||
|
||||
// check the service that was created
|
||||
svc := &corev1.Service{}
|
||||
err = cl.Get(ctx, client.ObjectKey{
|
||||
Name: cfg.Name,
|
||||
Namespace: cfg.Namespace,
|
||||
}, svc)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(svc.Name).To(Equal(cfg.Name))
|
||||
Expect(len(svc.Spec.Ports)).To(Equal(1))
|
||||
Expect(svc.Spec.Ports[0].Protocol).To(Equal(corev1.ProtocolTCP))
|
||||
Expect(svc.Spec.Ports[0].TargetPort.IntVal).To(Equal(cfg.Port))
|
||||
Expect(svc.Spec.Ports[0].TargetPort.Type).To(Equal(intstr.Int))
|
||||
Expect(svc.Spec.Ports[0].Port).To(Equal(int32(8080)))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -25,6 +25,8 @@ func DeleteService(ctx context.Context, name string, cl k8scorev1.ServiceInterfa
|
|||
return cl.Delete(ctx, name, metav1.DeleteOptions{})
|
||||
}
|
||||
|
||||
// NewService creates a new Service object in memory according to the input parameters.
|
||||
// This function operates in memory only and doesn't do any I/O whatsoever.
|
||||
func NewService(
|
||||
namespace,
|
||||
name string,
|
||||
|
|
@ -43,11 +45,8 @@ func NewService(
|
|||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: servicePorts,
|
||||
Selector: selector, //labels(name),
|
||||
// TODO: after switching to Ingress + Ingress controller, switch
|
||||
// this back to ClusterIP
|
||||
// Type: corev1.ServiceTypeClusterIP,
|
||||
Type: svcType,
|
||||
Selector: selector,
|
||||
Type: svcType,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue