Skip to content

Commit

Permalink
Merge pull request #475 from kubernetes-sigs/master
Browse files Browse the repository at this point in the history
👻 ff release-0.2 branch to master
  • Loading branch information
k8s-ci-robot authored Jun 6, 2019
2 parents 38483b2 + be010e1 commit f60c87e
Show file tree
Hide file tree
Showing 22 changed files with 439 additions and 87 deletions.
9 changes: 9 additions & 0 deletions .github/main.workflow
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
workflow "PR Checks" {
on = "pull_request"
resolves = ["verify-emoji"]
}

action "verify-emoji" {
uses = "./hack/release"
secrets = ["GITHUB_TOKEN"]
}
5 changes: 2 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ Please see https://git.k8s.io/community/CLA.md for more info
1. Setup tools
```bash
$ go get -u github.com/golang/dep/cmd/dep
$ go get -u gopkg.in/alecthomas/gometalinter.v2
$ gometalinter.v2 --install # if can't load package, refer: https://github.com/alecthomas/gometalinter/issues/404
$ curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.15.0
```
1. Test
```bash
TRACE=1 ./hack/check-everything.sh
GO111MODULE=on TRACE=1 ./hack/check-everything.sh
```

1 change: 1 addition & 0 deletions Gopkg.lock

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

4 changes: 4 additions & 0 deletions VERSIONING.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ a:
- Patch fix: :bug: (`:bug:`)
- Docs: :book: (`:book:`)
- Infra/Tests/Other: :running: (`:running:`)
- No release note: :ghost: (`:ghost:`)

Use :ghost: (no release note) only for the PRs that change or revert unreleased
changes, which don't deserve a release note. Please don't abuse it.

You can also use the equivalent emoji directly, since GitHub doesn't
render the `:xyz:` aliases in PR titles.
Expand Down
2 changes: 1 addition & 1 deletion examples/builtins/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func main() {
os.Exit(1)
}

