Skip to content

Commit

Permalink
add ingress
Browse files Browse the repository at this point in the history
  • Loading branch information
test committed Oct 24, 2022
1 parent 8fed653 commit 947fc80
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 25 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

Dash controller is responsible to manage lifecycle of DashApplication objects.

## Local Kubernets

You can spin up kubernetes cluster using kind.
The following script deploy also load balancer and ingress controller.

```bash
$ example/kind/run-kind.sh
```

## Installation

Install CRD:
Expand Down
57 changes: 52 additions & 5 deletions apis/dash/v1alpha1/dashapplication_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@ func init() {
}

type DashApplicationSpec struct {
// Image name
Image string `json:"image"`
// ContainerPort port number for image container
ContainerPort int32 `json:"containerPort"`

// Container spec
Container Container `json:"container"`
// Number of desired pods. This is a pointer to distinguish between explicit
// zero and not specified. Defaults to 1.
// +optional
Expand All @@ -22,6 +19,56 @@ type DashApplicationSpec struct {
// +optional
// ServiceAnnotations for dash k8s service
ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"`
// Ingress spec. If not specified only LoadBalancer service is created
// +optional
Ingress *Ingress `json:"ingress,omitempty"`
}

// A single application container that you want to run.
type Container struct {
// Image name
Image string `json:"image"`
// Entrypoint array. Not executed within a shell.
// +optional
Command []string `json:"command,omitempty"`
// Arguments to the entrypoint.
// +optional
Args []string `json:"args,omitempty"`
// ContainerPort port number for image container
ContainerPort int32 `json:"containerPort"`
}

type Ingress struct {
// Host is the fully qualified domain name of a network host, as defined by RFC 3986.
// Note the following deviations from the "host" part of the
// URI as defined in RFC 3986:
// 1. IPs are not allowed. Currently an IngressRuleValue can only apply to
// the IP in the Spec of the parent Ingress.
// 2. The `:` delimiter is not respected because ports are not allowed.
// Currently the port of an Ingress is implicitly :80 for http and
// :443 for https.
// Both these may change in the future.
// Incoming requests are matched against the host before the
// IngressRuleValue. If the host is unspecified, the Ingress routes all
// traffic based on the specified IngressRuleValue.
//
// Host can be "precise" which is a domain name without the terminating dot of
// a network host (e.g. "foo.bar.com") or "wildcard", which is a domain name
// prefixed with a single wildcard label (e.g. "*.foo.com").
// The wildcard character '*' must appear by itself as the first DNS label and
// matches only a single label. You cannot have a wildcard label by itself (e.g. Host == "*").
// Requests will be matched against the Host field in the following way:
// 1. If Host is precise, the request matches this rule if the http host header is equal to Host.
// 2. If Host is a wildcard, then the request matches this rule if the http host header
// is to equal to the suffix (removing the first label) of the wildcard rule.
// +optional
Host string `json:"host,omitempty"`
// Path is matched against the path of an incoming request. Currently it can
// contain characters disallowed from the conventional "path" part of a URL
// as defined by RFC 3986. Paths must begin with a '/' and must be present
// when using PathType with value "Exact" or "Prefix".
// +optional
Path string `json:"path,omitempty"`
}

type DashApplicationStatus struct {
Expand Down
46 changes: 46 additions & 0 deletions apis/dash/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

70 changes: 61 additions & 9 deletions config/crd/bases/dash.plural.sh_dashapplications.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,66 @@ spec:
type: object
spec:
properties:
containerPort:
description: ContainerPort port number for image container
format: int32
type: integer
image:
description: Image name
type: string
container:
description: Container spec
properties:
args:
description: Arguments to the entrypoint.
items:
type: string
type: array
command:
description: Entrypoint array. Not executed within a shell.
items:
type: string
type: array
containerPort:
description: ContainerPort port number for image container
format: int32
type: integer
image:
description: Image name
type: string
required:
- containerPort
- image
type: object
ingress:
description: Ingress spec. If not specified only LoadBalancer service
is created
properties:
host:
description: "Host is the fully qualified domain name of a network
host, as defined by RFC 3986. Note the following deviations
from the \"host\" part of the URI as defined in RFC 3986: 1.
IPs are not allowed. Currently an IngressRuleValue can only
apply to the IP in the Spec of the parent Ingress. 2. The `:`
delimiter is not respected because ports are not allowed. Currently
the port of an Ingress is implicitly :80 for http and :443 for
https. Both these may change in the future. Incoming requests
are matched against the host before the IngressRuleValue. If
the host is unspecified, the Ingress routes all traffic based
on the specified IngressRuleValue. \n Host can be \"precise\"
which is a domain name without the terminating dot of a network
host (e.g. \"foo.bar.com\") or \"wildcard\", which is a domain
name prefixed with a single wildcard label (e.g. \"*.foo.com\").
The wildcard character '*' must appear by itself as the first
DNS label and matches only a single label. You cannot have a
wildcard label by itself (e.g. Host == \"*\"). Requests will
be matched against the Host field in the following way: 1. If
Host is precise, the request matches this rule if the http host
header is equal to Host. 2. If Host is a wildcard, then the
request matches this rule if the http host header is to equal
to the suffix (removing the first label) of the wildcard rule."
type: string
path:
description: Path is matched against the path of an incoming request.
Currently it can contain characters disallowed from the conventional
"path" part of a URL as defined by RFC 3986. Paths must begin
with a '/' and must be present when using PathType with value
"Exact" or "Prefix".
type: string
type: object
labels:
additionalProperties:
type: string
Expand All @@ -61,8 +114,7 @@ spec:
description: ServiceAnnotations for dash k8s service
type: object
required:
- containerPort
- image
- container
type: object
status:
properties:
Expand Down
9 changes: 5 additions & 4 deletions example/dash_picsum.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ metadata:
name: picsum
namespace: default
spec:
image: "zreigz/dash-picsum:0.1.0"
containerPort: 8050
replicas: 1
serviceAnnotations:
service.beta.kubernetes.io/aws-load-balancer-internal: "true"
container:
image: "zreigz/dash-picsum:0.1.0"
containerPort: 8050
ingress:
path: "\picsum"
8 changes: 7 additions & 1 deletion example/kind/run-kind.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,10 @@ retry 10 check_all_deployments_ready metallb-system
echodate "Load balancer is ready."
kubectl apply -f "$DATA_FILE"/metallb-config.yaml
echodate "Deploy CRD"
kubectl create -f config/crd/bases/
kubectl create -f config/crd/bases/
echodate "Deploy ingress"
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=90s
70 changes: 66 additions & 4 deletions pkg/controller/dash_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
dashv1alpha1 "github.com/pluralsh/dash-controller/apis/dash/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
Expand Down Expand Up @@ -58,6 +59,20 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
}
}

ingress := &networkingv1.Ingress{}
if err := r.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, ingress); err != nil {
if !apierrors.IsNotFound(err) {
return ctrl.Result{}, err
}
if dashApp.Spec.Ingress != nil {
log.Info("create ingress")
ingress = genIngress(dashApp)
if err := r.Create(ctx, ingress); err != nil {
return ctrl.Result{}, err
}
}
}

dashApp.Status.Ready = true
if err := r.Status().Update(ctx, dashApp); err != nil {
return ctrl.Result{}, err
Expand All @@ -76,7 +91,6 @@ func generateService(dashApp *dashv1alpha1.DashApplication) *corev1.Service {
Annotations: dashApp.Spec.ServiceAnnotations,
},
Spec: corev1.ServiceSpec{
Type: "LoadBalancer",
Selector: baseAppLabels(dashApp.Name, nil),
Ports: []corev1.ServicePort{{
Protocol: corev1.ProtocolTCP,
Expand All @@ -86,9 +100,55 @@ func generateService(dashApp *dashv1alpha1.DashApplication) *corev1.Service {
},
}

if dashApp.Spec.Ingress == nil {
svc.Spec.Type = "LoadBalancer"
}

return svc
}

func genIngress(dashApp *dashv1alpha1.DashApplication) *networkingv1.Ingress {
prefix := networkingv1.PathTypePrefix
path := "/"
if dashApp.Spec.Ingress.Path != "" {
path = dashApp.Spec.Ingress.Path
}
ingress := &networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: dashApp.Name,
Namespace: dashApp.Namespace,
Labels: baseAppLabels(dashApp.Name, nil),
},
Spec: networkingv1.IngressSpec{
Rules: []networkingv1.IngressRule{
{
Host: dashApp.Spec.Ingress.Host,
IngressRuleValue: networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
{
Path: path,
PathType: &prefix,
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
Name: dashApp.Name,
Port: networkingv1.ServiceBackendPort{
Number: 80,
},
},
},
},
},
},
},
},
},
},
}

