Skip to content

Commit 7f06924

Browse files
committed
add build-controller.sh script into code generator
Pulls the scripts/build-controller.sh script and all necessary Bash library files from the original repository in order to be able to call `make build-controller SERVICE=$SERVICE` from the code-generator repository.
1 parent e012998 commit 7f06924

File tree

6 files changed

+515
-1
lines changed

6 files changed

+515
-1
lines changed

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ SHELL := /bin/bash # Use bash syntax
33
# Set up variables
44
GO111MODULE=on
55

6+
AWS_SERVICE=$(shell echo $(SERVICE) | tr '[:upper:]' '[:lower:]')
7+
68
# Build ldflags
79
VERSION ?= "v0.0.0"
810
GITCOMMIT=$(shell git rev-parse HEAD)
@@ -15,7 +17,7 @@ GO_LDFLAGS=-ldflags "-X main.version=$(VERSION) \
1517
# aws-sdk-go/private/model/api package is gated behind a build tag "codegen"...
1618
GO_TAGS=-tags codegen
1719

18-
.PHONY: all build-ack-generate test
20+
.PHONY: all build-ack-generate build-controller test
1921

2022
all: test
2123

@@ -24,6 +26,10 @@ build-ack-generate: ## Build ack-generate binary
2426
@go build ${GO_TAGS} ${GO_LDFLAGS} -o bin/ack-generate cmd/ack-generate/main.go
2527
@echo "ok."
2628

29+
build-controller: build-ack-generate ## Generate controller code for SERVICE
30+
@./scripts/install-controller-gen.sh
31+
@./scripts/build-controller.sh $(AWS_SERVICE)
32+
2733
test: ## Run code tests
2834
go test ${GO_TAGS} ./...
2935