// Setup a new controller to Reconciler ReplicaSets
// Setup a new controller to reconcile ReplicaSets
entryLog.Info("Setting up controller")
c, err := controller.New("foo-controller", mgr, controller.Options{
Reconciler: &reconcileReplicaSet{client: mgr.GetClient(), log: log.WithName("reconciler")},
Expand Down
6 changes: 6 additions & 0 deletions examples/crd/pkg/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ type ChaosPodList struct {
Items []ChaosPod `json:"items"`
}

// +kubebuilder:webhook:failurePolicy=fail,groups=chaosapps.metamagical.io,resources=chaospods,verbs=create;update,versions=v1,name=vchaospod.kb.io,path=/validate-chaosapps-metamagical-io-v1-chaospod,mutating=false

var _ webhook.Validator = &ChaosPod{}

// ValidateCreate implements webhookutil.validator so a webhook will be registered for the type
func (c *ChaosPod) ValidateCreate() error {
log.Info("validate create", "name", c.Name)
Expand Down Expand Up @@ -89,6 +93,8 @@ func (c *ChaosPod) ValidateUpdate(old runtime.Object) error {
return nil
}

// +kubebuilder:webhook:failurePolicy=fail,groups=chaosapps.metamagical.io,resources=chaospods,verbs=create;update,versions=v1,name=mchaospod.kb.io,path=/mutate-chaosapps-metamagical-io-v1-chaospod,mutating=true

var _ webhook.Defaulter = &ChaosPod{}

// Default implements webhookutil.defaulter so a webhook will be registered for the type
Expand Down
13 changes: 13 additions & 0 deletions hack/release/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM k8s.gcr.io/debian-base:v1.0.0

LABEL com.github.actions.name="KubeBuilder PR Emoji"
LABEL com.github.actions.name="Verify that KubeBuilder release notes emoji are present on the PR"
LABEL com.github.actions.icon="git-pull-request"
LABEL com.github.actions.color="blue"

RUN apt-get update -y && apt-get install -y bash jq curl

COPY common.sh /common.sh
COPY verify-emoji.sh /verify-emoji.sh

ENTRYPOINT ["/verify-emoji.sh"]
82 changes: 67 additions & 15 deletions hack/release/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ cr_major_pattern=":warning:|$(printf "\xe2\x9a\xa0")"
cr_minor_pattern=":sparkles:|$(printf "\xe2\x9c\xa8")"
cr_patch_pattern=":bug:|$(printf "\xf0\x9f\x90\x9b")"
cr_docs_pattern=":book:|$(printf "\xf0\x9f\x93\x96")"
cr_no_release_note_pattern=":ghost:|$(printf "\xf0\x9f\x91\xbb")"
cr_other_pattern=":running:|$(printf "\xf0\x9f\x8f\x83")"
cr_all_pattern="${cr_major_pattern}|${cr_minor_pattern}|${cr_patch_pattern}|${cr_docs_pattern}|${cr_other_pattern}"

# cr::symbol-type-raw turns :xyz: and the corresponding emoji
# into one of "major", "minor", "patch", "docs", "other", or
Expand All @@ -24,6 +26,9 @@ cr::symbol-type-raw() {
@(${cr_docs_pattern})?('!'))
echo "docs"
;;
@(${cr_no_release_note_pattern})?('!'))
echo "no_release_note"
;;
@(${cr_other_pattern})?('!'))
echo "other"
;;
Expand Down Expand Up @@ -66,6 +71,52 @@ git::ensure-release-branch() {
fi
}
# git::export-version-from-branch outputs the current version
# for the given branch (as the argument) as exported variables
# (${maj,min,patch}_ver, last_tag).
git::export-version-from-branch() {
local target_branch=${1?must specify a branch}
local current_branch=$(git branch --show-current -q)
local expected_maj_ver
local expected_min_ver
if [[ ${target_branch} =~ release-0.([[:digit:]]+) ]]; then
expected_maj_ver=0
expected_min_ver=${BASH_REMATCH[1]}
elif [[ ${target_branch} =~ release-([[:digit:]]+) ]]; then
expected_maj_ver=${BASH_REMATCH[1]}
else
echo "branch ${target_branch} does not appear to be for a release -- it should be release-X or release-0.Y" >&2
exit 1
fi
local tag_pattern='v([[:digit:]]+).([[:digit:]]+).([[:digit:]]+)'
git checkout -q ${target_branch}
# make sure we've got a tag that matches *some* release
last_tag=$(git describe --tags --abbrev=0) # try to fetch just the "current" tag name
if [[ ! ${last_tag} =~ ${tag_pattern} ]]; then
# it's probably for a previous version
echo "tag ${last_tag} does not appear to be for a release -- it should be vX.Y.Z" >&2
git checkout -q ${current_branch}
exit 1
fi
export min_ver=${BASH_REMATCH[2]}
export patch_ver=${BASH_REMATCH[3]}
export maj_ver=${BASH_REMATCH[1]}
export last_tag=${last_tag}
if ${2:-1} && ([[ ${maj_ver} != ${expected_maj_ver} ]] || [[ ${maj_ver} == 0 && ${min_ver} != ${expected_min_ver} ]]); then
echo "tag ${last_tag} does not appear to be for a the right release (${target_branch})" >&2
git checkout ${current_branch}
exit 1
fi
git checkout -q ${current_branch}
}
# git::export-current-version outputs the current version
# as exported variables (${maj,min,patch}_ver, last_tag) after
# checking that we're on the right release branch.
Expand All @@ -75,25 +126,26 @@ git::export-current-version() {
# deal with the release-0.1 branch, or similar
local release_ver=${BASH_REMATCH[1]}
maj_ver=${release_ver}
local tag_pattern='v${maj_ver}.([[:digit:]]+).([[:digit]]+)'
if [[ ${maj_ver} =~ 0\.([[:digit:]]+) ]]; then
maj_ver=0
min_ver=${BASH_REMATCH[1]}
local tag_pattern="v0.(${min_ver}).([[:digit:]]+)"
local expected_maj_ver=${release_ver}
if [[ ${expected_maj_ver} =~ 0\.([[:digit:]]+) ]]; then
expected_maj_ver=0
local expected_min_ver=${BASH_REMATCH[1]}
fi
# make sure we've got a tag that matches our release branch
last_tag=$(git describe --tags --abbrev=0) # try to fetch just the "current" tag name
if [[ ! ${last_tag} =~ ${tag_pattern} ]]; then
echo "tag ${last_tag} does not appear to be a release for this release (${release_ver})-- it should be v${maj_ver}.Y.Z" >&2
exit 1
git::export-version-from-branch "release-${release_ver}" false
local last_tag_branch=""
if [[ ${maj_ver} == "0" && ${min_ver} -eq $((expected_min_ver-1)) ]]; then
echo "most recent tag is a release behind (${last_tag}), checking previous release branch to be safe" >&2
last_tag_branch="release-0.${min_ver}"
elif [[ ${maj_ver} -eq $((expected_maj_ver-1)) ]]; then
echo "most recent tag is a release behind (${last_tag}), checking previous release branch to be safe" >&2
last_tag_branch="release-${maj_ver}"
fi

export min_ver=${BASH_REMATCH[1]}
export patch_ver=${BASH_REMATCH[2]}
export maj_ver=${maj_ver}
export last_tag=${last_tag}
if [[ -n "${last_tag_branch}" ]]; then
git::export-version-from-branch ${last_tag_branch} true
fi
}

# git::next-version figures out the next version to tag
Expand Down
6 changes: 3 additions & 3 deletions hack/release/release-notes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ while read commit_word commit; do
patch)
bugfixes="${bugfixes}- ${pr_title} (#${pr_number})${NEWLINE}"
;;
docs|other)
docs|no_release_note|other)
# skip non-code-changes
;;
unknown)
Expand All @@ -64,8 +64,8 @@ done <<<$(git rev-list ${last_tag}..HEAD --merges --pretty=format:%B)

