[release-1.0] Move community docs onto MkDocs site (#4559)

* Move community docs onto MkDocs site

* Edit about community processes

* Will this fix the tests?

* Add redirects

* Fix broken redirects

* Change folder name and fix samples

* lint

* more lint

* Fix code snippet

* style tweak

* Add signing the CLA as a prereq and update serving issue link

* Capitalization

* Clarify roles

* lint

Co-authored-by: snneji <snneji@vmware.com>
This commit is contained in:
Knative Prow Robot 2021-12-13 07:21:19 -08:00 committed by GitHub
parent 233dad36a9
commit 22ee342cf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
168 changed files with 23255 additions and 556 deletions

7
code-samples/README.md Normal file
View File

@ -0,0 +1,7 @@
# Knative code samples
Find and use Knative code samples to help you get up and running with common use
cases. Code samples include content from the Knative team and community members.
Browse all code samples to find other languages and use cases that might align
closer with your goals.

View File

@ -0,0 +1,42 @@
# Community code samples
Get up and running with one of the community code samples. These samples are
contributed and maintained by members of the Knative community.
!!! note
These samples might become outdated or the original author might be unable
to maintain their contribution. If you find that something isn't working,
lend a helping hand and fix it in a PR.
[Learn more about the lifespan of samples](https://github.com/knative/docs/blob/main/contribute-to-docs/what-to-contribute/creating-code-samples.md#user-focused-content)
[**See all Knative code samples**](../README.md)
## Interactive Serving sample
Check out this [Katacoda tutorial](https://www.katacoda.com/swapb/scenarios/knative-intro)
which guides you through installing Knative and the `kn` command line tool, deploying a
sample container, updating your deployment, and performing a traffic split
between the two versions.
## Serving samples
Knative Serving sample apps.
| Sample Name | Description | Language(s) |
| ----------- | ----------- | ----------- |
| Hello World | A quick introduction to Knative Serving that highlights how to deploy an app. | [Clojure](serving/helloworld-clojure/), [Dart](serving/helloworld-dart/), [Elixir](serving/helloworld-elixir/), [Haskell](serving/helloworld-haskell/), [Java - Micronaut](serving/helloworld-java-micronaut/), [Java - Quarkus](serving/helloworld-java-quarkus/), [R - Go Server](serving/helloworld-r/), [Rust](serving/helloworld-rust/), [Swift](serving/helloworld-swift/), [Vertx](serving/helloworld-vertx/) |
| Machine Learning | A quick introduction to using Knative Serving to serve machine learning models | [Python - BentoML](serving/machinelearning-python-bentoml)
### Eventing and Eventing Resources samples
_Be the first to contribute an Eventing or Eventing Sources code sample to the
community collection._
## Client samples
Knative `kn` Client sample workflows and apps.
| Sample Name | Description |
| ----------- | ----------- |
| [knfun](https://github.com/maximilien/knfun) | Knative micro-functions (Twitter and Watson APIs) demo using the `kn` client. |

View File

@ -0,0 +1,3 @@
# Knative Serving code samples
Community contributed and maintained code samples for Knative Serving.

View File

@ -0,0 +1,14 @@
pom.xml
pom.xml.asc
*.jar
*.class
/lib/
/classes/
/target/
/checkouts/
.lein-deps-sum
.lein-repl-history
.lein-plugins/
.lein-failures
.nrepl-port
.cpcache/

View File

@ -0,0 +1,17 @@
# Use the official Clojure image.
# https://hub.docker.com/_/clojure
FROM clojure
# Create the project and download dependencies.
WORKDIR /usr/src/app
COPY project.clj .
RUN lein deps
# Copy local code to the container image.
COPY . .
# Build an uberjar release artifact.
RUN mv "$(lein uberjar | sed -n 's/^Created \(.*standalone\.jar\)/\1/p')" app-standalone.jar
# Run the web service on container startup.
CMD ["java", "-jar", "app-standalone.jar"]

View File

@ -0,0 +1,157 @@
# Hello World - Clojure
A simple web app written in Clojure that you can use for testing. It reads in an
env variable `TARGET` and prints "Hello \${TARGET}!". If TARGET is not
specified, it will use "World" as the TARGET.
## Prerequisites
- A Kubernetes cluster with Knative installed and DNS configured. Follow the
[Knative installation instructions](https://knative.dev/docs/install/) if you need to create
one.
- [Docker](https://www.docker.com) installed and running on your local machine,
and a Docker Hub account configured (we'll use it for a container registry).
## Recreating the sample code
While you can clone all of the code from this directory, hello world apps are
generally more useful if you build them step-by-step. The following instructions
recreate the source files from this folder.
1. Create a new file named `src/helloworld/core.clj` and paste the following
code. This code creates a basic web server which listens on port 8080:
```clojure
(ns helloworld.core
(:use ring.adapter.jetty)
(:gen-class))
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/html"}
:body (str "Hello "
(if-let [target (System/getenv "TARGET")]
target
"World")
"!\n")})
(defn -main [& args]
(run-jetty handler {:port (if-let [port (System/getenv "PORT")]
(Integer/parseInt port)
8080)}))
```
1. In your project directory, create a file named `project.clj` and copy the
code below into it. This code defines the project's dependencies and
entrypoint.
```clojure
(defproject helloworld "1.0.0-SNAPSHOT"
:description "Hello World - Clojure sample"
:dependencies [[org.clojure/clojure "1.9.0"]
[ring/ring-core "1.6.3"]
[ring/ring-jetty-adapter "1.6.3"]]
:main helloworld.core)
```
1. In your project directory, create a file named `Dockerfile` and copy the code
block below into it. For detailed instructions on dockerizing a Clojure app,
see
[the clojure image documentation](https://github.com/docker-library/docs/tree/master/clojure).
```docker
# Use the official Clojure image.
# https://hub.docker.com/_/clojure
FROM clojure
# Create the project and download dependencies.
WORKDIR /usr/src/app
COPY project.clj .
RUN lein deps
# Copy local code to the container image.
COPY . .
# Build an uberjar release artifact.
RUN mv "$(lein uberjar | sed -n 's/^Created \(.*standalone\.jar\)/\1/p')" app-standalone.jar
# Run the web service on container startup.
CMD ["java", "-jar", "app-standalone.jar"]
```
1. Create a new file, `service.yaml` and copy the following service definition
into the file. Make sure to replace `{username}` with your Docker Hub
username.
```yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-clojure
namespace: default
spec:
template:
spec:
containers:
- image: docker.io/{username}/helloworld-clojure
env:
- name: TARGET
value: "Clojure Sample v1"
```
## Building and deploying the sample
Once you have recreated the sample code files (or used the files in the sample
folder) you're ready to build and deploy the sample app.
1. Use Docker to build the sample code into a container. To build and push with
Docker Hub, run these commands replacing `{username}` with your Docker Hub
username:
```bash
# Build the container on your local machine
docker build -t {username}/helloworld-clojure .
# Push the container to docker registry
docker push {username}/helloworld-clojure
```
1. After the build has completed and the container is pushed to docker hub, you
can deploy the app into your cluster. Ensure that the container image value
in `service.yaml` matches the container you built in the previous step. Apply
the configuration using `kubectl`:
```bash
kubectl apply --filename service.yaml
```
1. Now that your service is created, Knative will perform the following steps:
- Create a new immutable revision for this version of the app.
- Network programming to create a route, ingress, service, and load balance
for your app.
- Automatically scale your pods up and down (including to zero active pods).
1. To find the URL for your service, use
```
kubectl get ksvc helloworld-clojure --output=custom-columns=NAME:.metadata.name,URL:.status.url
NAME URL
helloworld-clojure http://helloworld-clojure.default.1.2.3.4.sslip.io
```
1. Now you can make a request to your app and see the result. Replace
the URL below with the URL returned in the previous command.
```bash
curl http://helloworld-clojure.default.1.2.3.4.sslip.io
Hello World!
```
## Removing the sample app deployment
To remove the sample app from your cluster, delete the service record:
```bash
kubectl delete --filename service.yaml
```

View File

@ -0,0 +1,6 @@
(defproject helloworld "1.0.0-SNAPSHOT"
:description "Hello World - Clojure sample"
:dependencies [[org.clojure/clojure "1.9.0"]
[ring/ring-core "1.6.3"]
[ring/ring-jetty-adapter "1.6.3"]]
:main helloworld.core)

View File

@ -0,0 +1,13 @@
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-clojure
namespace: default
spec:
template:
spec:
containers:
- image: docker.io/{username}/helloworld-clojure
env:
- name: TARGET
value: "Clojure Sample v1"

View File

@ -0,0 +1,17 @@
(ns helloworld.core
(:use ring.adapter.jetty)
(:gen-class))
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/html"}
:body (str "Hello "
(if-let [target (System/getenv "TARGET")]
target
"World")
"!\n")})
(defn -main [& args]
(run-jetty handler {:port (if-let [port (System/getenv "PORT")]
(Integer/parseInt port)
8080)}))

View File

@ -0,0 +1,4 @@
# See https://www.dartlang.org/guides/libraries/private-files
.dart_tool
.packages
pubspec.lock

View File

@ -0,0 +1,7 @@
# Use Google's official Dart image.
# https://hub.docker.com/r/google/dart-runtime/
FROM google/dart-runtime
# ONBUILD instructions for COPY and pub get is defined in the google/dart-runtime image,
# allowing you to keep this file very simple, see:
# https://github.com/dart-lang/dart_docker/blob/master/runtime/Dockerfile.template#L15-L18

View File

@ -0,0 +1,155 @@
# Hello World - Dart
A simple web app written in the [Dart](https://www.dart.dev) programming language
that you can use for testing. It reads in the env variable `TARGET` and prints
`"Hello $TARGET"`. If `TARGET` is not specified, it will use `"World"` as
`TARGET`.
## Prerequisites
- A Kubernetes cluster with Knative installed and DNS configured. Follow the
[Knative installation instructions](https://knative.dev/docs/install/) if you need to create
one.
- [Docker](https://www.docker.com) installed and running on your local machine,
and a Docker Hub account configured (we'll use it for a container registry).
- [dart-sdk](https://www.dart.dev/tools/sdk#install) installed and
configured if you want to run the program locally.
## Recreating the sample code
While you can clone all of the code from this directory, it is useful to know
how to build a hello world Dart application step-by-step. This application can
be created using the following instructions.
1. Create a new directory and write `pubspec.yaml` as follows:
```yaml
name: hello_world_dart
publish_to: none # let's not accidentally publish this to pub.dartlang.org
environment:
sdk: ">=2.12.0 <3.0.0"
dependencies:
shelf: ^1.0.0
```
2. If you want to run locally, install dependencies. If you only want to run in
Docker or Knative, you can skip this step.
```bash
> pub get
```
3. Create a new file `bin/server.dart` and write the following code:
```dart
import 'dart:io';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart';
Future main() async {
// Find port to listen on from environment variable.
final port = int.parse(Platform.environment['PORT'] ?? '8080');
// Read $TARGET from environment variable.
final target = Platform.environment['TARGET'] ?? 'World';
Response handler(Request request) => Response.ok('Hello $target');
// Serve handler on given port.
final server = await serve(
const Pipeline().addMiddleware(logRequests()).addHandler(handler),
InternetAddress.anyIPv4,
port,
);
print('Serving at http://${server.address.host}:${server.port}');
}
```
4. Create a new file named `Dockerfile`, this file defines instructions for
dockerizing your applications, for dart apps this can be done as follows:
```Dockerfile
# Use Google's official Dart image.
# https://hub.docker.com/r/google/dart-runtime/
FROM google/dart-runtime
```
5. Create a new file, `service.yaml` and copy the following service definition
into the file. Make sure to replace `{username}` with your Docker Hub
username.
```yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-dart
namespace: default
spec:
template:
spec:
containers:
- image: docker.io/{username}/helloworld-dart
env:
- name: TARGET
value: "Dart Sample v1"
```
## Building and deploying the sample
Once you have recreated the sample code files (or used the files in the sample
folder) you're ready to build and deploy the sample app.
1. Use Docker to build the sample code into a container. To build and push with
Docker Hub, run these commands replacing `{username}` with your Docker Hub
username:
```bash
# Build the container on your local machine
docker build -t {username}/helloworld-dart .
# Push the container to docker registry
docker push {username}/helloworld-dart
```
1. After the build has completed and the container is pushed to docker hub, you
can deploy the app into your cluster. Ensure that the container image value
in `service.yaml` matches the container you built in the previous step. Apply
the configuration using `kubectl`:
```bash
kubectl apply --filename service.yaml
```
1. Now that your service is created, Knative will perform the following steps:
- Create a new immutable revision for this version of the app.
- Network programming to create a route, ingress, service, and load balance
for your app.
- Automatically scale your pods up and down (including to zero active pods).
1. To find the URL for your service, use
```
kubectl get ksvc helloworld-dart --output=custom-columns=NAME:.metadata.name,URL:.status.url
NAME URL
helloworld-dart http://helloworld-dart.default.1.2.3.4.sslip.io
```
1. Now you can make a request to your app and see the result. Replace
the URL below with the URL returned in the previous command.
```bash
curl http://helloworld-dart.default.1.2.3.4.sslip.io
Hello Dart Sample v1
```
## Removing the sample app deployment
To remove the sample app from your cluster, delete the service record:
```bash
kubectl delete --filename service.yaml
```

View File

@ -0,0 +1,22 @@
import 'dart:io';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart';
Future main() async {
// Find port to listen on from environment variable.
final port = int.parse(Platform.environment['PORT'] ?? '8080');
// Read $TARGET from environment variable.
final target = Platform.environment['TARGET'] ?? 'World';
Response handler(Request request) => Response.ok('Hello $target');
// Serve handler on given port.
final server = await serve(
const Pipeline().addMiddleware(logRequests()).addHandler(handler),
InternetAddress.anyIPv4,
port,
);
print('Serving at http://${server.address.host}:${server.port}');
}

View File

@ -0,0 +1,8 @@
name: hello_world_dart
publish_to: none # let's not accidentally publish this to pub.dartlang.org
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
shelf: ^1.0.0

View File

@ -0,0 +1,13 @@
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-dart
namespace: default
spec:
template:
spec:
containers:
- image: docker.io/{username}/helloworld-dart
env:
- name: TARGET
value: "Dart Sample v1"

View File

@ -0,0 +1,2 @@
Dockerfile
README.md

View File

@ -0,0 +1,8 @@
FROM hayd/alpine-deno:1.0.0-rc2
WORKDIR /app
# These steps will be re-run upon each file change in your working directory:
COPY . ./
# Added to ENTRYPOINT of base image.
CMD ["run", "--allow-env", "--allow-net", "main.ts"]

View File

@ -0,0 +1,144 @@
# Hello World - Deno
A simple web app written in Deno.
Follow the steps below to create the sample code and then deploy the app to your
cluster. You can also download a working copy of the sample, by running the
following commands:
```bash
git clone https://github.com/knative/docs.git knative-docs
cd knative-docs/code-samples/community/hello-world/helloworld-deno
```
## Before you begin
- A Kubernetes cluster with Knative installed. Follow the
[Knative installation instructions](https://knative.dev/docs/install/) if you need to
create one.
- [Docker](https://www.docker.com) installed and running on your local machine,
and a Docker Hub account configured (we'll use it for a container registry).
## Recreating the sample code
1. Create a new file named `deps.ts` and paste the following script:
```ts
export { serve } from "https://deno.land/std@std@0.50.0/http/server.ts";
```
1. Create a new file named `main.ts` and paste the following script:
```ts
import { serve } from "./deps.ts";
import "https://deno.land/x/dotenv/mod.ts";
const PORT = Deno.env.get('PORT') || 8080;
const s = serve(`0.0.0.0:${PORT}`);
const body = new TextEncoder().encode("Hello Deno\n");
console.log(`Server started on port ${PORT}`);
for await (const req of s) {
req.respond({ body });
}
```
1. Create a new file named `Dockerfile` and copy the code block below into it.
```docker
FROM hayd/alpine-deno:1.0.0-rc2
WORKDIR /app
# These steps will be re-run upon each file change in your working directory:
COPY . ./
# Added to ENTRYPOINT of base image.
CMD ["run", "--allow-env", "--allow-net", "main.ts"]
```
1. Create a new file, `service.yaml` and copy the following service definition
into the file. Make sure to replace `{username}` with your Docker Hub
username.
```yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-deno
namespace: default
spec:
template:
spec:
containers:
- image: docker.io/{username}/helloworld-deno
```
## Building and deploying the sample
Once you have recreated the sample code files (or used the files in the sample
folder) you're ready to build and deploy the sample app.
1. Use Docker to build the sample code into a container. To build and push with
Docker Hub, run these commands replacing `{username}` with your Docker Hub
username:
```bash
# Build the container on your local machine
docker build -t {username}/helloworld-deno .
# Push the container to docker registry
docker push {username}/helloworld-deno
```
1. After the build has completed and the container is pushed to docker hub, you
can deploy the app into your cluster. Ensure that the container image value
in `service.yaml` matches the container you built in the previous step. Apply
the configuration using `kubectl`:
```bash
kubectl apply --filename service.yaml
```
1. Now that your service is created, Knative performs the following steps:
- Create a new immutable revision for this version of the app.
- Network programming to create a route, ingress, service, and load balance
for your app.
- Automatically scale your pods up and down (including to zero active pods).
1. Run the following command to find the domain URL for your service:
```bash
kubectl get ksvc helloworld-deno --output=custom-columns=NAME:.metadata.name,URL:.status.url
```
Example:
```bash
NAME URL
helloworld-deno http://helloworld-deno.default.1.2.3.4.sslip.io
```
1. Now you can make a request to your app and see the result. Replace
the URL below with the URL returned in the previous command.
```bash
curl http://helloworld-deno.default.1.2.3.4.sslip.io
```
Example:
```bash
curl http://helloworld-deno.default.1.2.3.4.sslip.io
[1] "Hello R Sample v1!"
```
> Note: Add `-v` option to get more detail if the `curl` command failed.
## Removing the sample app deployment
To remove the sample app from your cluster, delete the service record:
```bash
kubectl delete --filename service.yaml
```

View File

@ -0,0 +1 @@
export { serve } from "https://deno.land/std@0.50.0/http/server.ts";

View File

@ -0,0 +1,11 @@
import { serve } from "./deps.ts";
import "https://deno.land/x/dotenv/mod.ts";
const PORT = Deno.env.get('PORT') || 8080;
const s = serve(`0.0.0.0:${PORT}`);
const body = new TextEncoder().encode("Hello Deno\n");
console.log(`Server started on port ${PORT}`);
for await (const req of s) {
req.respond({ body });
}

View File

@ -0,0 +1,24 @@
# For building with Google Cloud Build using the gcloud command line tool.
# Similar to .gitignore, except we want the /config/*.secrete.exs uploaded for the build.
# App artifacts
/_build
/db
/deps
/*.ez
# Generated on crash by the VM
erl_crash.dump
# Generated on crash by NPM
npm-debug.log
/assets/package-lock.json
# Static artifacts
/assets/node_modules
# Since we are building assets from assets/,
# we ignore priv/static. You may want to comment
# this depending on your deployment strategy.
/priv/static/

View File

@ -0,0 +1,28 @@
# App artifacts
/_build
/db
/deps
/*.ez
# Generated on crash by the VM
erl_crash.dump
# Generated on crash by NPM
npm-debug.log
/assets/package-lock.json
# Static artifacts
/assets/node_modules
# Since we are building assets from assets/,
# we ignore priv/static. You may want to comment
# this depending on your deployment strategy.
/priv/static/
# Files matching config/*.secret.exs pattern contain sensitive
# data and you should not commit them into version control.
#
# Alternatively, you may comment the line below and commit the
# secrets files as long as you replace their contents by environment
# variables.
/config/*.secret.exs

View File

@ -0,0 +1,38 @@
FROM elixir:1.6.6-alpine
ARG APP_NAME=hello
ARG PHOENIX_SUBDIR=.
ENV MIX_ENV=prod REPLACE_OS_VARS=true TERM=xterm
WORKDIR /opt/app
RUN apk update \
&& apk --no-cache --update add nodejs nodejs-npm \
&& mix local.rebar --force \
&& mix local.hex --force
COPY . .
RUN mix do deps.get, deps.compile, compile
RUN cd ${PHOENIX_SUBDIR}/assets \
&& npm install \
&& ./node_modules/brunch/bin/brunch build -p \
&& cd .. \
&& mix phx.digest
RUN mix release --env=prod --verbose \
&& mv _build/prod/rel/${APP_NAME} /opt/release \
&& mv /opt/release/bin/${APP_NAME} /opt/release/bin/start_server
FROM alpine:latest
RUN apk update && apk --no-cache --update add bash openssl-dev ca-certificates
RUN addgroup -g 1000 appuser && \
adduser -S -u 1000 -G appuser appuser
RUN mkdir -p /opt/app/var
RUN chown appuser /opt/app/var
USER appuser
ENV MIX_ENV=prod REPLACE_OS_VARS=true
WORKDIR /opt/app
COPY --from=0 /opt/release .
ENV RUNNER_LOG_DIR /var/log
CMD ["/opt/app/bin/start_server", "foreground", "boot_var=/tmp"]

View File

@ -0,0 +1,293 @@
# Hello World - Elixir
A simple web application written in [Elixir](https://elixir-lang.org/) using the
[Phoenix Framework](https://phoenixframework.org/). The application prints all
environment variables to the main page.
## Set up Elixir and Phoenix Locally
Following the
[Phoenix Installation Guide](https://hexdocs.pm/phoenix/installation.html) is
the best way to get your computer set up for developing, building, running, and
packaging Elixir Web applications.
## Running Locally
To start your Phoenix server:
- Install dependencies with `mix deps.get`
- Install Node.js dependencies with `cd assets && npm install`
- Start Phoenix endpoint with `mix phx.server`
Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
## Recreating the sample code
1. Generate a new project.
```bash
mix phoenix.new helloelixir
```
When asked, if you want to `Fetch and install dependencies? [Yn]` select `y`
1. Follow the direction in the output to change directories into start your
local server with `mix phoenix.server`
1. In the new directory, create a new Dockerfile for packaging your application
for deployment
```docker
# Start from a base image for elixir
# Phoenix works best on pre 1.7 at the moment.
FROM elixir:1.6.6-alpine
# Set up Elixir and Phoenix
ARG APP_NAME=hello
ARG PHOENIX_SUBDIR=.
ENV MIX_ENV=prod REPLACE_OS_VARS=true TERM=xterm
WORKDIR /opt/app
# Update nodejs, rebar, and hex.
RUN apk update \
&& apk --no-cache --update add nodejs nodejs-npm \
&& mix local.rebar --force \
&& mix local.hex --force
COPY . .
# Download and compile dependencies, then compile Web app.
RUN mix do deps.get, deps.compile, compile
RUN cd ${PHOENIX_SUBDIR}/assets \
&& npm install \
&& ./node_modules/brunch/bin/brunch build -p \
&& cd .. \
&& mix phx.digest
# Create a release version of the application
RUN mix release --env=prod --verbose \
&& mv _build/prod/rel/${APP_NAME} /opt/release \
&& mv /opt/release/bin/${APP_NAME} /opt/release/bin/start_server
# Prepare final layer
FROM alpine:latest
RUN apk update && apk --no-cache --update add bash openssl-dev ca-certificates
# Add a user so the server will run as a non-root user.
RUN addgroup -g 1000 appuser && \
adduser -S -u 1000 -G appuser appuser
# Pre-create necessary temp directory for erlang and set permissions.
RUN mkdir -p /opt/app/var
RUN chown appuser /opt/app/var
# Run everything else as 'appuser'
USER appuser
ENV MIX_ENV=prod REPLACE_OS_VARS=true
WORKDIR /opt/app
COPY --from=0 /opt/release .
ENV RUNNER_LOG_DIR /var/log
# Command to execute the application.
CMD ["/opt/app/bin/start_server", "foreground", "boot_var=/tmp"]
```
1. Create a new file, `service.yaml` and copy the following Service definition
into the file. Make sure to replace `{username}` with your Docker Hub
username.
```yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-elixir
namespace: default
spec:
template:
spec:
containers:
- image: docker.io/{username}/helloworld-elixir
env:
- name: TARGET
value: "elixir Sample v1"
```
## Building and deploying the sample
The sample in this directory is ready to build and deploy without changes. You
can deploy the sample as is, or use you created version following the directions
above.
1. Generate a new `secret_key_base` in the `config/prod.secret.exs` file.
Phoenix applications use a secrets file on production deployments and, by
default, that file is not checked into source control. We have provides
shell of an example on `config/prod.secret.exs.sample` and you can use the
following command to generate a new prod secrets file.
```bash
SECRET_KEY_BASE=$(elixir -e ":crypto.strong_rand_bytes(48) |> Base.encode64 |> IO.puts")
sed "s|SECRET+KEY+BASE|$SECRET_KEY_BASE|" config/prod.secret.exs.sample >config/prod.secret.exs
```
1. Use Docker to build the sample code into a container. To build and push with
Docker Hub, run these commands replacing `{username}` with your Docker Hub
username:
```bash
# Build the container on your local machine
docker build -t {username}/helloworld-elixir .
# Push the container to docker registry
docker push {username}/helloworld-elixir
```
1. After the build has completed and the container is pushed to docker hub, you
can deploy the app into your cluster. Ensure that the container image value
in `service.yaml` matches the container you built in the previous step.
Apply the configuration using `kubectl`:
```bash
kubectl apply --filename service.yaml
```
1. Now that your service is created, Knative will perform the following steps:
- Create a new immutable revision for this version of the app.
- Network programming to create a route, ingress, service, and load balance
for your app.
- Automatically scale your pods up and down (including to zero active pods).
1. To find the URL for your service, use
```
kubectl get ksvc helloworld-elixir --output=custom-columns=NAME:.metadata.name,URL:.status.url
NAME URL
helloworld-elixir http://helloworld-elixir.default.1.2.3.4.sslip.io
```
1. Now you can make a request to your app to see the results. Replace
`{IP_ADDRESS}` with the address you see returned in the previous step.
```bash
curl http://helloworld-elixir.default.1.2.3.4.sslip.io
...
# HTML from your application is returned.
```
Here is the HTML returned from our deployed sample application:
```HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Hello Knative</title>
<link rel="stylesheet" type="text/css" href="/css/app-833cc7e8eeed7a7953c5a02e28130dbd.css?vsn=d">
</head>
<body>
<div class="container">
<header class="header">
<nav role="navigation">
</nav>
</header>
<p class="alert alert-info" role="alert"></p>
<p class="alert alert-danger" role="alert"></p>
<main role="main">
<div class="jumbotron">
<h2>Welcome to Knative and Elixir</h2>
<p>$TARGET = elixir Sample v1</p>
</div>
<h3>Environment</h3>
<ul>
<li>BINDIR = /opt/app/erts-9.3.2/bin</li>
<li>DEST_SYS_CONFIG_PATH = /opt/app/var/sys.config</li>
<li>DEST_VMARGS_PATH = /opt/app/var/vm.args</li>
<li>DISTILLERY_TASK = foreground</li>
<li>EMU = beam</li>
<li>ERL_LIBS = /opt/app/lib</li>
<li>ERL_OPTS = </li>
<li>ERTS_DIR = /opt/app/erts-9.3.2</li>
<li>ERTS_LIB_DIR = /opt/app/erts-9.3.2/../lib</li>
<li>ERTS_VSN = 9.3.2</li>
<li>HELLOWORLD_ELIXIR_00001_SERVICE_PORT = tcp://10.35.241.50:80</li>
<li>HELLOWORLD_ELIXIR_00001_SERVICE_PORT_80_TCP = tcp://10.35.241.50:80</li>
<li>HELLOWORLD_ELIXIR_00001_SERVICE_PORT_80_TCP_ADDR = 10.35.241.50</li>
<li>HELLOWORLD_ELIXIR_00001_SERVICE_PORT_80_TCP_PORT = 80</li>
<li>HELLOWORLD_ELIXIR_00001_SERVICE_PORT_80_TCP_PROTO = tcp</li>
<li>HELLOWORLD_ELIXIR_00001_SERVICE_SERVICE_HOST = 10.35.241.50</li>
<li>HELLOWORLD_ELIXIR_00001_SERVICE_SERVICE_PORT = 80</li>
<li>HELLOWORLD_ELIXIR_00001_SERVICE_SERVICE_PORT_HTTP = 80</li>
<li>HELLOWORLD_ELIXIR_PORT = tcp://10.35.253.90:80</li>
<li>HELLOWORLD_ELIXIR_PORT_80_TCP = tcp://10.35.253.90:80</li>
<li>HELLOWORLD_ELIXIR_PORT_80_TCP_ADDR = 10.35.253.90</li>
<li>HELLOWORLD_ELIXIR_PORT_80_TCP_PORT = 80</li>
<li>HELLOWORLD_ELIXIR_PORT_80_TCP_PROTO = tcp</li>
<li>HELLOWORLD_ELIXIR_SERVICE_HOST = 10.35.253.90</li>
<li>HELLOWORLD_ELIXIR_SERVICE_PORT = 80</li>
<li>HELLOWORLD_ELIXIR_SERVICE_PORT_HTTP = 80</li>
<li>HOME = /root</li>
<li>HOSTNAME = helloworld-elixir-00001-deployment-84f68946b4-76hcv</li>
<li>KUBERNETES_PORT = tcp://10.35.240.1:443</li>
<li>KUBERNETES_PORT_443_TCP = tcp://10.35.240.1:443</li>
<li>KUBERNETES_PORT_443_TCP_ADDR = 10.35.240.1</li>
<li>KUBERNETES_PORT_443_TCP_PORT = 443</li>
<li>KUBERNETES_PORT_443_TCP_PROTO = tcp</li>
<li>KUBERNETES_SERVICE_HOST = 10.35.240.1</li>
<li>KUBERNETES_SERVICE_PORT = 443</li>
<li>KUBERNETES_SERVICE_PORT_HTTPS = 443</li>
<li>LD_LIBRARY_PATH = /opt/app/erts-9.3.2/lib:</li>
<li>MIX_ENV = prod</li>
<li>NAME = hello@127.0.0.1</li>
<li>NAME_ARG = -name hello@127.0.0.1</li>
<li>NAME_TYPE = -name</li>
<li>OLDPWD = /opt/app</li>
<li>OTP_VER = 20</li>
<li>PATH = /opt/app/erts-9.3.2/bin:/opt/app/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin</li>
<li>PORT = 8080</li>
<li>PROGNAME = opt/app/releases/0.0.1/hello.sh</li>
<li>PWD = /opt/app</li>
<li>RELEASES_DIR = /opt/app/releases</li>
<li>RELEASE_CONFIG_DIR = /opt/app</li>
<li>RELEASE_ROOT_DIR = /opt/app</li>
<li>REL_NAME = hello</li>
<li>REL_VSN = 0.0.1</li>
<li>REPLACE_OS_VARS = true</li>
<li>ROOTDIR = /opt/app</li>
<li>RUNNER_LOG_DIR = /var/log</li>
<li>RUN_ERL_ENV = </li>
<li>SHLVL = 1</li>
<li>SRC_SYS_CONFIG_PATH = /opt/app/releases/0.0.1/sys.config</li>
<li>SRC_VMARGS_PATH = /opt/app/releases/0.0.1/vm.args</li>
<li>SYS_CONFIG_PATH = /opt/app/var/sys.config</li>
<li>TARGET = elixir Sample v1</li>
<li>TERM = xterm</li>
<li>VMARGS_PATH = /opt/app/var/vm.args</li>
</ul>
</main>
</div> <!-- /container -->
<script src="/js/app-930ab1950e10d7b5ab5083423c28f06e.js?vsn=d"></script>
</body>
</html>
```
## Removing the sample app deployment
To remove the sample app from your cluster, delete the service record:
```bash
kubectl delete --filename service.yaml
```

View File

@ -0,0 +1,62 @@
exports.config = {
// See http://brunch.io/#documentation for docs.
files: {
javascripts: {
joinTo: "js/app.js"
// To use a separate vendor.js bundle, specify two files path
// http://brunch.io/docs/config#-files-
// joinTo: {
// "js/app.js": /^js/,
// "js/vendor.js": /^(?!js)/
// }
//
// To change the order of concatenation of files, explicitly mention here
// order: {
// before: [
// "vendor/js/jquery-2.1.1.js",
// "vendor/js/bootstrap.min.js"
// ]
// }
},
stylesheets: {
joinTo: "css/app.css"
},
templates: {
joinTo: "js/app.js"
}
},
conventions: {
// This option sets where we should place non-css and non-js assets in.
// By default, we set this to "/assets/static". Files in this directory
// will be copied to `paths.public`, which is "priv/static" by default.
assets: /^(static)/
},
// Phoenix paths configuration
paths: {
// Dependencies and current project directories to watch
watched: ["static", "css", "js", "vendor"],
// Where to compile files to
public: "../priv/static"
},
// Configure your plugins
plugins: {
babel: {
// Do not use ES6 compiler in vendor code
ignore: [/vendor/]
}
},
modules: {
autoRequire: {
"js/app.js": ["js/app"]
}
},
npm: {
enabled: true
}
};

View File

@ -0,0 +1 @@
/* This file is for your main application css. */

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,21 @@
// Brunch automatically concatenates all files in your
// watched paths. Those paths can be configured at
// config.paths.watched in "brunch-config.js".
//
// However, those files will only be executed if
// explicitly imported. The only exception are files
// in vendor, which are never wrapped in imports and
// therefore are always executed.
// Import dependencies
//
// If you no longer want to use a dependency, remember
// to also remove its path from "config.paths.watched".
import "phoenix_html"
// Import local files
//
// Local files can be imported directly using relative
// paths "./socket" or full ones "web/static/js/socket".
// import socket from "./socket"

View File

@ -0,0 +1,62 @@
// NOTE: The contents of this file will only be executed if
// you uncomment its entry in "assets/js/app.js".
// To use Phoenix channels, the first step is to import Socket
// and connect at the socket path in "lib/web/endpoint.ex":
import {Socket} from "phoenix"
let socket = new Socket("/socket", {params: {token: window.userToken}})
// When you connect, you'll often need to authenticate the client.
// For example, imagine you have an authentication plug, `MyAuth`,
// which authenticates the session and assigns a `:current_user`.
// If the current user exists you can assign the user's token in
// the connection for use in the layout.
//
// In your "lib/web/router.ex":
//
// pipeline :browser do
// ...
// plug MyAuth
// plug :put_user_token
// end
//
// defp put_user_token(conn, _) do
// if current_user = conn.assigns[:current_user] do
// token = Phoenix.Token.sign(conn, "user socket", current_user.id)
// assign(conn, :user_token, token)
// else
// conn
// end
// end
//
// Now you need to pass this token to JavaScript. You can do so
// inside a script tag in "lib/web/templates/layout/app.html.eex":
//
// <script>window.userToken = "<%= assigns[:user_token] %>";</script>
//
// You will need to verify the user token in the "connect/2" function
// in "lib/web/channels/user_socket.ex":
//
// def connect(%{"token" => token}, socket) do
// # max_age: 1209600 is equivalent to two weeks in seconds
// case Phoenix.Token.verify(socket, "user socket", token, max_age: 1209600) do
// {:ok, user_id} ->
// {:ok, assign(socket, :user, user_id)}
// {:error, reason} ->
// :error
// end
// end
//
// Finally, pass the token on connect as below. Or remove it
// from connect if you don't care about authentication.
socket.connect()
// Now that you are connected, you can join channels with a topic:
let channel = socket.channel("topic:subtopic", {})
channel.join()
.receive("ok", resp => { console.log("Joined successfully", resp) })
.receive("error", resp => { console.log("Unable to join", resp) })
export default socket

View File

@ -0,0 +1,18 @@
{
"repository": {},
"license": "MIT",
"scripts": {
"deploy": "brunch build --production",
"watch": "brunch watch --stdin"
},
"dependencies": {
"phoenix": "file:../deps/phoenix",
"phoenix_html": "file:../deps/phoenix_html"
},
"devDependencies": {
"babel-brunch": "6.1.1",
"brunch": "2.10.9",
"clean-css-brunch": "2.10.0",
"uglify-js-brunch": "2.10.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,5 @@
# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
#
# To ban all spiders from the entire site uncomment the next two lines:
# User-agent: *
# Disallow: /

View File

@ -0,0 +1,23 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
#
# This configuration file is loaded before any dependency and
# is restricted to this project.
use Mix.Config
# Configures the endpoint
config :hello, HelloWeb.Endpoint,
url: [host: "localhost"],
secret_key_base: "P0fv0U47j6mdL40sn3f3BIuvEyIdbxUq+yKc8Mcbwig5105brf2Dzio5ANjTVRUo",
render_errors: [view: HelloWeb.ErrorView, accepts: ~w(html json)]
#pubsub: [name: Hello.PubSub,
# adapter: Phoenix.PubSub.PG2]
# Configures Elixir's Logger
config :logger, :console,
format: "$time $metadata[$level] $message\n",
metadata: [:user_id]
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env}.exs"

View File

@ -0,0 +1,49 @@
use Mix.Config
# For development, we disable any cache and enable
# debugging and code reloading.
#
# The watchers configuration can be used to run external
# watchers to your application. For example, we use it
# with brunch.io to recompile .js and .css sources.
config :hello, HelloWeb.Endpoint,
http: [port: 4000],
debug_errors: true,
code_reloader: true,
check_origin: false,
watchers: [node: ["node_modules/brunch/bin/brunch", "watch", "--stdin",
cd: Path.expand("../assets", __DIR__)]]
# ## SSL Support
#
# In order to use HTTPS in development, a self-signed
# certificate can be generated by running the following
# command from your terminal:
#
# openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout priv/server.key -out priv/server.pem
#
# The `http:` config above can be replaced with:
#
# https: [port: 4000, keyfile: "priv/server.key", certfile: "priv/server.pem"],
#
# If desired, both `http:` and `https:` keys can be
# configured to run both http and https servers on
# different ports.
# Watch static and templates for browser reloading.
config :hello, HelloWeb.Endpoint,
live_reload: [
patterns: [
~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$},
~r{priv/gettext/.*(po)$},
~r{lib/hello_web/views/.*(ex)$},
~r{lib/hello_web/templates/.*(eex)$}
]
]
# Do not include metadata nor timestamps in development logs
config :logger, :console, format: "[$level] $message\n"
# Set a higher stacktrace during development. Avoid configuring such
# in production as building large stacktraces may be expensive.
config :phoenix, :stacktrace_depth, 20

View File

@ -0,0 +1,67 @@
use Mix.Config
# For production, we often load configuration from external
# sources, such as your system environment. For this reason,
# you won't find the :http configuration below, but set inside
# HelloWeb.Endpoint.init/2 when load_from_system_env is
# true. Any dynamic configuration should be done there.
#
# Don't forget to configure the url host to something meaningful,
# Phoenix uses this information when generating URLs.
#
# Finally, we also include the path to a cache manifest
# containing the digested version of static files. This
# manifest is generated by the mix phx.digest task
# which you typically run after static files are built.
config :hello, HelloWeb.Endpoint,
load_from_system_env: false,
http: [port: 8080],
check_origin: false,
server: true,
root: ".",
cache_static_manifest: "priv/static/cache_manifest.json"
# Do not print debug messages in production
config :logger, level: :info
# ## SSL Support
#
# To get SSL working, you will need to add the `https` key
# to the previous section and set your `:url` port to 443:
#
# config :hello, HelloWeb.Endpoint,
# ...
# url: [host: "example.com", port: 443],
# https: [:inet6,
# port: 443,
# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")]
#
# Where those two env variables return an absolute path to
# the key and cert in disk or a relative path inside priv,
# for example "priv/ssl/server.key".
#
# We also recommend setting `force_ssl`, ensuring no data is
# ever sent via http, always redirecting to https:
#
# config :hello, HelloWeb.Endpoint,
# force_ssl: [hsts: true]
#
# Check `Plug.SSL` for all available options in `force_ssl`.
# ## Using releases
#
# If you are doing OTP releases, you need to instruct Phoenix
# to start the server for all endpoints:
#
# config :phoenix, :serve_endpoints, true
#
# Alternatively, you can configure exactly which server to
# start per endpoint:
#
# config :hello, HelloWeb.Endpoint, server: true
#
# Finally import the config/prod.secret.exs
# which should be versioned separately.
import_config "prod.secret.exs"

View File

@ -0,0 +1,12 @@
use Mix.Config
# In this file, we keep production configuration that
# you'll likely want to automate and keep away from
# your version control system.
#
# You should document the content of this
# file or create a script for recreating it, since it's
# kept out of version control and might be hard to recover
# or recreate for your teammates (or yourself later on).
config :hello, HelloWeb.Endpoint,
secret_key_base: "SECRET+KEY+BASE"

View File

@ -0,0 +1,10 @@
use Mix.Config
# We don't run a server during test. If one is required,
# you can enable the server option below.
config :hello, HelloWeb.Endpoint,
http: [port: 4001],
server: false
# Print only warnings and errors during test
config :logger, level: :warn

View File

@ -0,0 +1,9 @@
defmodule Hello do
@moduledoc """
Hello keeps the contexts that define your domain
and business logic.
Contexts are also responsible for managing your data, regardless
if it comes from the database, an external API or others.
"""
end

View File

@ -0,0 +1,31 @@
defmodule Hello.Application do
use Application
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
def start(_type, _args) do
IO.puts :stderr, "Application starting up"
import Supervisor.Spec
# Define workers and child supervisors to be supervised
children = [
# Start the endpoint when the application starts
supervisor(HelloWeb.Endpoint, []),
# Start your own worker by calling: Hello.Worker.start_link(arg1, arg2, arg3)
# worker(Hello.Worker, [arg1, arg2, arg3]),
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Hello.Supervisor]
Supervisor.start_link(children, opts)
end
# Tell Phoenix to update the endpoint configuration
# whenever the application is updated.
def config_change(changed, _new, removed) do
IO.puts :stderr, "Config changed"
HelloWeb.Endpoint.config_change(changed, removed)
:ok
end
end

View File

@ -0,0 +1,67 @@
defmodule HelloWeb do
@moduledoc """
The entrypoint for defining your web interface, such
as controllers, views, channels and so on.
This can be used in your application as:
use HelloWeb, :controller
use HelloWeb, :view
The definitions below will be executed for every view,
controller, etc, so keep them short and clean, focused
on imports, uses and aliases.
Do NOT define functions inside the quoted expressions
below. Instead, define any helper function in modules
and import those modules here.
"""
def controller do
quote do
use Phoenix.Controller, namespace: HelloWeb
import Plug.Conn
import HelloWeb.Router.Helpers
import HelloWeb.Gettext
end
end
def view do
quote do
use Phoenix.View, root: "lib/hello_web/templates",
namespace: HelloWeb
# Import convenience functions from controllers
import Phoenix.Controller, only: [get_flash: 2, view_module: 1]
# Use all HTML functionality (forms, tags, etc)
use Phoenix.HTML
import HelloWeb.Router.Helpers
import HelloWeb.ErrorHelpers
import HelloWeb.Gettext
end
end
def router do
quote do
use Phoenix.Router
import Plug.Conn
import Phoenix.Controller
end
end
def channel do
quote do
use Phoenix.Channel
import HelloWeb.Gettext
end
end
@doc """
When used, dispatch to the appropriate controller/view/etc.
"""
defmacro __using__(which) when is_atom(which) do
apply(__MODULE__, which, [])
end
end

View File

@ -0,0 +1,38 @@
defmodule HelloWeb.UserSocket do
use Phoenix.Socket
## Channels
# channel "room:*", HelloWeb.RoomChannel
## Transports
transport :longpoll, Phoenix.Transports.LongPoll
# transport :longpoll, Phoenix.Transports.LongPoll
# Socket params are passed from the client and can
# be used to verify and authenticate a user. After
# verification, you can put default assigns into
# the socket that will be set for all channels, ie
#
# {:ok, assign(socket, :user_id, verified_user_id)}
#
# To deny connection, return `:error`.
#
# See `Phoenix.Token` documentation for examples in
# performing token verification on connect.
def connect(_params, socket) do
IO.puts :stderr, "UserSocket.connect called"
{:ok, socket}
end
# Socket id's are topics that allow you to identify all sockets for a given user:
#
# def id(socket), do: "user_socket:#{socket.assigns.user_id}"
#
# Would allow you to broadcast a "disconnect" event and terminate
# all active sockets and channels for a given user:
#
# HelloWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
#
# Returning `nil` makes this socket anonymous.
def id(_socket), do: nil
end

View File

@ -0,0 +1,14 @@
defmodule HelloWeb.PageController do
use HelloWeb, :controller
def index(conn, params) do
env = System.get_env()
target = Map.get(env, "TARGET")
render conn, "index.html",
title: "Hello Knative",
greeting: "Welcome to Knative and Elixir",
target: target,
env: env
end
end

View File

@ -0,0 +1,57 @@
defmodule HelloWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :hello
socket "/socket", HelloWeb.UserSocket
# Serve at "/" the static files from "priv/static" directory.
#
# You should set gzip to true if you are running phoenix.digest
# when deploying your static files in production.
plug Plug.Static,
at: "/", from: :hello, gzip: false,
only: ~w(css fonts images js favicon.ico robots.txt)
# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
if code_reloading? do
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
plug Phoenix.CodeReloader
end
plug Plug.Logger
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Poison
plug Plug.MethodOverride
plug Plug.Head
# The session will be stored in the cookie and signed,
# this means its contents can be read but not tampered with.
# Set :encryption_salt if you would also like to encrypt it.
plug Plug.Session,
store: :cookie,
key: "_hello_key",
signing_salt: "M0cGJtXU"
plug HelloWeb.Router
@doc """
Callback invoked for dynamically configuring the endpoint.
It receives the endpoint configuration and checks if
configuration should be loaded from the system environment.
"""
def init(_key, config) do
IO.puts :stderr, "called HelloWeb.Endpoint.init"
if config[:load_from_system_env] do
port = System.get_env("PORT") || raise "expected the PORT environment variable to be set"
{:ok, Keyword.put(config, :http, [:inet6, port: port])}
else
{:ok, config}
end
end
end

View File

@ -0,0 +1,24 @@
defmodule HelloWeb.Gettext do
@moduledoc """
A module providing Internationalization with a gettext-based API.
By using [Gettext](https://hexdocs.pm/gettext),
your module gains a set of macros for translations, for example:
import HelloWeb.Gettext
# Simple translation
gettext "Here is the string to translate"
# Plural translation
ngettext "Here is the string to translate",
"Here are the strings to translate",
3
# Domain-based translation
dgettext "errors", "Here is the error message to translate"
See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
"""
use Gettext, otp_app: :hello
end

View File

@ -0,0 +1,27 @@
defmodule HelloWeb.Router do
use HelloWeb, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end
pipeline :api do
plug :accepts, ["json"]
end
scope "/", HelloWeb do
pipe_through :browser # Use the default browser stack
get "/", PageController, :index
get "/:name", PageController, :show
end
# Other scopes may use custom stacks.
# scope "/api", HelloWeb do
# pipe_through :api
# end
end

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title><%= @title %></title>
<link rel="stylesheet" type="text/css" href="<%= static_path(@conn, "/css/app.css") %>">
</head>
<body>
<div class="container">
<header class="header">
<nav role="navigation">
</nav>
</header>
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<main role="main">
<%= render @view_module, @view_template, assigns %>
</main>
</div> <!-- /container -->
<script src="<%= static_path(@conn, "/js/app.js") %>"></script>
</body>
</html>

View File

@ -0,0 +1,12 @@
<div class="jumbotron">
<h2><%= @greeting %></h2>
<p>$TARGET = <%= @target %></p>
</div>
<h3>Environment</h3>
<ul>
<%= for key <- Enum.sort(Map.keys(@env)) do %>
<li><%= key %> = <%= Map.get(@env, key) %></li>
<% end %>
</ul>

View File

@ -0,0 +1,44 @@
defmodule HelloWeb.ErrorHelpers do
@moduledoc """
Conveniences for translating and building error messages.
"""
use Phoenix.HTML
@doc """
Generates tag for inlined form input errors.
"""
def error_tag(form, field) do
Enum.map(Keyword.get_values(form.errors, field), fn (error) ->
content_tag :span, translate_error(error), class: "help-block"
end)
end
@doc """
Translates an error message using gettext.
"""
def translate_error({msg, opts}) do
# When using gettext, we typically pass the strings we want
# to translate as a static argument:
#
# # Translate "is invalid" in the "errors" domain
# dgettext "errors", "is invalid"
#
# # Translate the number of files with plural rules
# dngettext "errors", "1 file", "%{count} files", count
#
# Because the error messages we show in our forms and APIs
# are defined inside Ecto, we need to translate them dynamically.
# This requires us to call the Gettext module passing our gettext
# backend as first argument.
#
# Note we use the "errors" domain, which means translations
# should be written to the errors.po file. The :count option is
# set by Ecto and indicates we should also apply plural rules.
if count = opts[:count] do
Gettext.dngettext(HelloWeb.Gettext, "errors", msg, msg, count, opts)
else
Gettext.dgettext(HelloWeb.Gettext, "errors", msg, opts)
end
end
end

View File

@ -0,0 +1,16 @@
defmodule HelloWeb.ErrorView do
use HelloWeb, :view
# If you want to customize a particular status code
# for a certain format, you may uncomment below.
# def render("500.html", _assigns) do
# "Internal Server Error"
# end
# By default, Phoenix returns the status message from
# the template name. For example, "404.html" becomes
# "Not Found".
def template_not_found(template, _assigns) do
Phoenix.Controller.status_message_from_template(template)
end
end

View File

@ -0,0 +1,3 @@
defmodule HelloWeb.HelloView do
use HelloWeb, :view
end

View File

@ -0,0 +1,3 @@
defmodule HelloWeb.LayoutView do
use HelloWeb, :view
end

View File

@ -0,0 +1,3 @@
defmodule HelloWeb.PageView do
use HelloWeb, :view
end

View File

@ -0,0 +1,44 @@
defmodule Hello.Mixfile do
use Mix.Project
def project do
[
app: :hello,
version: "0.0.1",
elixir: "~> 1.4",
elixirc_paths: elixirc_paths(Mix.env),
compilers: [:phoenix, :gettext] ++ Mix.compilers,
start_permanent: Mix.env == :prod,
deps: deps()
]
end
# Configuration for the OTP application.
#
# Type `mix help compile.app` for more information.
def application do
[
mod: {Hello.Application, []},
extra_applications: [:logger, :runtime_tools]
]
end
# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]
# Specifies your project dependencies.
#
# Type `mix help deps` for examples and options.
defp deps do
[
{:phoenix, "~> 1.3.2"},
{:phoenix_pubsub, "~> 1.0"},
{:phoenix_html, "~> 2.10"},
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:gettext, "~> 0.11"},
{:cowboy, "~> 1.0"},
{:distillery, "~> 1.5"}
]
end
end

View File

@ -0,0 +1,15 @@
%{
"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"},
"distillery": {:hex, :distillery, "1.5.2", "eec18b2d37b55b0bcb670cf2bcf64228ed38ce8b046bb30a9b636a6f5a4c0080", [:mix], [], "hexpm"},
"file_system": {:hex, :file_system, "0.2.5", "a3060f063b116daf56c044c273f65202e36f75ec42e678dc10653056d3366054", [:mix], [], "hexpm"},
"gettext": {:hex, :gettext, "0.15.0", "40a2b8ce33a80ced7727e36768499fc9286881c43ebafccae6bab731e2b2b8ce", [:mix], [], "hexpm"},
"mime": {:hex, :mime, "1.3.0", "5e8d45a39e95c650900d03f897fbf99ae04f60ab1daa4a34c7a20a5151b7a5fe", [:mix], [], "hexpm"},
"phoenix": {:hex, :phoenix, "1.3.2", "2a00d751f51670ea6bc3f2ba4e6eb27ecb8a2c71e7978d9cd3e5de5ccf7378bd", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_html": {:hex, :phoenix_html, "2.11.2", "86ebd768258ba60a27f5578bec83095bdb93485d646fc4111db8844c316602d6", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.1.5", "8d4c9b1ef9ca82deee6deb5a038d6d8d7b34b9bb909d99784a49332e0d15b3dc", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2 or ~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.2", "bfa7fd52788b5eaa09cb51ff9fcad1d9edfeb68251add458523f839392f034c1", [:mix], [], "hexpm"},
"plug": {:hex, :plug, "1.5.1", "1ff35bdecfb616f1a2b1c935ab5e4c47303f866cb929d2a76f0541e553a58165", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.3", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
"ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"},
}

View File

@ -0,0 +1,11 @@
## `msgid`s in this file come from POT (.pot) files.
##
## Do not add, change, or remove `msgid`s manually here as
## they're tied to the ones in the corresponding POT file
## (with the same domain).
##
## Use `mix gettext.extract --merge` or `mix gettext.merge`
## to merge POT files into PO files.
msgid ""
msgstr ""
"Language: en\n"

View File

@ -0,0 +1,10 @@
## This file is a PO Template file.
##
## `msgid`s here are often extracted from source code.
## Add new translations manually only if they're dynamic
## translations that can't be statically extracted.
##
## Run `mix gettext.extract` to bring this file up to
## date. Leave `msgstr`s empty as changing them here as no
## effect: edit them in PO (`.po`) files instead.

View File

@ -0,0 +1,53 @@
# Import all plugins from `rel/plugins`
# They can then be used by adding `plugin MyPlugin` to
# either an environment, or release definition, where
# `MyPlugin` is the name of the plugin module.
Path.join(["rel", "plugins", "*.exs"])
|> Path.wildcard()
|> Enum.map(&Code.eval_file(&1))
use Mix.Releases.Config,
# This sets the default release built by `mix release`
default_release: :default,
# This sets the default environment used by `mix release`
default_environment: Mix.env()
# For a full list of config options for both releases
# and environments, visit https://hexdocs.pm/distillery/configuration.html
# You may define one or more environments in this file,
# an environment's settings will override those of a release
# when building in that environment, this combination of release
# and environment configuration is called a profile
environment :dev do
# If you are running Phoenix, you should make sure that
# server: true is set and the code reloader is disabled,
# even in dev mode.
# It is recommended that you build with MIX_ENV=prod and pass
# the --env flag to Distillery explicitly if you want to use
# dev mode.
set dev_mode: true
set include_erts: false
set cookie: :"Bps5@RVvPgL9c~C~D(<o2o>DCQ5*Iu!<h[wb{zngH1o&j?EtGh~U$nB9kBamX(LF"
end
environment :prod do
set include_erts: true
set include_src: false
set cookie: :"oj@tGtLJFd=:Qn]]9d;6mI^.K}*eJuMF(&@`(s}zCIgba<J:;c`aLvLjw@ZN!Z@3"
end
# You may define one or more releases in this file.
# If you have not set a default release, or selected one
# when running `mix release`, the first release in the file
# will be used by default
release :hello do
set version: current_version(:hello)
set applications: [
:runtime_tools
]
end

View File

@ -0,0 +1,13 @@
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-elixir
namespace: default
spec:
template:
spec:
containers:
- image: docker.io/{username}/helloworld-elixir
env:
- name: TARGET
value: "elixir Sample v1"

View File

@ -0,0 +1,8 @@
defmodule HelloWeb.PageControllerTest do
use HelloWeb.ConnCase
test "GET /", %{conn: conn} do
conn = get conn, "/"
assert html_response(conn, 200) =~ "Welcome to Knative and Elixir"
end
end

View File

@ -0,0 +1,16 @@
defmodule HelloWeb.ErrorViewTest do
use HelloWeb.ConnCase, async: true
# Bring render/3 and render_to_string/3 for testing custom views
import Phoenix.View
test "renders 404.html" do
assert render_to_string(HelloWeb.ErrorView, "404.html", []) ==
"Not Found"
end
test "renders 500.html" do
assert render_to_string(HelloWeb.ErrorView, "500.html", []) ==
"Internal Server Error"
end
end

View File

@ -0,0 +1,3 @@
defmodule HelloWeb.LayoutViewTest do
use HelloWeb.ConnCase, async: true
end

View File

@ -0,0 +1,3 @@
defmodule HelloWeb.PageViewTest do
use HelloWeb.ConnCase, async: true
end

View File

@ -0,0 +1,33 @@
defmodule HelloWeb.ChannelCase do
@moduledoc """
This module defines the test case to be used by
channel tests.
Such tests rely on `Phoenix.ChannelTest` and also
import other functionality to make it easier
to build common datastructures and query the data layer.
Finally, if the test case interacts with the database,
it cannot be async. For this reason, every test runs
inside a transaction which is reset at the beginning
of the test unless the test case is marked as async.
"""
use ExUnit.CaseTemplate
using do
quote do
# Import conveniences for testing with channels
use Phoenix.ChannelTest
# The default endpoint for testing
@endpoint HelloWeb.Endpoint
end
end
setup _tags do
:ok
end
end

View File

@ -0,0 +1,34 @@
defmodule HelloWeb.ConnCase do
@moduledoc """
This module defines the test case to be used by
tests that require setting up a connection.
Such tests rely on `Phoenix.ConnTest` and also
import other functionality to make it easier
to build common datastructures and query the data layer.
Finally, if the test case interacts with the database,
it cannot be async. For this reason, every test runs
inside a transaction which is reset at the beginning
of the test unless the test case is marked as async.
"""
use ExUnit.CaseTemplate
using do
quote do
# Import conveniences for testing with connections
use Phoenix.ConnTest
import HelloWeb.Router.Helpers
# The default endpoint for testing
@endpoint HelloWeb.Endpoint
end
end
setup _tags do
{:ok, conn: Phoenix.ConnTest.build_conn()}
end
end

View File

@ -0,0 +1,2 @@
ExUnit.start()

View File

@ -0,0 +1,6 @@
.stack-work/
*.cabal
*~
/.idea/
/dist/
/out/

View File

@ -0,0 +1,22 @@
# Use the official Haskell image to create a build artifact.
# https://hub.docker.com/_/haskell/
FROM haskell:8.2.2 as builder
# Copy local code to the container image.
WORKDIR /app
COPY . .
# Build and test our code, then build the “helloworld-haskell-exe” executable.
RUN stack setup
RUN stack build --copy-bins
# Use a Docker multi-stage build to create a lean production image.
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM fpco/haskell-scratch:integer-gmp
# Copy the "helloworld-haskell-exe" executable from the builder stage to the production image.
WORKDIR /root/
COPY --from=builder /root/.local/bin/helloworld-haskell-exe .
# Run the web service on container startup.
CMD ["./helloworld-haskell-exe"]

View File

@ -0,0 +1,183 @@
# Hello World - Haskell
A simple web app written in Haskell that you can use for testing. It reads in an
env variable `TARGET` and prints "Hello \${TARGET}!". If TARGET is not
specified, it will use "World" as the TARGET.
## Prerequisites
- A Kubernetes cluster with Knative installed and DNS configured. Follow the
[Knative installation instructions](https://knative.dev/docs/install/) if you need to create
one.
- [Docker](https://www.docker.com) installed and running on your local machine,
and a Docker Hub account configured (we'll use it for a container registry).
## Recreating the sample code
While you can clone all of the code from this directory, hello world apps are
generally more useful if you build them step-by-step. The following instructions
recreate the source files from this folder.
1. Create a new file named `stack.yaml` and paste the following code:
```yaml
flags: {}
packages:
- .
extra-deps: []
resolver: lts-10.7
```
1. Create a new file named `package.yaml` and paste the following code
```yaml
name: helloworld-haskell
version: 0.1.0.0
dependencies:
- base >= 4.7 && < 5
- scotty
- text
executables:
helloworld-haskell-exe:
main: Main.hs
source-dirs: app
ghc-options:
- -threaded
- -rtsopts
- -with-rtsopts=-N
```
1. Create a `app` folder, then create a new file named `Main.hs` in that folder
and paste the following code. This code creates a basic web server which
listens on port 8080:
```haskell
{-# LANGUAGE OverloadedStrings #-}
import Data.Maybe
import Data.Monoid ((<>))
import Data.Text.Lazy (Text)
import Data.Text.Lazy
import System.Environment (lookupEnv)
import Web.Scotty (ActionM, ScottyM, scotty)
import Web.Scotty.Trans
main :: IO ()
main = do
t <- fromMaybe "World" <$> lookupEnv "TARGET"
pStr <- fromMaybe "8080" <$> lookupEnv "PORT"
let p = read pStr :: Int
scotty p (route t)
route :: String -> ScottyM()
route t = get "/" $ hello t
hello :: String -> ActionM()
hello t = text $ pack ("Hello " ++ t)
```
1. In your project directory, create a file named `Dockerfile` and copy the code
block below into it.
```docker
# Use the official Haskell image to create a build artifact.
# https://hub.docker.com/_/haskell/
FROM haskell:8.2.2 as builder
# Copy local code to the container image.
WORKDIR /app
COPY . .
# Build and test our code, then build the “helloworld-haskell-exe” executable.
RUN stack setup
RUN stack build --copy-bins
# Use a Docker multi-stage build to create a lean production image.
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM fpco/haskell-scratch:integer-gmp
# Copy the "helloworld-haskell-exe" executable from the builder stage to the production image.
WORKDIR /root/
COPY --from=builder /root/.local/bin/helloworld-haskell-exe .
# Run the web service on container startup.
CMD ["./helloworld-haskell-exe"]
```
1. Create a new file, `service.yaml` and copy the following service definition
into the file. Make sure to replace `{username}` with your Docker Hub
username.
```yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-haskell
namespace: default
spec:
template:
spec:
containers:
- image: docker.io/{username}/helloworld-haskell
env:
- name: TARGET
value: "Haskell Sample v1"
```
## Build and deploy this sample
Once you have recreated the sample code files (or used the files in the sample
folder) you're ready to build and deploy the sample app.
1. Use Docker to build the sample code into a container. To build and push with
Docker Hub, enter these commands replacing `{username}` with your Docker Hub
username:
```bash
# Build the container on your local machine
docker build -t {username}/helloworld-haskell .
# Push the container to docker registry
docker push {username}/helloworld-haskell
```
1. After the build has completed and the container is pushed to Docker Hub, you
can deploy the app into your cluster. Ensure that the container image value
in `service.yaml` matches the container you built in the previous step. Apply
the configuration using `kubectl`:
```bash
kubectl apply --filename service.yaml
```
1. Now that your service is created, Knative will perform the following steps:
- Create a new immutable revision for this version of the app.
- Network programming to create a route, ingress, service, and load balance
for your app.
- Automatically scale your pods up and down (including to zero active pods).
1. To find the URL for your service, enter:
```
kubectl get ksvc helloworld-haskell --output=custom-columns=NAME:.metadata.name,URL:.status.url
NAME URL
helloworld-haskell http://helloworld-haskell.default.1.2.3.4.sslip.io
```
1. Now you can make a request to your app and see the result. Replace
the URL below with the URL returned in the previous command.
```bash
curl http://helloworld-haskell.default.1.2.3.4.sslip.io
Hello world: Haskell Sample v1
```
## Removing the sample app deployment
To remove the sample app from your cluster, delete the service record:
```bash
kubectl delete --filename service.yaml
```

View File

@ -0,0 +1,22 @@
{-# LANGUAGE OverloadedStrings #-}
import Data.Maybe
import Data.Monoid ((<>))
import Data.Text.Lazy (Text)
import Data.Text.Lazy
import System.Environment (lookupEnv)
import Web.Scotty (ActionM, ScottyM, scotty)
import Web.Scotty.Trans
main :: IO ()
main = do
t <- fromMaybe "World" <$> lookupEnv "TARGET"
pStr <- fromMaybe "8080" <$> lookupEnv "PORT"
let p = read pStr :: Int
scotty p (route t)
route :: String -> ScottyM()
route t = get "/" $ hello t
hello :: String -> ActionM()
hello t = text $ pack ("Hello " ++ t)

View File

@ -0,0 +1,15 @@
name: helloworld-haskell
version: 0.1.0.0
dependencies:
- base >= 4.7 && < 5
- scotty
- text
executables:
helloworld-haskell-exe:
main: Main.hs
source-dirs: app
ghc-options:
- -threaded
- -rtsopts
- -with-rtsopts=-N

View File

@ -0,0 +1,13 @@
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-haskell
namespace: default
spec:
template:
spec:
containers:
- image: docker.io/{username}/helloworld-haskell
env:
- name: TARGET
value: "Haskell Sample v1"

View File

@ -0,0 +1,5 @@
flags: {}
packages:
- .
extra-deps: []
resolver: lts-10.7

View File

@ -0,0 +1 @@
target/

View File

@ -0,0 +1,14 @@
Thumbs.db
.DS_Store
.gradle
build/
target/
out/
.idea
*.iml
*.ipr
*.iws
.project
.settings
.classpath
.factorypath

View File

@ -0,0 +1 @@
-Dcom.sun.management.jmxremote -noverify -XX:TieredStopAtLevel=1

View File

@ -0,0 +1,110 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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://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 java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL =
"https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: : " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}

View File

@ -0,0 +1 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip

View File

@ -0,0 +1,23 @@
# Use the official maven/Java 8 image to create a build artifact.
# https://hub.docker.com/_/maven
FROM maven:3.5-jdk-8-alpine as builder
# Copy local code to the container image.
WORKDIR /app
COPY pom.xml .
COPY src ./src
# Build a release artifact.
RUN mvn package -DskipTests
# Use AdoptOpenJDK for base image.
# It's important to use OpenJDK 8u191 or above that has container support enabled.
# https://hub.docker.com/r/adoptopenjdk/openjdk8
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM adoptopenjdk/openjdk8:jdk8u202-b08-alpine-slim
# Copy the jar to the production image from the builder stage.
COPY --from=builder /app/target/helloworld-1.0.0-SNAPSHOT.jar /helloworld.jar
# Run the web service on container startup.
CMD ["java","-jar","/helloworld.jar"]

View File

@ -0,0 +1,284 @@
# Hello World - Java (Micronaut)
Learn how to deploy a simple web app that is written in Java and uses Micronaut.
This samples uses Docker to build locally. The app reads in a `TARGET` env
variable and then prints "Hello World: \${TARGET}!". If a value for `TARGET` is
not specified, the "NOT SPECIFIED" default value is used.
Use this sample to walk you through the steps of creating and modifying the
sample app, building and pushing your container image to a registry, and then
deploying your app to your Knative cluster.
## Before you begin
You must meet the following requirements to complete this sample:
- A version of the Knative Serving component installed and DNS configured. Follow the
[Knative installation instructions](https://knative.dev/docs/install/) if you need
to create a Knative cluster.
- The following software downloaded and install on your loacal machine:
- [Java SE 8 or later JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html).
- [Micronaut 1.1](https://micronaut.io/).
- [Docker](https://www.docker.com) for building and pushing your container
image.
- [curl](https://curl.haxx.se/) to test the sample app after deployment.
- A [Docker Hub](https://hub.docker.com/) account where you can push your
container image.
**Tip**: You can clone the [Knatve/docs repo](https://github.com/knative/docs)
and then modify the source files. Alternatively, learn more by manually creating
the files yourself.
## Creating and configuring the sample code
To create and configure the source files in the root of your working directory:
1. Create the `pom.xml` file:
```xml
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.micronaut</groupId>
<artifactId>helloworld</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<micronaut.version>1.1.0</micronaut.version>
<jdk.version>1.8</jdk.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<exec.mainClass>com.example.helloworld.Application</exec.mainClass>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-bom</artifactId>
<version>${micronaut.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-validation</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-runtime</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-client</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-server-netty</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${exec.mainClass}</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
```
1. Create the `HelloWorldController.java` file in the
`src/main/java/com/example/helloworld` directory. The
`[ROOT]/src/main/java/com/example/helloworld/HelloWorldController.java` file
handles requests to the root URI `/`.
```java
package com.example.helloworld;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
@Controller("/")
public class HelloWorldController {
@Get(value = "/", produces = MediaType.TEXT_PLAIN)
public String index() {
String target = System.getenv("TARGET");
if (target == null) {
target = "NOT SPECIFIED";
}
return "Hello World: " + target;
}
}
```
1. The Micronaut application is configured via
`src/main/resources/application.yml`:
```yaml
micronaut:
application:
name: helloworld-java-micronaut
server:
port: ${PORT:8080}
```
1. Create the `Dockerfile` file:
```docker
# Use the official maven/Java 8 image to create a build artifact.
# https://hub.docker.com/_/maven
FROM maven:3.5-jdk-8-alpine as builder
# Copy local code to the container image.
WORKDIR /app
COPY pom.xml .
COPY src ./src
# Build a release artifact.
RUN mvn package -DskipTests
# Use AdoptOpenJDK for base image.
# It's important to use OpenJDK 8u191 or above that has container support enabled.
# https://hub.docker.com/r/adoptopenjdk/openjdk8
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM adoptopenjdk/openjdk8:jdk8u202-b08-alpine-slim
# Copy the jar to the production image from the builder stage.
COPY --from=builder /app/target/helloworld-1.0.0-SNAPSHOT.jar /helloworld.jar
# Run the web service on container startup.
CMD ["java","-jar","/helloworld.jar"]
```
1. Create the `service.yaml` file. You must specify your Docker Hub username in
`{username}`. You can also configure the `TARGET`, for example you can modify
the `Micronaut Sample v1` value.
```yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-java-micronaut
namespace: default
spec:
template:
spec:
containers:
- image: docker.io/{username}/helloworld-java-micronaut
env:
- name: TARGET
value: "Micronaut Sample v1"
```
## Building and deploying the sample
To build a container image, push your image to the registry, and then deploy
your sample app to your cluster:
1. Use Docker to build your container image and then push that image to your
Docker Hub registry. You must replace the `{username}` variables in the
following commands with your Docker Hub username.
```bash
# Build the container on your local machine
docker build -t {username}/helloworld-java-micronaut .
# Push the container to docker registry
docker push {username}/helloworld-java-micronaut
```
1. Now that your container image is in the registry, you can deploy it to your
Knative cluster by running the `kubectl apply` command:
```bash
kubectl apply --filename service.yaml
```
Result: A service name `helloworld-java-micronaut` is created in your cluster
along with the following resources:
- A new immutable revision for the version of the app that you just deployed.
- The following networking resources are created for your app:
- route
- ingress
- service
- load balancer
- Auto scaling is enable to allow your pods to scale up to meet traffic, and
also back down to zero when there is no traffic.
## Testing the sample app
To verify that your sample app has been successfully deployed:
1. Retrieve the URL for your service, by running the following `kubectl get`
command:
```bash
kubectl get ksvc helloworld-java-micronaut --output=custom-columns=NAME:.metadata.name,URL:.status.url
```
Example result:
```bash
NAME URL
helloworld-java-micronaut http://helloworld-java-micronaut.default.1.2.3.4.sslip.io
```
1. Now you can make a request to your app and see the result. Replace
the URL below with the URL returned in the previous command.
```bash
curl http://helloworld-java-micronaut.default.1.2.3.4.sslip.io
```
Example result:
```bash
Hello World: Micronaut Sample v1
```
Congratulations on deploying your sample Java app to Knative!
## Removing the sample app deployment
To remove the sample app from your cluster, run the following `kubectl delete`
command:
```bash
kubectl delete --filename service.yaml
```

View File

@ -0,0 +1,173 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.micronaut</groupId>
<artifactId>helloworld</artifactId>
<version>1.0.0-SNAPSHOT</version>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<executions>
<execution>
<id>test-compile</id>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject-java</artifactId>
<version>${micronaut.version}</version>
</path>
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-validation</artifactId>
<version>${micronaut.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</execution>
</executions>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject-java</artifactId>
<version>${micronaut.version}</version>
</path>
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-validation</artifactId>
<version>${micronaut.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer>
<mainClass>${exec.mainClass}</mainClass>
</transformer>
<transformer />
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<executable>java</executable>
<arguments>
<argument>-noverify</argument>
<argument>-Dmicronaut.env.deduction=false</argument>
<argument>-XX:TieredStopAtLevel=1</argument>
<argument>-Dcom.sun.management.jmxremote</argument>
<argument>-classpath</argument>
<classpath />
<argument>${exec.mainClass}</argument>
</arguments>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>apiguardian-api</artifactId>
<groupId>org.apiguardian</groupId>
</exclusion>
<exclusion>
<artifactId>opentest4j</artifactId>
<groupId>org.opentest4j</groupId>
</exclusion>
<exclusion>
<artifactId>junit-platform-commons</artifactId>
<groupId>org.junit.platform</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.3.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>junit-platform-engine</artifactId>
<groupId>org.junit.platform</groupId>
</exclusion>
<exclusion>
<artifactId>apiguardian-api</artifactId>
<groupId>org.apiguardian</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.micronaut.test</groupId>
<artifactId>micronaut-test-junit5</artifactId>
<version>1.0.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>micronaut-test-core</artifactId>
<groupId>io.micronaut.test</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-bom</artifactId>
<version>${micronaut.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<micronaut.version>1.1.0</micronaut.version>
<exec.mainClass>com.example.helloworld.Application</exec.mainClass>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdk.version>1.8</jdk.version>
</properties>
</project>

View File

@ -0,0 +1,5 @@
profile: service
defaultPackage: com.example.helloworld
---
testFramework: junit
sourceLanguage: java

View File

@ -0,0 +1,286 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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://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.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven2 Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# TODO classpath?
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar"
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
wget "$jarUrl" -O "$wrapperJarPath"
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
curl -o "$wrapperJarPath" "$jarUrl"
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

View File

@ -0,0 +1,161 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar"
FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
echo Found %WRAPPER_JAR%
) else (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
echo Finished downloading %WRAPPER_JAR%
)
@REM End of extension
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

View File

@ -0,0 +1,172 @@
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.micronaut</groupId>
<artifactId>helloworld</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<micronaut.version>1.1.0</micronaut.version>
<jdk.version>1.8</jdk.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<exec.mainClass>com.example.helloworld.Application</exec.mainClass>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-bom</artifactId>
<version>${micronaut.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-validation</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-runtime</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-client</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-server-netty</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micronaut.test</groupId>
<artifactId>micronaut-test-junit5</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${exec.mainClass}</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<executable>java</executable>
<arguments>
<!-- Non production flags -->
<argument>-noverify</argument>
<argument>-Dmicronaut.env.deduction=false</argument>
<argument>-XX:TieredStopAtLevel=1</argument>
<argument>-Dcom.sun.management.jmxremote</argument>
<argument>-classpath</argument>
<classpath/>
<argument>${exec.mainClass}</argument>
</arguments>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject-java</artifactId>
<version>${micronaut.version}</version>
</path>
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-validation</artifactId>
<version>${micronaut.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
<executions>
<execution>
<id>test-compile</id>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject-java</artifactId>
<version>${micronaut.version}</version>
</path>
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-validation</artifactId>
<version>${micronaut.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@ -0,0 +1,12 @@
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: helloworld-java-micronaut
spec:
template:
spec:
containers:
- image: docker.io/{username}/helloworld-java-micronaut
env:
- name: TARGET
value: "Micronaut Sample v1"

View File

@ -0,0 +1,10 @@
package com.example.helloworld;
import io.micronaut.runtime.Micronaut;
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class);
}
}

View File

@ -0,0 +1,18 @@
package com.example.helloworld;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
@Controller("/")
public class HelloWorldController {
@Get(value = "/", produces = MediaType.TEXT_PLAIN)
public String index() {
String target = System.getenv("TARGET");
if (target == null) {
target = "NOT SPECIFIED";
}
return "Hello World: " + target;
}
}

View File

@ -0,0 +1,5 @@
micronaut:
application:
name: helloworld-micronaut
server:
port: ${PORT:8080}

View File

@ -0,0 +1,15 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<withJansi>true</withJansi>
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -0,0 +1,28 @@
package com.example.helloworld;
import static org.junit.jupiter.api.Assertions.assertEquals;
import javax.inject.Inject;
import org.junit.jupiter.api.Test;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.client.RxHttpClient;
import io.micronaut.http.client.annotation.Client;
import io.micronaut.test.annotation.MicronautTest;
@MicronautTest
public class HelloWorldControllerTest {
@Inject
@Client("/")
RxHttpClient client;
@Test
public void testIndex() throws Exception {
HttpResponse<String> response = client.toBlocking().exchange("/", String.class);
assertEquals(HttpStatus.OK, response.status());
assertEquals("Hello World: NOT SPECIFIED", response.body());
}
}

View File

@ -0,0 +1,11 @@
README.md
_index.md
.editorconfig
.gitignore
.git
target/
.settings
.idea
.project
.classpath
*.iml

View File

@ -0,0 +1,8 @@
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false

View File

@ -0,0 +1,31 @@
/target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/build/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VScode ###
.vscode
st
**/*.log

View File

@ -0,0 +1,117 @@
/*
* Copyright 2007-present the original author or authors.
*
* 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://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 java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.3";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + " .jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}

View File

@ -0,0 +1,2 @@
distributionUrl=http://repo2.maven.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip
wrapperUrl=http://repo2.maven.org/maven2/io/takari/maven-wrapper/0.5.4/maven-wrapper-0.5.4.jar

View File

@ -0,0 +1,18 @@
FROM quay.io/rhdevelopers/quarkus-java-builder as builder
COPY . /project
WORKDIR /project
# uncomment this to set the MAVEN_MIRROR_URL of your choice, to make faster builds
# ARG MAVEN_MIRROR_URL=<your-maven-mirror-url>
# e.g.
#ARG MAVEN_MIRROR_URL=http://192.168.64.1:8081/nexus/content/groups/public
RUN /usr/local/bin/entrypoint-run.sh mvn -DskipTests clean package
FROM fabric8/java-jboss-openjdk8-jdk:1.5.4
USER jboss
ENV JAVA_APP_DIR=/deployments
COPY --from=builder /project/target/lib/* /deployments/lib/
COPY --from=builder /project/target/*-runner.jar /deployments/app.jar
ENTRYPOINT [ "/deployments/run-java.sh" ]

Some files were not shown because too many files have changed in this diff Show More