return ingress
}

func genDeployment(dashApp *dashv1alpha1.DashApplication) *appsv1.Deployment {
name := dashApp.Name
deployment := &appsv1.Deployment{
Expand All @@ -109,11 +169,13 @@ func genDeployment(dashApp *dashv1alpha1.DashApplication) *appsv1.Deployment {
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: name,
Image: dashApp.Spec.Image,
Name: name,
Image: dashApp.Spec.Container.Image,
Args: dashApp.Spec.Container.Args,
Command: dashApp.Spec.Container.Command,
Ports: []corev1.ContainerPort{
{
ContainerPort: dashApp.Spec.ContainerPort,
ContainerPort: dashApp.Spec.Container.ContainerPort,
Protocol: corev1.ProtocolTCP,
Name: name,
},
Expand Down
2 changes: 1 addition & 1 deletion resources/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ spec:
serviceAccountName: dash-controller-sa
containers:
- name: dash-controller
image: ghcr.io/pluralsh/dash-controller:0.0.2
image: ghcr.io/pluralsh/dash-controller:0.0.3
imagePullPolicy: Always
2 changes: 1 addition & 1 deletion resources/rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ rules:
resources: ["dashapplications", "dashapplications/status"]
verbs: ["get", "list", "watch", "update", "create", "delete", "patch"]
- apiGroups: [""]
resources: ["events", "services"]
resources: ["events", "services", "ingresses"]
verbs: ["list", "watch", "create", "update", "patch", "get", "patch"]
- apiGroups: ["apps"]
resources: ["deployments"]
Expand Down

0 comments on commit 947fc80

Please sign in to comment.