# TODO: sort non merge commits with tags

[[ -n "${breaking}" ]] && printf '\e[1;31mbreaking changes this version\e[0m' >&2
[[ -n "${unknown}" ]] && printf '\e[1;35munknown changes in this release -- categorize manually\e[0m' >&2
[[ -n "${breaking}" ]] && printf '\e[1;31mbreaking changes this version\e[0m\n' >&2
[[ -n "${unknown}" ]] && printf '\e[1;35munknown changes in this release -- categorize manually\e[0m\n' >&2

echo "" >&2
echo "" >&2
Expand Down
29 changes: 29 additions & 0 deletions hack/release/verify-emoji.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash

set -eu
set -o pipefail

pr_title=$(jq -r '.pull_request.title' < ${GITHUB_EVENT_PATH})

read pr_prefix rest <<<${pr_title}

source "$(dirname ${BASH_SOURCE})/common.sh"

pr_type=$(cr::symbol-type ${pr_prefix})

summary=""
conclusion="success"
if [[ ${pr_type} == "unknown" ]]; then
summary="You must specify an emoji at the beginning of the PR to indicate what kind of change this is.\nValid emoji: ${cr_all_pattern}.\nYou specified '${pr_prefix}'.\nSee VERSIONING.md for more information."
conclusion="failure"
else
summary="PR is a ${pr_type} change (${pr_prefix})."
fi

# get the PR (the PR sent from the event has the base branch head as the head)
base_link=$(jq -r '.pull_request.url' < ${GITHUB_EVENT_PATH})
head_commit=$(curl -H "Authorization: Bearer ${GITHUB_TOKEN}" -H 'Accept: application/vnd.github.antiope-preview+json' -q ${base_link} | jq -r '.head.sha')
echo "head commit is ${head_commit}"

curl https://api.github.com/repos/${GITHUB_REPOSITORY}/check-runs -XPOST -H "Authorization: Bearer ${GITHUB_TOKEN}" -H 'Accept: application/vnd.github.antiope-preview+json' -H 'Content-Type: application/json' -q --data-raw '{"name": "Verify Emoji", "head_sha": "'${head_commit}'", "conclusion": "'${conclusion}'", "status": "completed", "completed_at": "'$(date -Iseconds)'", "output": {"title": "Verify Emoji", "summary": "'"${summary}"'"}}'

17 changes: 15 additions & 2 deletions pkg/builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type Builder struct {
watchRequest []watchRequest
config *rest.Config
ctrl controller.Controller
name string
}

// SimpleController returns a new Builder.
Expand Down Expand Up @@ -131,6 +132,16 @@ func (blder *Builder) WithEventFilter(p predicate.Predicate) *Builder {
return blder
}

// Named sets the name of the controller to the given name. The name shows up
// in metrics, among other things, and thus should be a prometheus compatible name
// (underscores and alphanumeric characters only).
//
// By default, controllers are named using the lowercase version of their kind.
func (blder *Builder) Named(name string) *Builder {
blder.name = name
return blder
}

