-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from humoflife/bootstrap
initial commit
- Loading branch information
Showing
27 changed files
with
1,564 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,3 +19,8 @@ | |
|
||
# Go workspace file | ||
go.work | ||
|
||
kubeconfig | ||
function-shell | ||
function-amd64.xpkg | ||
function-arm64.xpkg |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# syntax=docker/dockerfile:1 | ||
|
||
# We use the latest Go 1.x version unless asked to use something else. | ||
# The GitHub Actions CI job sets this argument for a consistent Go version. | ||
ARG GO_VERSION=1 | ||
|
||
# Setup the base environment. The BUILDPLATFORM is set automatically by Docker. | ||
# The --platform=${BUILDPLATFORM} flag tells Docker to build the function using | ||
# the OS and architecture of the host running the build, not the OS and | ||
# architecture that we're building the function for. | ||
FROM --platform=${BUILDPLATFORM} golang:${GO_VERSION} AS build | ||
|
||
RUN apt-get update && apt-get install -y jq unzip zsh | ||
|
||
# TODO: Install awscli, gcloud | ||
# RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "/tmp/awscliv2.zip" && \ | ||
# unzip "/tmp/awscliv2.zip" && \ | ||
# ./aws/install | ||
|
||
WORKDIR /fn | ||
|
||
# Most functions don't want or need CGo support, so we disable it. | ||
ENV CGO_ENABLED=0 | ||
|
||
# We run go mod download in a separate step so that we can cache its results. | ||
# This lets us avoid re-downloading modules if we don't need to. The type=target | ||
# mount tells Docker to mount the current directory read-only in the WORKDIR. | ||
# The type=cache mount tells Docker to cache the Go modules cache across builds. | ||
RUN --mount=target=. --mount=type=cache,target=/go/pkg/mod go mod download | ||
|
||
# The TARGETOS and TARGETARCH args are set by docker. We set GOOS and GOARCH to | ||
# these values to ask Go to compile a binary for these architectures. If | ||
# TARGETOS and TARGETOS are different from BUILDPLATFORM, Go will cross compile | ||
# for us (e.g. compile a linux/amd64 binary on a linux/arm64 build machine). | ||
ARG TARGETOS | ||
ARG TARGETARCH | ||
|
||
# Build the function binary. The type=target mount tells Docker to mount the | ||
# current directory read-only in the WORKDIR. The type=cache mount tells Docker | ||
# to cache the Go modules cache across builds. | ||
RUN --mount=target=. \ | ||
--mount=type=cache,target=/go/pkg/mod \ | ||
--mount=type=cache,target=/root/.cache/go-build \ | ||
GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /function . | ||
|
||
# Produce the Function image. We use a very lightweight 'distroless' image that | ||
# does not include any of the build tools used in previous stages. | ||
#FROM gcr.io/distroless/base-debian11 AS image | ||
FROM gcr.io/distroless/python3-debian12 AS image | ||
WORKDIR / | ||
COPY --from=build /bin /bin | ||
COPY --from=build /etc /etc | ||
COPY --from=build /lib /lib | ||
COPY --from=build /tmp /tmp | ||
COPY --from=build /usr /usr | ||
COPY --from=build /function /function | ||
EXPOSE 9443 | ||
USER nonroot:nonroot | ||
ENTRYPOINT ["/function"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
REPO_URL="xpkg.upbound.io/upbound/function-shell" | ||
VERSION_TAG="v0.0.3" | ||
#PACKAGE_FILES="function-amd64.xpkg,function-arm64.xpkg" | ||
PACKAGE_FILES="function-arm64.xpkg" | ||
|
||
help: ## Print help for targets with comments | ||
@printf "For more targets and info see comments in Makefile.\n\n" | ||
@grep -E '^[a-zA-Z0-9._-]+:.*## .*$$' Makefile | sort | \ | ||
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-25s\033[0m %s\n", $$1, $$2}' | ||
|
||
#all: docker-build-amd64 docker-build-arm64 xpkg-build-amd64 xpkg-build-arm64 xpkg-push | ||
all: docker-build-amd64 docker-build-arm64 xpkg-build-arm64 xpkg-push | ||
|
||
docker-build-amd64: ## Build AMD64 Docker Image | ||
docker build . --quiet --platform=linux/amd64 --tag runtime-amd64 | ||
|
||
docker-build-arm64: ## Build ARM64 Docker Image | ||
docker build . --quiet --platform=linux/arm64 --tag runtime-arm64 | ||
|
||
xpkg-build-amd64: ## Build AMD64 Composition Function XPKG | ||
crossplane xpkg build \ | ||
--package-root=package \ | ||
--embed-runtime-image=runtime-amd64 \ | ||
--package-file=function-amd64.xpkg | ||
|
||
xpkg-build-arm64: ## Build ARM64 Composition Function XPKG | ||
crossplane xpkg build \ | ||
--package-root=package \ | ||
--embed-runtime-image=runtime-arm64 \ | ||
--package-file=function-arm64.xpkg | ||
|
||
xpkg-push: ## Push XPKG Package Files, Requires Upbound login | ||
up xpkg push ${REPO_URL}:${VERSION_TAG} -f ${PACKAGE_FILES} | ||
|
||
lint: ## Lint the Code | ||
golangci-lint run | ||
|
||
fn-build: ## Build Function Code | ||
go generate ./... | ||
go build . | ||
|
||
test: ## Run Code Tests | ||
go test -v -cover . | ||
|
||
render: ## Render Examples, Requires make debug first | ||
crossplane beta render \ | ||
example/out-of-cluster/xr.yaml \ | ||
example/out-of-cluster/composition.yaml \ | ||
example/out-of-cluster/functions.yaml | ||
|
||
debug: ## Run Shell Function For Rendering Examples | ||
go run . --insecure --debug |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
To get started: | ||
|
||
1. Replace `function-template-go` with your function in `go.mod`, | ||
`package/crossplane.yaml`, and any Go imports. (You can also do this | ||
automatically by running the `./init.sh <function-name>` script.) | ||
2. Update `input/v1beta1/` to reflect your desired input (and run `go generate`) | ||
3. Add your logic to `RunFunction` in `fn.go` | ||
4. Add tests for your logic in `fn_test.go` | ||
5. Update `README.md`, to be about your function! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
# function-shell | ||
|
||
This Crossplane composition function accepts commands to run in a shell and it | ||
returns the output to specified fields. It accepts the following paramereters: | ||
- `shellEnvVarsSecretRef` - referencing environment variables in a | ||
Kubernetes secret. shellEnvVarsSecretRef requires a `name`, a | ||
`namespace` and a `key` for the secret. Inside of it, the shell | ||
expects a JSON structure with key value environment variables. Example: | ||
|
||
``` | ||
{ | ||
"ENV_FOO": "foo value", | ||
"ENV_BAR": "bar value" | ||
} | ||
``` | ||
- 'shellEnvVars' - an array of environment variables with a <b>key</b> and <b>value</b> | ||
each. | ||
- 'shellCommand' - a shell command line that can contain pipes and | ||
redirects and calling multiple programs. | ||
- 'stdoutField' - the path to the field where the shell standard output | ||
should be written. | ||
- 'stderrField' - the path to the field where the shell standard error | ||
output should be written. | ||
|
||
## Practical Example: Obtain Dashboard Ids from Datadog | ||
|
||
The composition calls the `function-shell` instructing it to obtain dashboard ids | ||
from a Datadog account. For this, it specifies the location of a Kubernetes | ||
secret where the `DATADOG_API_KEY` and `DATADOG_APP_KEY` environment variable values | ||
are stored. The Datadog API endpoint is passed in a clear text environment | ||
variable. The shell command uses a `curl` to the endpoint with a header that | ||
contains the access credentials. The command output is piped into jq and | ||
filtered for the ids. | ||
|
||
The `function-shell` writes the dashboard ids to the specified output status field, and | ||
any output that went to stderr into the specified stderr status field. | ||
|
||
The composition is for illustration purposes only. When using the | ||
`function-shell` in your own compositions, you may want to patch function input | ||
from claim and other composition field values. | ||
|
||
Note: `function-shell` has to receive permissions in form of a rolebinding to | ||
read secrets and perform other actions that may be prohibited by default. Below | ||
is a `clusterrolebinding` that will work, but you should exercise appropriate caution when | ||
setting function permissions. | ||
|
||
``` | ||
#!/bin/bash | ||
SA=$(kubectl -n upbound-system get sa -o name | grep function-shell | sed -e 's|serviceaccount\/|upbound-system:|g') | ||
kubectl create clusterrolebinding function-shell-admin-binding --clusterrole cluster-admin --serviceaccount="${SA}" | ||
``` | ||
|
||
```yaml | ||
--- | ||
apiVersion: apiextensions.crossplane.io/v1 | ||
kind: Composition | ||
metadata: | ||
name: shell.upbound.io | ||
spec: | ||
compositeTypeRef: | ||
apiVersion: upbound.io/v1alpha1 | ||
kind: XShell | ||
mode: Pipeline | ||
pipeline: | ||
- step: shell | ||
functionRef: | ||
name: function-shell | ||
input: | ||
apiVersion: shell.fn.crossplane.io/v1beta1 | ||
kind: Parameters | ||
shellEnvVarsSecretRef: | ||
name: datadog-secret | ||
namespace: upbound-system | ||
key: credentials | ||
shellEnvVars: | ||
- key: DATADOG_API_URL | ||
value: "https://api.datadoghq.com/api/v1/dashboard" | ||
shellCommand: | | ||
curl -X GET "${DATADOG_API_URL}" \ | ||
-H "Accept: application/json" \ | ||
-H "DD-API-KEY: ${DATADOG_API_KEY}" \ | ||
-H "DD-APPLICATION-KEY: ${DATADOG_APP_KEY}"|\ | ||
jq '.dashboards[] .id'; | ||
stdoutField: status.atFunction.shell.stdout | ||
stderrField: status.atFunction.shell.stderr | ||
``` | ||
The composition is called through the following `claim`. | ||
|
||
``` | ||
--- | ||
apiVersion: upbound.io/v1alpha1 | ||
kind: Shell | ||
metadata: | ||
name: shell-1 | ||
spec: {} | ||
``` | ||
The API definition is as follows. Note that the API contains status fields that | ||
are populated by `function-shell`. | ||
``` | ||
apiVersion: apiextensions.crossplane.io/v1 | ||
kind: CompositeResourceDefinition | ||
metadata: | ||
name: xshells.upbound.io | ||
spec: | ||
group: upbound.io | ||
names: | ||
kind: XShell | ||
plural: xshells | ||
claimNames: | ||
kind: Shell | ||
plural: shells | ||
defaultCompositionRef: | ||
name: shell.upbound.io | ||
versions: | ||
- name: v1alpha1 | ||
served: true | ||
referenceable: true | ||
schema: | ||
openAPIV3Schema: | ||
properties: | ||
spec: | ||
properties: | ||
cmd: | ||
type: string | ||
status: | ||
properties: | ||
atFunction: | ||
type: object | ||
x-kubernetes-preserve-unknown-fields: true | ||
``` | ||
The `crossplane beta trace` output after applying the in-cluster | ||
shell-claim.yaml is as follows: | ||
``` | ||
cbt shell.upbound.io/shell-1 | ||
NAME SYNCED READY STATUS | ||
Shell/shell-1 (default) True True Available | ||
└─ XShell/shell-1-ttfbh True True Available | ||
``` | ||
The `XShell/shell-1-ttfbh` yaml output looks as per below. Notice the dashboard | ||
ids in the `status.atFunction.shell.stdout` field, and the `curl` stderr output | ||
in the `status.atFunction.shell.stderr` field. | ||
``` | ||
apiVersion: upbound.io/v1alpha1 | ||
kind: XShell | ||
metadata: | ||
creationTimestamp: "2024-04-05T04:47:03Z" | ||
finalizers: | ||
- composite.apiextensions.crossplane.io | ||
generateName: shell-1- | ||
generation: 3 | ||
labels: | ||
crossplane.io/claim-name: shell-1 | ||
crossplane.io/claim-namespace: default | ||
crossplane.io/composite: shell-1-ttfbh | ||
name: shell-1-ttfbh | ||
resourceVersion: "2181275" | ||
uid: 9ebda770-bab3-4822-bd36-739cea5cd35e | ||
spec: | ||
claimRef: | ||
apiVersion: upbound.io/v1alpha1 | ||
kind: Shell | ||
name: shell-1 | ||
namespace: default | ||
compositionRef: | ||
name: shell.upbound.io | ||
compositionRevisionRef: | ||
name: shell.upbound.io-ed28247 | ||
compositionUpdatePolicy: Automatic | ||
resourceRefs: [] | ||
status: | ||
atFunction: | ||
shell: | ||
stderr: "% Total % Received % Xferd Average Speed Time Time Time | ||
\ Current\n Dload Upload Total Spent | ||
\ Left Speed\n\r 0 0 0 0 0 0 0 0 --:--:-- | ||
--:--:-- --:--:-- 0\r 0 0 0 0 0 0 0 0 --:--:-- | ||
--:--:-- --:--:-- 0\r100 4255 100 4255 0 0 9081 0 --:--:-- | ||
--:--:-- --:--:-- 9072" | ||
stdout: |- | ||
"vn4-agn-ftd" | ||
"9pt-bhb-uwj" | ||
"6su-nff-222" | ||
"sm3-cxs-q98" | ||
"ssx-sci-uvi" | ||
"3fd-h4e-7w6" | ||
"qth-94z-ip5" | ||
conditions: | ||
- lastTransitionTime: "2024-04-05T04:47:29Z" | ||
reason: ReconcileSuccess | ||
status: "True" | ||
type: Synced | ||
- lastTransitionTime: "2024-04-05T04:47:29Z" | ||
reason: Available | ||
status: "True" | ||
type: Ready | ||
``` | ||
# Run code generation - see input/generate.go | ||
$ go generate ./... | ||
# Run tests - see fn_test.go | ||
$ go test ./... | ||
# Build the function's runtime image - see Dockerfile | ||
$ docker build . --tag=runtime | ||
# Build a function package - see package/crossplane.yaml | ||
$ crossplane xpkg build -f package --embed-runtime-image=runtime | ||
``` | ||
|
||
[functions]: https://docs.crossplane.io/latest/concepts/composition-functions | ||
[go]: https://go.dev | ||
[function guide]: https://docs.crossplane.io/knowledge-base/guides/write-a-composition-function-in-go | ||
[package docs]: https://pkg.go.dev/github.com/crossplane/function-sdk-go | ||
[docker]: https://www.docker.com | ||
[cli]: https://docs.crossplane.io/latest/cli |
Oops, something went wrong.