scripts/build-controller.sh

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#!/usr/bin/env bash
2+
3+
# A script that builds a single ACK service controller for an AWS service API
4+
5+
set -eo pipefail
6+
7+
SCRIPTS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
8+
ROOT_DIR="$SCRIPTS_DIR/.."
9+
BIN_DIR="$ROOT_DIR/bin"
10+
11+
source "$SCRIPTS_DIR/lib/common.sh"
12+
source "$SCRIPTS_DIR/lib/k8s.sh"
13+
14+
check_is_installed controller-gen "You can install controller-gen with the helper scripts/install-controller-gen.sh"
15+
16+
if ! k8s_controller_gen_version_equals "$CONTROLLER_TOOLS_VERSION"; then
17+
echo "FATAL: Existing version of controller-gen "`controller-gen --version`", required version is $CONTROLLER_TOOLS_VERSION."
18+
echo "FATAL: Please uninstall controller-gen and install the required version with scripts/install-controller-gen.sh."
19+
exit 1
20+
fi
21+
22+
ACK_GENERATE_CACHE_DIR=${ACK_GENERATE_CACHE_DIR:-"~/.cache/aws-controllers-k8s"}
23+
# The ack-generate code generator is in a separate source code repository,
24+
# typically at $GOPATH/src/github.com/aws-controllers-k8s/code-generator
25+
DEFAULT_ACK_GENERATE_BIN_PATH="$ROOT_DIR/bin/ack-generate"
26+
ACK_GENERATE_BIN_PATH=${ACK_GENERATE_BIN_PATH:-$DEFAULT_ACK_GENERATE_BIN_PATH}
27+
ACK_GENERATE_API_VERSION=${ACK_GENERATE_API_VERSION:-"v1alpha1"}
28+
ACK_GENERATE_CONFIG_PATH=${ACK_GENERATE_CONFIG_PATH:-""}
29+
DEFAULT_TEMPLATES_DIR="$ROOT_DIR/templates"
30+
TEMPLATES_DIR=${TEMPLATES_DIR:-$DEFAULT_TEMPLATES_DIR}
31+
32+
USAGE="
33+
Usage:
34+
$(basename "$0") <service>
35+
36+
<service> should be an AWS service API aliases that you wish to build -- e.g.
37+
's3' 'sns' or 'sqs'
38+
39+
Environment variables:
40+
ACK_GENERATE_CACHE_DIR Overrides the directory used for caching AWS API
41+
models used by the ack-generate tool.
42+
Default: $ACK_GENERATE_CACHE_DIR
43+
ACK_GENERATE_BIN_PATH: Overrides the path to the the ack-generate binary.
44+
Default: $ACK_GENERATE_BIN_PATH
45+
ACK_GENERATE_API_VERSION: Overrides the version of the Kubernetes API objects
46+
generated by the ack-generate apis command. If not
47+
specified, and the service controller has been
48+
previously generated, the latest generated API
49+
version is used. If the service controller has yet
50+
to be generated, 'v1alpha1' is used.
51+
ACK_GENERATE_CONFIG_PATH: Specify a path to the generator config YAML file to
52+
instruct the code generator for the service.
53+
Default: services/{SERVICE}/generator.yaml
54+
K8S_RBAC_ROLE_NAME: Name of the Kubernetes Role to use when generating
55+
the RBAC manifests for the custom resource
56+
definitions.
57+
Default: $K8S_RBAC_ROLE_NAME
58+
"
59+
60+
if [ $# -ne 1 ]; then
61+
echo "ERROR: $(basename "$0") only accepts a single parameter" 1>&2
62+
echo "$USAGE"
63+
exit 1
64+
fi
65+
66+
if [ ! -f $ACK_GENERATE_BIN_PATH ]; then
67+
if is_installed "ack-generate"; then
68+
ACK_GENERATE_BIN_PATH=$(which "ack-generate")
69+
else
70+
echo "ERROR: Unable to find an ack-generate binary.
71+
Either set the ACK_GENERATE_BIN_PATH to a valid location or
72+
run:
73+
74+
make build-ack-generate
75+
76+
from the root directory or install ack-generate using:
77+
78+
go get -u github.com/aws-controllers-k8s/code-generator/cmd/ack-generate" 1>&2
79+
exit 1;
80+
fi
81+
fi
82+
SERVICE=$(echo "$1" | tr '[:upper:]' '[:lower:]')
83+
84+
# Source code for the controller will be in a separate repo, typically in
85+
# $GOPATH/src/github.com/aws-controllers-k8s/$AWS_SERVICE-controller/
86+
DEFAULT_SERVICE_CONTROLLER_SOURCE_PATH="$ROOT_DIR/../$SERVICE-controller"
87+
SERVICE_CONTROLLER_SOURCE_PATH=${SERVICE_CONTROLLER_SOURCE_PATH:-$DEFAULT_SERVICE_CONTROLLER_SOURCE_PATH}
88+
89+
if [[ ! -d $SERVICE_CONTROLLER_SOURCE_PATH ]]; then
90+
echo "Error evaluating SERVICE_CONTROLLER_SOURCE_PATH environment variable:" 1>&2
91+
echo "$SERVICE_CONTROLLER_SOURCE_PATH is not a directory." 1>&2
92+
echo "${USAGE}"
93+
exit 1
94+
fi
95+
96+
K8S_RBAC_ROLE_NAME=${K8S_RBAC_ROLE_NAME:-"ack-$SERVICE-controller"}
97+
98+
# If there's a generator.yaml in the service's directory and the caller hasn't
99+
# specified an override, use that.
100+
if [ -z "$ACK_GENERATE_CONFIG_PATH" ]; then
101+
if [ -f "$SERVICE_CONTROLLER_SOURCE_PATH/generator.yaml" ]; then
102+
ACK_GENERATE_CONFIG_PATH="$SERVICE_CONTROLLER_SOURCE_PATH/generator.yaml"
103+
fi
104+
fi
105+
106+
ag_args="$SERVICE -o $SERVICE_CONTROLLER_SOURCE_PATH --templates-dir $TEMPLATES_DIR"
107+
if [ -n "$ACK_GENERATE_CACHE_DIR" ]; then
108+
ag_args="$ag_args --cache-dir $ACK_GENERATE_CACHE_DIR"
109+
fi
110+
111+
apis_args="apis $ag_args"
112+
if [ -n "$ACK_GENERATE_API_VERSION" ]; then
113+
apis_args="$apis_args --version $ACK_GENERATE_API_VERSION"
114+
fi
115+
116+
if [ -n "$ACK_GENERATE_CONFIG_PATH" ]; then
117+
ag_args="$ag_args --generator-config-path $ACK_GENERATE_CONFIG_PATH"
118+
apis_args="$apis_args --generator-config-path $ACK_GENERATE_CONFIG_PATH"
119+
fi
120+
121+
echo "Building Kubernetes API objects for $SERVICE"
122+
$ACK_GENERATE_BIN_PATH $apis_args
123+
if [ $? -ne 0 ]; then
124+
exit 2
125+
fi
126+
127+
config_output_dir="$SERVICE_CONTROLLER_SOURCE_PATH/config/"
128+
129+
pushd $SERVICE_CONTROLLER_SOURCE_PATH/apis/$ACK_GENERATE_API_VERSION 1>/dev/null
130+
131+
echo "Generating deepcopy code for $SERVICE"
132+
controller-gen object:headerFile=$TEMPLATES_DIR/boilerplate.txt paths=./...
133+
134+
echo "Generating custom resource definitions for $SERVICE"
135+
# Latest version of controller-gen (master) is required for following two reasons
136+
# a) support for pointer values in map https://github.com/kubernetes-sigs/controller-tools/pull/317
137+
# b) support for float type (allowDangerousTypes) https://github.com/kubernetes-sigs/controller-tools/pull/449
138+
controller-gen crd:allowDangerousTypes=true paths=./... output:crd:artifacts:config=$config_output_dir/crd/bases
139+
140+
popd 1>/dev/null
141+
142+
echo "Building service controller for $SERVICE"
143+
controller_args="controller $ag_args"
144+
$ACK_GENERATE_BIN_PATH $controller_args
145+
if [ $? -ne 0 ]; then
146+
exit 2
147+
fi
148+
149+
pushd $SERVICE_CONTROLLER_SOURCE_PATH/pkg/resource 1>/dev/null
150+
151+
echo "Generating RBAC manifests for $SERVICE"
152+
controller-gen rbac:roleName=$K8S_RBAC_ROLE_NAME paths=./... output:rbac:artifacts:config=$config_output_dir/rbac
153+
# controller-gen rbac outputs a ClusterRole definition in a
154+
# $config_output_dir/rbac/role.yaml file. We have some other standard Role
155+
# files for a reader and writer role, so here we rename the `role.yaml` file to
156+
# `cluster-role-controller.yaml` to better reflect what is in that file.
157+
mv $config_output_dir/rbac/role.yaml $config_output_dir/rbac/cluster-role-controller.yaml
158+
159+
popd 1>/dev/null
160+
161+
echo "Running gofmt against generated code for $SERVICE"
162+
gofmt -w "$SERVICE_CONTROLLER_SOURCE_PATH"

scripts/install-controller-gen.sh

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env bash
2+
3+
# ./scripts/install-controller-gen.sh
4+
#
5+
# Checks that the `controller-gen` binary is available on the host system and
6+
# if it is, that it matches the exact version that we require in order to
7+
# standardize the YAML manifests for CRDs and Kubernetes Roles.
8+
#
9+
# If the locally-installed controller-gen does not match the required version,
10+
# prints an error message asking the user to uninstall it.
11+
#
12+
# NOTE: We use this technique of building using `go build` within a temp
13+
# directory because controller-tools does not have a binary release artifact
14+
# for controller-gen.
15+
#
16+
# See: https://github.com/kubernetes-sigs/controller-tools/issues/500
17+
18+
set -eo pipefail
19+
20+
SCRIPTS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
21+
ROOT_DIR="$SCRIPTS_DIR/.."
22+
CONTROLLER_TOOLS_VERSION="v0.4.0"
23+
24+
source "$SCRIPTS_DIR/lib/common.sh"
25+
source "$SCRIPTS_DIR/lib/k8s.sh"
26+
27+
if ! is_installed controller-gen; then
28+
# GOBIN and GOPATH are not always set, so default to GOPATH from `go env`
29+
__GOPATH=$(go env GOPATH)
30+
__install_dir=${GOBIN:-$__GOPATH/bin}
31+
__install_path="$__install_dir/controller-gen"
32+
__work_dir=$(mktemp -d /tmp/controller-gen-XXX)
33+
34+
echo -n "installing controller-gen ${CONTROLLER_TOOLS_VERSION} ... "
35+
cd "$__work_dir"
36+
37+
go mod init tmp 1>/dev/null 2>&1
38+
go get -d "sigs.k8s.io/controller-tools/cmd/controller-gen@${CONTROLLER_TOOLS_VERSION}" 1>/dev/null 2>&1
39+
go build -o "$__work_dir/controller-gen" sigs.k8s.io/controller-tools/cmd/controller-gen 1>/dev/null 2>&1
40+
mv "$__work_dir/controller-gen" "$__install_path"
41+
42+
rm -rf "$WORK_DIR"
43+
echo "ok."
44+
fi

scripts/lib/aws.sh

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env bash
2+
3+
DEFAULT_AWS_CLI_VERSION="2.0.52"
4+
5+
# daws() executes the AWS Python CLI tool from a Docker container.
6+
#
7+
# Instead of relying on developers having a particular version of the AWS
8+
# Python CLI tool, this method allows a specific version of the CLI tool to be
9+
# executed within a Docker container.
10+
#
11+
# You call the daws function just like you were calling the `aws` CLI tool.
12+
#
13+
# Usage:
14+
#
15+
# daws SERVICE COMMAND [OPTIONS]
16+
#
17+
# Example:
18+
#
19+
# daws ecr describe-repositories --repository-name my-repo
20+
#
21+
# To use a specific version of the AWS CLI, set the ACK_AWS_CLI_IMAGE_VERSION
22+
# environment variable, otherwise the value of DEFAULT_AWS_CLI_VERSION is used.
23+
daws() {
24+
aws_cli_img_version=${ACK_AWS_CLI_IMAGE_VERSION:-$DEFAULT_AWS_CLI_VERSION}
25+
aws_cli_img="amazon/aws-cli:$aws_cli_img_version"
26+
docker run --rm -v ~/.aws:/root/.aws:z -v $(pwd):/aws "$aws_cli_img" "$@"
27+
}
28+
29+
# aws_check_credentials() calls the STS::GetCallerIdentity API call and
30+
# verifies that there is a local identity for running AWS commands
31+
aws_check_credentials() {
32+
echo -n "checking AWS credentials ... "
33+
daws sts get-caller-identity --query "Account" >/dev/null ||
34+
( echo "\nFATAL: No AWS credentials found. Please run \`aws configure\` to set up the CLI for your credentials." && exit 1)
35+
echo "ok."
36+
}
37+
38+
# generate_aws_temp_creds function will generate temporary AWS CREDENTIALS which are valid for 900 seconds
39+
aws_generate_temp_creds() {
40+
__uuid=$(uuidgen | cut -d'-' -f1 | tr '[:upper:]' '[:lower:]')
41+
42+
if [ -z "$AWS_ROLE_ARN" ]; then
43+
printf "Missing input Role ARN, exiting...\n"
44+
exit 1
45+
fi
46+
47+
JSON=$(daws sts assume-role \
48+
--role-arn "$AWS_ROLE_ARN" \
49+
--role-session-name tmp-role-"$__uuid" \
50+
--duration-seconds 900 \
51+
--output json || exit 1)
52+
53+
AWS_ACCESS_KEY_ID=$(echo "${JSON}" | jq --raw-output ".Credentials[\"AccessKeyId\"]")
54+
AWS_SECRET_ACCESS_KEY=$(echo "${JSON}" | jq --raw-output ".Credentials[\"SecretAccessKey\"]")
55+
AWS_SESSION_TOKEN=$(echo "${JSON}" | jq --raw-output ".Credentials[\"SessionToken\"]")
56+
}
57+
58+
aws_account_id() {
59+
JSON=$(daws sts get-caller-identity --output json || exit 1)
60+
echo "${JSON}" | jq --raw-output ".Account"
61+
}

scripts/lib/common.sh

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/usr/bin/env bash
2+
3+
# setting the -x option if debugging is true
4+
if [[ "${DEBUG:-"false"}" = "true" ]]; then
5+
set -x
6+
fi
7+
8+
# check_is_installed checks to see if the supplied executable is installed and
9+
# exits if not. An optional second argument is an extra message to display when
10+
# the supplied executable is not installed.
11+
#
12+
# Usage:
13+
#
14+
# check_is_installed PROGRAM [ MSG ]
15+
#
16+
# Example:
17+
#
18+
# check_is_installed kind "You can install kind with the helper scripts/install-kind.sh"
19+
check_is_installed() {
20+
local __name="$1"
21+
local __extra_msg="$2"
22+
if ! is_installed "$__name"; then
23+
echo "FATAL: Missing requirement '$__name'"
24+
echo "Please install $__name before running this script."
25+
if [[ -n $__extra_msg ]]; then
26+
echo ""
27+
echo "$__extra_msg"
28+
echo ""
29+
fi
30+
exit 1
31+
fi
32+
}
33+
34+
is_installed() {
35+
local __name="$1"
36+
if $(which $__name >/dev/null 2>&1); then
37+
return 0
38+
else
39+
return 1
40+
fi
41+
}
42+
43+
display_timelines() {
44+
echo ""
45+
echo "Displaying all step durations."
46+
echo "TIMELINE: Docker build took $DOCKER_BUILD_DURATION seconds."
47+
echo "TIMELINE: Upping test cluster took $UP_CLUSTER_DURATION seconds."
48+
echo "TIMELINE: Base image integration tests took $BASE_INTEGRATION_DURATION seconds."
49+
echo "TIMELINE: Current image integration tests took $LATEST_INTEGRATION_DURATION seconds."
50+
echo "TIMELINE: Down processes took $DOWN_DURATION seconds."
51+
}
52+
53+
should_execute() {
54+
if [[ "$TEST_PASS" -ne 0 ]]; then
55+
echo "NOTE: Skipping operation '$1'. Test is already marked as failed."
56+
return 1
57+
else
58+
return 0
59+
fi
60+
}
61+
62+
# filenoext returns just the name of the supplied filename without the
63+
# extension
64+
filenoext() {
65+
local __name="$1"
66+
local __filename=$( basename "$__name" )
67+
# How much do I despise Bash?!
68+
echo "${__filename%.*}"
69+
}
70+
71+
DEFAULT_DEBUG_PREFIX="DEBUG: "
72+
73+
# debug_msg prints out a supplied message if the DEBUG environs variable is
74+
# set. An optional second argument indicates the "indentation level" for the
75+
# message. If the indentation level argument is missing, we look for the
76+
# existence of an environs variable called "indent_level" and use that.
77+
debug_msg() {
78+
local __msg=${1:-}
79+
local __indent_level=${2:-}
80+
local __debug="${DEBUG:-""}"
81+
local __debug_prefix="${DEBUG_PREFIX:-$DEFAULT_DEBUG_PREFIX}"
82+
if [ ! -n "$__debug" ]; then
83+
return 0
84+
fi
85+
__indent=""
86+
if [ -n "$__indent_level" ]; then
87+
__indent="$( for each in $( seq 0 $__indent_level ); do printf " "; done )"
88+
fi
89+
echo "$__debug_prefix$__indent$__msg"
90+
}

0 commit comments

Comments
 (0)