// Complete builds the Application ControllerManagedBy.
func (blder *Builder) Complete(r reconcile.Reconciler) error {
_, err := blder.Build(r)
Expand Down Expand Up @@ -227,12 +238,14 @@ func (blder *Builder) doManager() error {
}

func (blder *Builder) getControllerName() (string, error) {
if blder.name != "" {
return blder.name, nil
}
gvk, err := getGvk(blder.apiType, blder.mgr.GetScheme())
if err != nil {
return "", err
}
name := fmt.Sprintf("%s-application", strings.ToLower(gvk.Kind))
return name, nil
return strings.ToLower(gvk.Kind), nil
}

func (blder *Builder) doController(r reconcile.Reconciler) error {
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type Options struct {
// Work typically is reads and writes Kubernetes objects to make the system state match the state specified
// in the object Spec.
type Controller interface {
// Reconciler is called to Reconciler an object by Namespace/Name
// Reconciler is called to reconcile an object by Namespace/Name
reconcile.Reconciler

// Watch takes events provided by a Source and uses the EventHandler to
Expand Down
26 changes: 13 additions & 13 deletions pkg/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ clients, caches, schemes, etc. Controllers should be Started through the Manage
Controller
Controller implements a Kubernetes API by responding to events (object Create, Update, Delete) and ensuring that
the state specified in the Spec of the object matches the state of the system. This is called a Reconciler.
the state specified in the Spec of the object matches the state of the system. This is called a reconcile.
If they do not match, the Controller will create / update / delete objects as needed to make them match.
Controllers are implemented as worker queues that process reconcile.Requests (requests to Reconciler the
Controllers are implemented as worker queues that process reconcile.Requests (requests to reconcile the
state for a specific object).
Unlike http handlers, Controllers DO NOT handle events directly, but enqueue Requests to eventually Reconciler
Unlike http handlers, Controllers DO NOT handle events directly, but enqueue Requests to eventually reconcile
the object. This means the handling of multiple events may be batched together and the full state of the
system must be read for each Reconciler.
system must be read for each reconcile.
* Controllers require a Reconciler to be provided to perform the work pulled from the work queue.
Expand All @@ -65,11 +65,11 @@ Validating webhook is used to validate if an object meets certain requirements.
Reconciler
Reconciler is a function provided to a Controller that may be called at anytime with the Name and Namespace of an object.
When called, Reconciler will ensure that the state of the system matches what is specified in the object at the
time Reconciler is called.
When called, the Reconciler will ensure that the state of the system matches what is specified in the object at the
time the Reconciler is called.
Example: Reconciler invoked for a ReplicaSet object. The ReplicaSet specifies 5 replicas but only
3 Pods exist in the system. Reconciler creates 2 more Pods and sets their OwnerReference to point at the
3 Pods exist in the system. The Reconciler creates 2 more Pods and sets their OwnerReference to point at the
ReplicaSet with controller=true.
* Reconciler contains all of the business logic of a Controller.
Expand All @@ -80,7 +80,7 @@ a mapping (e.g. owner references) that maps the object that triggers the reconci
* Reconciler is provided the Name / Namespace of the object to reconcile.
* Reconciler does not care about the event contents or event type responsible for triggering the Reconciler.
* Reconciler does not care about the event contents or event type responsible for triggering the reconcile.
- e.g. it doesn't matter whether a ReplicaSet was created or updated, Reconciler will always compare the number of
Pods in the system against what is specified in the object at the time it is called.
Expand Down Expand Up @@ -159,7 +159,7 @@ Controller Example
1.2 Pod (created by ReplicaSet) -> handler.EnqueueRequestForOwnerHandler - enqueue a Request with the
Owning ReplicaSet Namespace and Name.
2. Reconciler ReplicaSet in response to an event
2. reconcile ReplicaSet in response to an event
2.1 ReplicaSet object created -> Read ReplicaSet, try to read Pods -> if is missing create Pods.
Expand All @@ -169,7 +169,7 @@ Owning ReplicaSet Namespace and Name.
Watching and EventHandling
Controllers may Watch multiple Kinds of objects (e.g. Pods, ReplicaSets and Deployments), but they Reconciler
Controllers may Watch multiple Kinds of objects (e.g. Pods, ReplicaSets and Deployments), but they reconcile
only a single Type. When one Type of object must be updated in response to changes in another Type of object,
an EnqueueRequestFromMapFunc may be used to map events from one type to another. e.g. Respond to a cluster resize
event (add / delete Node) by re-reconciling all instances of some API.
Expand All @@ -189,10 +189,10 @@ Controller Writing Tips
Reconciler Runtime Complexity:
* It is better to write Controllers to perform an O(1) Reconciler N times (e.g. on N different objects) instead of
performing an O(N) Reconciler 1 time (e.g. on a single object which manages N other objects).
* It is better to write Controllers to perform an O(1) reconcile N times (e.g. on N different objects) instead of
performing an O(N) reconcile 1 time (e.g. on a single object which manages N other objects).
* Example: If you need to update all Services in response to a Node being added - Reconciler Services but Watch
* Example: If you need to update all Services in response to a Node being added - reconcile Services but Watch
Nodes (transformed to Service object name / Namespaces) instead of Reconciling Nodes and updating Services
Event Multiplexing:
Expand Down
Loading

0 comments on commit f60c87e

Please sign in to comment.