Skip to content

Commit

Permalink
Add Python Guestbook Component Example (pulumi#689)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukehoban committed May 15, 2020
1 parent 265e9d1 commit ec3ccc0
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 3 deletions.
2 changes: 1 addition & 1 deletion aws-ts-stackreference/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ stacks via [StackReference](https://www.pulumi.com/docs/intro/concepts/organizin
```bash
$ cd company
$ npm install
````
```

1. Create a new stack:

Expand Down
1 change: 1 addition & 0 deletions kubernetes-py-guestbook/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ paket-files/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
venv

# Cake - Uncomment if you are using it
# tools/**
Expand Down
12 changes: 10 additions & 2 deletions kubernetes-py-guestbook/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/tree/master/kubernetes-py-guestbook/simple)
[![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/tree/master/kubernetes-ts-guestbook/components)

# Simple Guestbook App
# Simple and Component-based Kubernetes Guestbook Apps

A port of the standard [Kubernetes Guestbook](https://kubernetes.io/docs/tutorials/stateless-application/guestbook/)
to Pulumi. This example shows you how to build and deploy a simple, multi-tier web application using Kubernetes and
Expand All @@ -9,3 +9,11 @@ Docker, and consists of three components:
* A single-instance Redis master to store guestbook entries
* Multiple replicated Redis instances to serve reads
* Multiple web frontend instances

In this directory, you will find two variants of the Guestbook:

1. [simple/](./simple) is a straight port of the original YAML.
2. [components](./components) demonstrates benefits of using a real language, namely eliminating boilerplate through
the use of real component abstractions.

Both examples provision the exact same Kubernetes Guestbook application, but showcase different aspects of Pulumi.
3 changes: 3 additions & 0 deletions kubernetes-py-guestbook/components/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: kubernetes-py-guestbook
runtime: python
description: Kubernetes Guestbook example based on https://kubernetes.io/docs/tutorials/stateless-application/guestbook/
88 changes: 88 additions & 0 deletions kubernetes-py-guestbook/components/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
[![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new)

# Kubernetes Guestbook (with Components)

A version of the [Kubernetes Guestbook](https://kubernetes.io/docs/tutorials/stateless-application/guestbook/)
application using Pulumi. Unlike [the straight port of the original YAML](../simple), this variant
leverages real code to eliminate boilerplate. A `ServiceDeployment` class is used that combines the common pattern
of deploying a container image using a Kubernetes `Deployment`, and then scaling it using a `Service`.

## Running the App

Follow the steps in [Pulumi Installation](https://www.pulumi.com/docs/get-started/install/) and [Kubernetes Setup](https://www.pulumi.com/docs/intro/cloud-providers/kubernetes/setup/) to get Pulumi working with Kubernetes.

Create a new stack:

```sh
$ pulumi stack init
Enter a stack name: testbook
```

This example will attempt to expose the Guestbook application to the Internet with a `Service` of
type `LoadBalancer`. Since minikube does not support `LoadBalancer`, the Guestbook application
already knows to use type `ClusterIP` instead; all you need to do is to tell it whether you're
deploying to minikube:

```sh
pulumi config set isMinikube <value>
```

Perform the deployment:

```sh
$ pulumi up
Previewing update (guestbook):

Type Name Plan
+ pulumi:pulumi:Stack guestbook-easy-guestbook create
+ ├─ k8sx:component:ServiceDeployment frontend create
+ │ ├─ kubernetes:apps:Deployment frontend create
+ │ └─ kubernetes:core:Service frontend create
+ ├─ k8sx:component:ServiceDeployment redis-replica create
+ │ ├─ kubernetes:apps:Deployment redis-replica create
+ │ └─ kubernetes:core:Service redis-replica create
+ └─ k8sx:component:ServiceDeployment redis-master create
+ ├─ kubernetes:apps:Deployment redis-master create
+ └─ kubernetes:core:Service redis-master create

Resources:
+ 10 to create

Do you want to perform this update? yes
Updating (guestbook):

Type Name Status
+ pulumi:pulumi:Stack guestbook-easy-guestbook created
+ ├─ k8sx:component:ServiceDeployment redis-master created
+ │ ├─ kubernetes:apps:Deployment redis-master created
+ │ └─ kubernetes:core:Service redis-master created
+ ├─ k8sx:component:ServiceDeployment frontend created
+ │ ├─ kubernetes:apps:Deployment frontend created
+ │ └─ kubernetes:core:Service frontend created
+ └─ k8sx:component:ServiceDeployment redis-replica created
+ ├─ kubernetes:apps:Deployment redis-replica created
+ └─ kubernetes:core:Service redis-replica created

Outputs:
frontend_ip: "10.105.48.30"

Resources:
+ 10 created

Duration: 21s

Permalink: https://app.pulumi.com/acmecorp/k8sjs-guestbook/updates/1
```

And finally - open the application in your browser to see the running application. If you're running
macOS you can simply run:

```sh
open $(pulumi stack output frontend_ip)
```

> _Note_: minikube does not support type `LoadBalancer`; if you are deploying to minikube, make sure
> to run `kubectl port-forward svc/frontend 8080:80` to forward the cluster port to the local
> machine and access the service via `localhost:8080`.
![Guestbook in browser](./imgs/guestbook.png)
39 changes: 39 additions & 0 deletions kubernetes-py-guestbook/components/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2016-2020, Pulumi Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http:https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import pulumi
from service_deployment import ServiceDeployment

# Minikube does not implement services of type `LoadBalancer`; require the user to specify if we're
# running on minikube, and if so, create only services of type ClusterIP.
config = pulumi.Config()
isMinikube = config.get_bool("isMinikube")

ServiceDeployment(
"redis-master",
image="k8s.gcr.io/redis:e2e",
ports=[6379])
ServiceDeployment(
"redis-slave",
image="gcr.io/google_samples/gb-redisslave:v1",
ports=[6379])
frontend = ServiceDeployment(
"frontend",
image="gcr.io/google-samples/gb-frontend:v4",
replicas=3,
ports=[80],
allocate_ip_address=True,
is_minikube=config.get_bool("isMinikube"))

pulumi.export("frontend_ip", frontend.ip_address)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions kubernetes-py-guestbook/components/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pulumi>=2.0.0,<3.0.0
pulumi-kubernetes>=2.0.0,<3.0.0
70 changes: 70 additions & 0 deletions kubernetes-py-guestbook/components/service_deployment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright 2016-2020, Pulumi Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http:https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import List

import pulumi
from pulumi import ResourceOptions, ComponentResource, Output
from pulumi_kubernetes.apps.v1 import Deployment
from pulumi_kubernetes.core.v1 import Service, Namespace


class ServiceDeployment(ComponentResource):
deployment: Deployment
service: Service
ip_address: Output[str]

def __init__(self, name: str, image: str,
resources: dict = None, replicas: int = None,
ports: List[int] = None, allocate_ip_address: bool = None,
is_minikube: bool = None, opts: ResourceOptions = None):
super().__init__('k8sx:component:ServiceDeployment', name, {}, opts)

labels = {"app": name}
container = {
"name": name,
"image": image,
"resources": resources or {"requests": {"cpu": "100m", "memory": "100Mi"}},
"ports": [{"container_port": p} for p in ports] if ports else None,
}
self.deployment = Deployment(
name,
spec={
"selector": {"match_labels": labels},
"replicas": 1,
"template": {
"metadata": {"labels": labels},
"spec": {"containers": [container]},
},
},
opts=pulumi.ResourceOptions(parent=self))
self.service = Service(
name,
metadata={
"name": name,
"labels": self.deployment.metadata['labels'],
},
spec={
"ports": [{"port": p, "targetPort": p} for p in ports] if ports else None,
"selector": self.deployment.spec['template']['metadata']['labels'],
"type": ("ClusterIP" if is_minikube else "LoadBalancer") if allocate_ip_address else None,
},
opts=pulumi.ResourceOptions(parent=self))
if allocate_ip_address:
if is_minikube:
self.ip_address = self.service.spec['clusterIP']
else:
ingress=self.service.status['load_balancer']['ingress'][0]
self.ip_address = ingress.apply(lambda i: ingress["ip"] if "ip" in i else ingress['hostname'])
self.register_outputs({})

0 comments on commit ec3ccc0

Please sign in to comment.