Add dependencies to Eventing 0.9.0 (#470)

This commit is contained in:
Ying Chun Guo 2019-11-12 16:41:22 +08:00 committed by Knative Prow Robot
parent 1f27ecaa92
commit d66887a310
147 changed files with 15863 additions and 102 deletions

5
go.mod
View File

@ -4,9 +4,11 @@ require (
contrib.go.opencensus.io/exporter/prometheus v0.1.0 // indirect
contrib.go.opencensus.io/exporter/stackdriver v0.12.5 // indirect; indirect needed by knative serving
github.com/google/go-containerregistry v0.0.0-20191029173801-50b26ee28691 // indirect
github.com/markbates/inflect v1.0.4 // indirect
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/pkg/errors v0.8.1
github.com/robfig/cron v1.2.0 // indirect
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.4.0
@ -16,7 +18,8 @@ require (
k8s.io/apimachinery v0.0.0-20191004115701-31ade1b30762
k8s.io/cli-runtime v0.0.0-20191016113937-7693ce2cae74
k8s.io/client-go v0.0.0-20191016110837-54936ba21026
knative.dev/pkg v0.0.0-20191022181926-0b19b4ad9139
knative.dev/eventing v0.10.0
knative.dev/pkg v0.0.0-20191107185656-884d50f09454
knative.dev/serving v0.10.0
knative.dev/test-infra v0.0.0-20191106182702-a6a34418ff33
sigs.k8s.io/yaml v1.1.0

14
go.sum
View File

@ -104,6 +104,8 @@ github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/
github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/envy v1.6.5 h1:X3is06x7v0nW2xiy2yFbbIjwHz57CD6z6MkvqULTCm8=
github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
@ -182,6 +184,8 @@ github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@ -208,6 +212,8 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/markbates/inflect v1.0.4 h1:5fh1gzTFhfae06u3hzHYO9xe3l3v3nW5Pwt3naLTP5g=
github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs=
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a h1:+J2gw7Bw77w/fbK7wnNJJDKmw1IbWft2Ul5BzrG1Qm8=
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
@ -271,6 +277,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzr
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
@ -516,8 +524,10 @@ k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKf
k8s.io/kubernetes v1.11.10/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/utils v0.0.0-20190221042446-c2654d5206da h1:ElyM7RPonbKnQqOcw7dG2IK5uvQQn3b/WPHqD5mBvP4=
k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
knative.dev/pkg v0.0.0-20191022181926-0b19b4ad9139 h1:wUapryP4MZmajUymSs5i3rL5Xh+SoZAqw/M5aFf0wvA=
knative.dev/pkg v0.0.0-20191022181926-0b19b4ad9139/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
knative.dev/eventing v0.10.0 h1:dDrOjBWWgloOCUjxR8ixUIh8nqCTbmM5BbGo8rvf1bc=
knative.dev/eventing v0.10.0/go.mod h1:UxweNv8yXhsdHJitcb9R6rmfNaUD2DFi9GWwNRyIs58=
knative.dev/pkg v0.0.0-20191107185656-884d50f09454 h1:nkslWFyRWaJp3nPDm+GSQOSvN8NpZg8qIYTR+XssNNg=
knative.dev/pkg v0.0.0-20191107185656-884d50f09454/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
knative.dev/serving v0.10.0 h1:T1csznAQrc/DvCE4KROz4NqOtJ24mnU9eF9RMeeYaCc=
knative.dev/serving v0.10.0/go.mod h1:x2n255JS2XBI39tmjZ8CwTxIf9EKNMCrkVuiOttLRm0=
knative.dev/test-infra v0.0.0-20191106182702-a6a34418ff33 h1:qoTgY6ZKEMs3MUV8hQWhMUjdyecKZsLqYCxFIP8eYwo=

View File

@ -0,0 +1,27 @@
// Copyright © 2019 The Knative 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.
package v1alpha1
import (
client_v1alpha1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1"
)
// knSourcesClient is a combination of Sources client interface and namespace
// Temporarily help to add sources dependencies
// May be changed when adding real sources features
type knSourcesClient struct {
client client_v1alpha1.SourcesV1alpha1Interface
namespace string
}

27
pkg/v1alpha1/client.go Normal file
View File

@ -0,0 +1,27 @@
// Copyright © 2019 The Knative 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.
package v1alpha1
import (
client_v1alpha1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1"
)
// knSourcesClient is a combination of Sources client interface and namespace
// Temporarily help to add sources dependencies
// May be changed when adding real sources features
type knSourcesClient struct {
client client_v1alpha1.SourcesV1alpha1Interface
namespace string
}

5
vendor/github.com/gobuffalo/envy/.env generated vendored Normal file
View File

@ -0,0 +1,5 @@
# This is a comment
# We can use equal or colon notation
DIR: root
FLAVOUR: none
INSIDE_FOLDER=false

29
vendor/github.com/gobuffalo/envy/.gitignore generated vendored Normal file
View File

@ -0,0 +1,29 @@
*.log
.DS_Store
doc
tmp
pkg
*.gem
*.pid
coverage
coverage.data
build/*
*.pbxuser
*.mode1v3
.svn
profile
.console_history
.sass-cache/*
.rake_tasks~
*.log.lck
solr/
.jhw-cache/
jhw.*
*.sublime*
node_modules/
dist/
generated/
.vendor/
bin/*
gin-bin
.idea/

3
vendor/github.com/gobuffalo/envy/.gometalinter.json generated vendored Normal file
View File

@ -0,0 +1,3 @@
{
"Enable": ["vet", "golint", "goimports", "deadcode", "gotype", "ineffassign", "misspell", "nakedret", "unconvert", "megacheck", "varcheck"]
}

26
vendor/github.com/gobuffalo/envy/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,26 @@
language: go
sudo: false
matrix:
include:
- go: "1.9.x"
- go: "1.10.x"
- go: "1.11.x"
env:
- GO111MODULE=off
- go: "1.11.x"
env:
- GO111MODULE=on
- go: "tip"
env:
- GO111MODULE=off
- go: "tip"
env:
- GO111MODULE=on
allow_failures:
- go: "tip"
install: make deps
script: make ci-test

8
vendor/github.com/gobuffalo/envy/LICENSE.txt generated vendored Normal file
View File

@ -0,0 +1,8 @@
The MIT License (MIT)
Copyright (c) 2018 Mark Bates
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

46
vendor/github.com/gobuffalo/envy/Makefile generated vendored Normal file
View File

@ -0,0 +1,46 @@
TAGS ?= "sqlite"
GO_BIN ?= go
install:
packr
$(GO_BIN) install -v .
deps:
$(GO_BIN) get github.com/gobuffalo/release
$(GO_BIN) get github.com/gobuffalo/packr/packr
$(GO_BIN) get -tags ${TAGS} -t ./...
ifeq ($(GO111MODULE),on)
$(GO_BIN) mod tidy
endif
build:
packr
$(GO_BIN) build -v .
test:
packr
$(GO_BIN) test -tags ${TAGS} ./...
ci-test:
$(GO_BIN) test -tags ${TAGS} -race ./...
lint:
gometalinter --vendor ./... --deadline=1m --skip=internal
update:
$(GO_BIN) get -u -tags ${TAGS}
ifeq ($(GO111MODULE),on)
$(GO_BIN) mod tidy
endif
packr
make test
make install
ifeq ($(GO111MODULE),on)
$(GO_BIN) mod tidy
endif
release-test:
$(GO_BIN) test -tags ${TAGS} -race ./...
release:
release -y -f version.go

93
vendor/github.com/gobuffalo/envy/README.md generated vendored Normal file
View File

@ -0,0 +1,93 @@
# envy
[![Build Status](https://travis-ci.org/gobuffalo/envy.svg?branch=master)](https://travis-ci.org/gobuffalo/envy)
Envy makes working with ENV variables in Go trivial.
* Get ENV variables with default values.
* Set ENV variables safely without affecting the underlying system.
* Temporarily change ENV vars; useful for testing.
* Map all of the key/values in the ENV.
* Loads .env files (by using [godotenv](https://github.com/joho/godotenv/))
* More!
## Installation
```text
$ go get -u github.com/gobuffalo/envy
```
## Usage
```go
func Test_Get(t *testing.T) {
r := require.New(t)
r.NotZero(os.Getenv("GOPATH"))
r.Equal(os.Getenv("GOPATH"), envy.Get("GOPATH", "foo"))
r.Equal("bar", envy.Get("IDONTEXIST", "bar"))
}
func Test_MustGet(t *testing.T) {
r := require.New(t)
r.NotZero(os.Getenv("GOPATH"))
v, err := envy.MustGet("GOPATH")
r.NoError(err)
r.Equal(os.Getenv("GOPATH"), v)
_, err = envy.MustGet("IDONTEXIST")
r.Error(err)
}
func Test_Set(t *testing.T) {
r := require.New(t)
_, err := envy.MustGet("FOO")
r.Error(err)
envy.Set("FOO", "foo")
r.Equal("foo", envy.Get("FOO", "bar"))
}
func Test_Temp(t *testing.T) {
r := require.New(t)
_, err := envy.MustGet("BAR")
r.Error(err)
envy.Temp(func() {
envy.Set("BAR", "foo")
r.Equal("foo", envy.Get("BAR", "bar"))
_, err = envy.MustGet("BAR")
r.NoError(err)
})
_, err = envy.MustGet("BAR")
r.Error(err)
}
```
## .env files support
Envy now supports loading `.env` files by using the [godotenv library](https://github.com/joho/godotenv/).
That means one can use and define multiple `.env` files which will be loaded on-demand. By default, no env files will be loaded. To load one or more, you need to call the `envy.Load` function in one of the following ways:
```go
envy.Load() // 1
envy.Load("MY_ENV_FILE") // 2
envy.Load(".env", ".env.prod") // 3
envy.Load(".env", "NON_EXISTING_FILE") // 4
// 5
envy.Load(".env")
envy.Load("NON_EXISTING_FILE")
// 6
envy.Load(".env", "NON_EXISTING_FILE", ".env.prod")
```
1. Will load the default `.env` file
2. Will load the file `MY_ENV_FILE`, **but not** `.env`
3. Will load the file `.env`, and after that will load the `.env.prod` file. If any variable is redefined in `. env.prod` it will be overwritten (will contain the `env.prod` value)
4. Will load the `.env` file and return an error as the second file does not exist. The values in `.env` will be loaded and available.
5. Same as 4
6. Will load the `.env` file and return an error as the second file does not exist. The values in `.env` will be loaded and available, **but the ones in** `.env.prod` **won't**.

238
vendor/github.com/gobuffalo/envy/envy.go generated vendored Normal file
View File

@ -0,0 +1,238 @@
/*
package envy makes working with ENV variables in Go trivial.
* Get ENV variables with default values.
* Set ENV variables safely without affecting the underlying system.
* Temporarily change ENV vars; useful for testing.
* Map all of the key/values in the ENV.
* Loads .env files (by using [godotenv](https://github.com/joho/godotenv/))
* More!
*/
package envy
import (
"flag"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"github.com/joho/godotenv"
)
var gil = &sync.RWMutex{}
var env = map[string]string{}
func init() {
Load()
loadEnv()
}
// Load the ENV variables to the env map
func loadEnv() {
gil.Lock()
defer gil.Unlock()
// Detect the Go version on the user system, not the one that was used to compile the binary
v := ""
out, err := exec.Command("go", "version").Output()
if err == nil {
// This will break when Go 2 lands
v = strings.Split(string(out), " ")[2][4:]
} else {
v = runtime.Version()[4:]
}
goRuntimeVersion, _ := strconv.ParseFloat(runtime.Version()[4:], 64)
goVersion, err := strconv.ParseFloat(v, 64)
if err != nil {
goVersion = goRuntimeVersion
}
if os.Getenv("GO_ENV") == "" {
// if the flag "test.v" is *defined*, we're running as a unit test. Note that we don't care
// about v.Value (verbose test mode); we just want to know if the test environment has defined
// it. It's also possible that the flags are not yet fully parsed (i.e. flag.Parsed() == false),
// so we could not depend on v.Value anyway.
//
if v := flag.Lookup("test.v"); v != nil {
env["GO_ENV"] = "test"
}
}
// set the GOPATH if using >= 1.8 and the GOPATH isn't set
if goVersion >= 8 && os.Getenv("GOPATH") == "" {
out, err := exec.Command("go", "env", "GOPATH").Output()
if err == nil {
gp := strings.TrimSpace(string(out))
os.Setenv("GOPATH", gp)
}
}
for _, e := range os.Environ() {
pair := strings.Split(e, "=")
env[pair[0]] = os.Getenv(pair[0])
}
}
// Reload the ENV variables. Useful if
// an external ENV manager has been used
func Reload() {
env = map[string]string{}
loadEnv()
}
// Load .env files. Files will be loaded in the same order that are received.
// Redefined vars will override previously existing values.
// IE: envy.Load(".env", "test_env/.env") will result in DIR=test_env
// If no arg passed, it will try to load a .env file.
func Load(files ...string) error {
// If no files received, load the default one
if len(files) == 0 {
err := godotenv.Overload()
if err == nil {
Reload()
}
return err
}
// We received a list of files
for _, file := range files {
// Check if it exists or we can access
if _, err := os.Stat(file); err != nil {
// It does not exist or we can not access.
// Return and stop loading
return err
}
// It exists and we have permission. Load it
if err := godotenv.Overload(file); err != nil {
return err
}
// Reload the env so all new changes are noticed
Reload()
}
return nil
}
// Get a value from the ENV. If it doesn't exist the
// default value will be returned.
func Get(key string, value string) string {
gil.RLock()
defer gil.RUnlock()
if v, ok := env[key]; ok {
return v
}
return value
}
// Get a value from the ENV. If it doesn't exist
// an error will be returned
func MustGet(key string) (string, error) {
gil.RLock()
defer gil.RUnlock()
if v, ok := env[key]; ok {
return v, nil
}
return "", fmt.Errorf("could not find ENV var with %s", key)
}
// Set a value into the ENV. This is NOT permanent. It will
// only affect values accessed through envy.
func Set(key string, value string) {
gil.Lock()
defer gil.Unlock()
env[key] = value
}
// MustSet the value into the underlying ENV, as well as envy.
// This may return an error if there is a problem setting the
// underlying ENV value.
func MustSet(key string, value string) error {
gil.Lock()
defer gil.Unlock()
err := os.Setenv(key, value)
if err != nil {
return err
}
env[key] = value
return nil
}
// Map all of the keys/values set in envy.
func Map() map[string]string {
gil.RLock()
defer gil.RUnlock()
cp := map[string]string{}
for k, v := range env {
cp[k] = v
}
return env
}
// Temp makes a copy of the values and allows operation on
// those values temporarily during the run of the function.
// At the end of the function run the copy is discarded and
// the original values are replaced. This is useful for testing.
// Warning: This function is NOT safe to use from a goroutine or
// from code which may access any Get or Set function from a goroutine
func Temp(f func()) {
oenv := env
env = map[string]string{}
for k, v := range oenv {
env[k] = v
}
defer func() { env = oenv }()
f()
}
func GoPath() string {
return Get("GOPATH", "")
}
// GoPaths returns all possible GOPATHS that are set.
func GoPaths() []string {
gp := Get("GOPATH", "")
if runtime.GOOS == "windows" {
return strings.Split(gp, ";") // Windows uses a different separator
}
return strings.Split(gp, ":")
}
func importPath(path string) string {
for _, gopath := range GoPaths() {
srcpath := filepath.Join(gopath, "src")
rel, err := filepath.Rel(srcpath, path)
if err == nil {
return filepath.ToSlash(rel)
}
}
// fallback to trim
rel := strings.TrimPrefix(path, filepath.Join(GoPath(), "src"))
rel = strings.TrimPrefix(rel, string(filepath.Separator))
return filepath.ToSlash(rel)
}
func CurrentPackage() string {
pwd, _ := os.Getwd()
return importPath(pwd)
}
func Environ() []string {
gil.RLock()
defer gil.RUnlock()
var e []string
for k, v := range env {
e = append(e, fmt.Sprintf("%s=%s", k, v))
}
return e
}

8
vendor/github.com/gobuffalo/envy/go.mod generated vendored Normal file
View File

@ -0,0 +1,8 @@
module github.com/gobuffalo/envy
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/joho/godotenv v1.3.0
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.2.2
)

8
vendor/github.com/gobuffalo/envy/go.sum generated vendored Normal file
View File

@ -0,0 +1,8 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=

10
vendor/github.com/gobuffalo/envy/shoulders.md generated vendored Normal file
View File

@ -0,0 +1,10 @@
# github.com/gobuffalo/envy Stands on the Shoulders of Giants
github.com/gobuffalo/envy does not try to reinvent the wheel! Instead, it uses the already great wheels developed by the Go community and puts them all together in the best way possible. Without these giants this project would not be possible. Please make sure to check them out and thank them for all of their hard work.
Thank you to the following **GIANTS**:
* [github.com/gobuffalo/envy](https://godoc.org/github.com/gobuffalo/envy)
* [github.com/joho/godotenv](https://godoc.org/github.com/joho/godotenv)

3
vendor/github.com/gobuffalo/envy/version.go generated vendored Normal file
View File

@ -0,0 +1,3 @@
package envy
const Version = "v1.6.5"

1
vendor/github.com/joho/godotenv/.gitignore generated vendored Normal file
View File

@ -0,0 +1 @@
.DS_Store

8
vendor/github.com/joho/godotenv/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,8 @@
language: go
go:
- 1.x
os:
- linux
- osx

23
vendor/github.com/joho/godotenv/LICENCE generated vendored Normal file
View File

@ -0,0 +1,23 @@
Copyright (c) 2013 John Barton
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

163
vendor/github.com/joho/godotenv/README.md generated vendored Normal file
View File

@ -0,0 +1,163 @@
# GoDotEnv [![Build Status](https://travis-ci.org/joho/godotenv.svg?branch=master)](https://travis-ci.org/joho/godotenv) [![Build status](https://ci.appveyor.com/api/projects/status/9v40vnfvvgde64u4?svg=true)](https://ci.appveyor.com/project/joho/godotenv) [![Go Report Card](https://goreportcard.com/badge/github.com/joho/godotenv)](https://goreportcard.com/report/github.com/joho/godotenv)
A Go (golang) port of the Ruby dotenv project (which loads env vars from a .env file)
From the original Library:
> Storing configuration in the environment is one of the tenets of a twelve-factor app. Anything that is likely to change between deployment environmentssuch as resource handles for databases or credentials for external servicesshould be extracted from the code into environment variables.
>
> But it is not always practical to set environment variables on development machines or continuous integration servers where multiple projects are run. Dotenv load variables from a .env file into ENV when the environment is bootstrapped.
It can be used as a library (for loading in env for your own daemons etc) or as a bin command.
There is test coverage and CI for both linuxish and windows environments, but I make no guarantees about the bin version working on windows.
## Installation
As a library
```shell
go get github.com/joho/godotenv
```
or if you want to use it as a bin command
```shell
go get github.com/joho/godotenv/cmd/godotenv
```
## Usage
Add your application configuration to your `.env` file in the root of your project:
```shell
S3_BUCKET=YOURS3BUCKET
SECRET_KEY=YOURSECRETKEYGOESHERE
```
Then in your Go app you can do something like
```go
package main
import (
"github.com/joho/godotenv"
"log"
"os"
)
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
s3Bucket := os.Getenv("S3_BUCKET")
secretKey := os.Getenv("SECRET_KEY")
// now do something with s3 or whatever
}
```
If you're even lazier than that, you can just take advantage of the autoload package which will read in `.env` on import
```go
import _ "github.com/joho/godotenv/autoload"
```
While `.env` in the project root is the default, you don't have to be constrained, both examples below are 100% legit
```go
_ = godotenv.Load("somerandomfile")
_ = godotenv.Load("filenumberone.env", "filenumbertwo.env")
```
If you want to be really fancy with your env file you can do comments and exports (below is a valid env file)
```shell
# I am a comment and that is OK
SOME_VAR=someval
FOO=BAR # comments at line end are OK too
export BAR=BAZ
```
Or finally you can do YAML(ish) style
```yaml
FOO: bar
BAR: baz
```
as a final aside, if you don't want godotenv munging your env you can just get a map back instead
```go
var myEnv map[string]string
myEnv, err := godotenv.Read()
s3Bucket := myEnv["S3_BUCKET"]
```
... or from an `io.Reader` instead of a local file
```go
reader := getRemoteFile()
myEnv, err := godotenv.Parse(reader)
```
... or from a `string` if you so desire
```go
content := getRemoteFileContent()
myEnv, err := godotenv.Unmarshal(content)
```
### Command Mode
Assuming you've installed the command as above and you've got `$GOPATH/bin` in your `$PATH`
```
godotenv -f /some/path/to/.env some_command with some args
```
If you don't specify `-f` it will fall back on the default of loading `.env` in `PWD`
### Writing Env Files
Godotenv can also write a map representing the environment to a correctly-formatted and escaped file
```go
env, err := godotenv.Unmarshal("KEY=value")
err := godotenv.Write(env, "./.env")
```
... or to a string
```go
env, err := godotenv.Unmarshal("KEY=value")
content, err := godotenv.Marshal(env)
```
## Contributing
Contributions are most welcome! The parser itself is pretty stupidly naive and I wouldn't be surprised if it breaks with edge cases.
*code changes without tests will not be accepted*
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Added some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
## Releases
Releases should follow [Semver](http://semver.org/) though the first couple of releases are `v1` and `v1.1`.
Use [annotated tags for all releases](https://github.com/joho/godotenv/issues/30). Example `git tag -a v1.2.1`
## CI
Linux: [![Build Status](https://travis-ci.org/joho/godotenv.svg?branch=master)](https://travis-ci.org/joho/godotenv) Windows: [![Build status](https://ci.appveyor.com/api/projects/status/9v40vnfvvgde64u4)](https://ci.appveyor.com/project/joho/godotenv)
## Who?
The original library [dotenv](https://github.com/bkeepers/dotenv) was written by [Brandon Keepers](http://opensoul.org/), and this port was done by [John Barton](https://johnbarton.co/) based off the tests/fixtures in the original library.

346
vendor/github.com/joho/godotenv/godotenv.go generated vendored Normal file
View File

@ -0,0 +1,346 @@
// Package godotenv is a go port of the ruby dotenv library (https://github.com/bkeepers/dotenv)
//
// Examples/readme can be found on the github page at https://github.com/joho/godotenv
//
// The TL;DR is that you make a .env file that looks something like
//
// SOME_ENV_VAR=somevalue
//
// and then in your go code you can call
//
// godotenv.Load()
//
// and all the env vars declared in .env will be available through os.Getenv("SOME_ENV_VAR")
package godotenv
import (
"bufio"
"errors"
"fmt"
"io"
"os"
"os/exec"
"regexp"
"sort"
"strings"
)
const doubleQuoteSpecialChars = "\\\n\r\"!$`"
// Load will read your env file(s) and load them into ENV for this process.
//
// Call this function as close as possible to the start of your program (ideally in main)
//
// If you call Load without any args it will default to loading .env in the current path
//
// You can otherwise tell it which files to load (there can be more than one) like
//
// godotenv.Load("fileone", "filetwo")
//
// It's important to note that it WILL NOT OVERRIDE an env variable that already exists - consider the .env file to set dev vars or sensible defaults
func Load(filenames ...string) (err error) {
filenames = filenamesOrDefault(filenames)
for _, filename := range filenames {
err = loadFile(filename, false)
if err != nil {
return // return early on a spazout
}
}
return
}
// Overload will read your env file(s) and load them into ENV for this process.
//
// Call this function as close as possible to the start of your program (ideally in main)
//
// If you call Overload without any args it will default to loading .env in the current path
//
// You can otherwise tell it which files to load (there can be more than one) like
//
// godotenv.Overload("fileone", "filetwo")
//
// It's important to note this WILL OVERRIDE an env variable that already exists - consider the .env file to forcefilly set all vars.
func Overload(filenames ...string) (err error) {
filenames = filenamesOrDefault(filenames)
for _, filename := range filenames {
err = loadFile(filename, true)
if err != nil {
return // return early on a spazout
}
}
return
}
// Read all env (with same file loading semantics as Load) but return values as
// a map rather than automatically writing values into env
func Read(filenames ...string) (envMap map[string]string, err error) {
filenames = filenamesOrDefault(filenames)
envMap = make(map[string]string)
for _, filename := range filenames {
individualEnvMap, individualErr := readFile(filename)
if individualErr != nil {
err = individualErr
return // return early on a spazout
}
for key, value := range individualEnvMap {
envMap[key] = value
}
}
return
}
// Parse reads an env file from io.Reader, returning a map of keys and values.
func Parse(r io.Reader) (envMap map[string]string, err error) {
envMap = make(map[string]string)
var lines []string
scanner := bufio.NewScanner(r)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err = scanner.Err(); err != nil {
return
}
for _, fullLine := range lines {
if !isIgnoredLine(fullLine) {
var key, value string
key, value, err = parseLine(fullLine, envMap)
if err != nil {
return
}
envMap[key] = value
}
}
return
}
//Unmarshal reads an env file from a string, returning a map of keys and values.
func Unmarshal(str string) (envMap map[string]string, err error) {
return Parse(strings.NewReader(str))
}
// Exec loads env vars from the specified filenames (empty map falls back to default)
// then executes the cmd specified.
//
// Simply hooks up os.Stdin/err/out to the command and calls Run()
//
// If you want more fine grained control over your command it's recommended
// that you use `Load()` or `Read()` and the `os/exec` package yourself.
func Exec(filenames []string, cmd string, cmdArgs []string) error {
Load(filenames...)
command := exec.Command(cmd, cmdArgs...)
command.Stdin = os.Stdin
command.Stdout = os.Stdout
command.Stderr = os.Stderr
return command.Run()
}
// Write serializes the given environment and writes it to a file
func Write(envMap map[string]string, filename string) error {
content, error := Marshal(envMap)
if error != nil {
return error
}
file, error := os.Create(filename)
if error != nil {
return error
}
_, err := file.WriteString(content)
return err
}
// Marshal outputs the given environment as a dotenv-formatted environment file.
// Each line is in the format: KEY="VALUE" where VALUE is backslash-escaped.
func Marshal(envMap map[string]string) (string, error) {
lines := make([]string, 0, len(envMap))
for k, v := range envMap {
lines = append(lines, fmt.Sprintf(`%s="%s"`, k, doubleQuoteEscape(v)))
}
sort.Strings(lines)
return strings.Join(lines, "\n"), nil
}
func filenamesOrDefault(filenames []string) []string {
if len(filenames) == 0 {
return []string{".env"}
}
return filenames
}
func loadFile(filename string, overload bool) error {
envMap, err := readFile(filename)
if err != nil {
return err
}
currentEnv := map[string]bool{}
rawEnv := os.Environ()
for _, rawEnvLine := range rawEnv {
key := strings.Split(rawEnvLine, "=")[0]
currentEnv[key] = true
}
for key, value := range envMap {
if !currentEnv[key] || overload {
os.Setenv(key, value)
}
}
return nil
}
func readFile(filename string) (envMap map[string]string, err error) {
file, err := os.Open(filename)
if err != nil {
return
}
defer file.Close()
return Parse(file)
}
func parseLine(line string, envMap map[string]string) (key string, value string, err error) {
if len(line) == 0 {
err = errors.New("zero length string")
return
}
// ditch the comments (but keep quoted hashes)
if strings.Contains(line, "#") {
segmentsBetweenHashes := strings.Split(line, "#")
quotesAreOpen := false
var segmentsToKeep []string
for _, segment := range segmentsBetweenHashes {
if strings.Count(segment, "\"") == 1 || strings.Count(segment, "'") == 1 {
if quotesAreOpen {
quotesAreOpen = false
segmentsToKeep = append(segmentsToKeep, segment)
} else {
quotesAreOpen = true
}
}
if len(segmentsToKeep) == 0 || quotesAreOpen {
segmentsToKeep = append(segmentsToKeep, segment)
}
}
line = strings.Join(segmentsToKeep, "#")
}
firstEquals := strings.Index(line, "=")
firstColon := strings.Index(line, ":")
splitString := strings.SplitN(line, "=", 2)
if firstColon != -1 && (firstColon < firstEquals || firstEquals == -1) {
//this is a yaml-style line
splitString = strings.SplitN(line, ":", 2)
}
if len(splitString) != 2 {
err = errors.New("Can't separate key from value")
return
}
// Parse the key
key = splitString[0]
if strings.HasPrefix(key, "export") {
key = strings.TrimPrefix(key, "export")
}
key = strings.Trim(key, " ")
// Parse the value
value = parseValue(splitString[1], envMap)
return
}
func parseValue(value string, envMap map[string]string) string {
// trim
value = strings.Trim(value, " ")
// check if we've got quoted values or possible escapes
if len(value) > 1 {
rs := regexp.MustCompile(`\A'(.*)'\z`)
singleQuotes := rs.FindStringSubmatch(value)
rd := regexp.MustCompile(`\A"(.*)"\z`)
doubleQuotes := rd.FindStringSubmatch(value)
if singleQuotes != nil || doubleQuotes != nil {
// pull the quotes off the edges
value = value[1 : len(value)-1]
}
if doubleQuotes != nil {
// expand newlines
escapeRegex := regexp.MustCompile(`\\.`)
value = escapeRegex.ReplaceAllStringFunc(value, func(match string) string {
c := strings.TrimPrefix(match, `\`)
switch c {
case "n":
return "\n"
case "r":
return "\r"
default:
return match
}
})
// unescape characters
e := regexp.MustCompile(`\\([^$])`)
value = e.ReplaceAllString(value, "$1")
}
if singleQuotes == nil {
value = expandVariables(value, envMap)
}
}
return value
}
func expandVariables(v string, m map[string]string) string {
r := regexp.MustCompile(`(\\)?(\$)(\()?\{?([A-Z0-9_]+)?\}?`)
return r.ReplaceAllStringFunc(v, func(s string) string {
submatch := r.FindStringSubmatch(s)
if submatch == nil {
return s
}
if submatch[1] == "\\" || submatch[2] == "(" {
return submatch[0][1:]
} else if submatch[4] != "" {
return m[submatch[4]]
}
return s
})
}
func isIgnoredLine(line string) bool {
trimmedLine := strings.Trim(line, " \n\t")
return len(trimmedLine) == 0 || strings.HasPrefix(trimmedLine, "#")
}
func doubleQuoteEscape(line string) string {
for _, c := range doubleQuoteSpecialChars {
toReplace := "\\" + string(c)
if c == '\n' {
toReplace = `\n`
}
if c == '\r' {
toReplace = `\r`
}
line = strings.Replace(line, string(c), toReplace, -1)
}
return line
}

29
vendor/github.com/markbates/inflect/.gitignore generated vendored Normal file
View File

@ -0,0 +1,29 @@
*.log
.DS_Store
doc
tmp
pkg
*.gem
*.pid
coverage
coverage.data
build/*
*.pbxuser
*.mode1v3
.svn
profile
.console_history
.sass-cache/*
.rake_tasks~
*.log.lck
solr/
.jhw-cache/
jhw.*
*.sublime*
node_modules/
dist/
generated/
.vendor/
bin/*
gin-bin
.idea/

View File

@ -0,0 +1,3 @@
{
"Enable": ["vet", "golint", "goimports", "deadcode", "gotype", "ineffassign", "misspell", "nakedret", "unconvert", "megacheck", "varcheck"]
}

1
vendor/github.com/markbates/inflect/.hgignore generated vendored Normal file
View File

@ -0,0 +1 @@
swp$

26
vendor/github.com/markbates/inflect/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,26 @@
language: go
sudo: false
matrix:
include:
- go: "1.9.x"
- go: "1.10.x"
- go: "1.11.x"
env:
- GO111MODULE=off
- go: "1.11.x"
env:
- GO111MODULE=on
- go: "tip"
env:
- GO111MODULE=off
- go: "tip"
env:
- GO111MODULE=on
allow_failures:
- go: "tip"
install: make deps
script: make ci-test

7
vendor/github.com/markbates/inflect/LICENCE generated vendored Normal file
View File

@ -0,0 +1,7 @@
Copyright (c) 2011 Chris Farmiloe
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

55
vendor/github.com/markbates/inflect/Makefile generated vendored Normal file
View File

@ -0,0 +1,55 @@
TAGS ?= "sqlite"
GO_BIN ?= go
install:
packr
$(GO_BIN) install -tags ${TAGS} -v .
make tidy
tidy:
ifeq ($(GO111MODULE),on)
$(GO_BIN) mod tidy
else
echo skipping go mod tidy
endif
deps:
$(GO_BIN) get github.com/gobuffalo/release
$(GO_BIN) get github.com/gobuffalo/packr/packr
$(GO_BIN) get -tags ${TAGS} -t ./...
make tidy
build:
packr
$(GO_BIN) build -v .
make tidy
test:
packr
$(GO_BIN) test -tags ${TAGS} ./...
make tidy
ci-test:
$(GO_BIN) test -tags ${TAGS} -race ./...
make tidy
lint:
gometalinter --vendor ./... --deadline=1m --skip=internal
make tidy
update:
$(GO_BIN) get -u -tags ${TAGS}
make tidy
packr
make test
make install
make tidy
release-test:
$(GO_BIN) test -tags ${TAGS} -race ./...
make tidy
release:
make tidy
release -y -f version.go
make tidy

214
vendor/github.com/markbates/inflect/README.md generated vendored Normal file
View File

@ -0,0 +1,214 @@
[![Build Status](https://travis-ci.org/markbates/inflect.svg?branch=master)](https://travis-ci.org/markbates/inflect)
#### INSTALLATION
go get github.com/markbates/inflect
#### PACKAGE
package inflect
#### FUNCTIONS
```go
func AddAcronym(word string)
func AddHuman(suffix, replacement string)
func AddIrregular(singular, plural string)
func AddPlural(suffix, replacement string)
func AddSingular(suffix, replacement string)
func AddUncountable(word string)
func Asciify(word string) string
func Camelize(word string) string
func CamelizeDownFirst(word string) string
func Capitalize(word string) string
func Dasherize(word string) string
func ForeignKey(word string) string
func ForeignKeyCondensed(word string) string
func Humanize(word string) string
func Ordinalize(word string) string
func Parameterize(word string) string
func ParameterizeJoin(word, sep string) string
func Pluralize(word string) string
func Singularize(word string) string
func Tableize(word string) string
func Titleize(word string) string
func Typeify(word string) string
func Uncountables() map[string]bool
func Underscore(word string) string
```
#### TYPES
```go
type Rule struct {
// contains filtered or unexported fields
}
```
used by rulesets
```go
type Ruleset struct {
// contains filtered or unexported fields
}
```
a Ruleset is the config of pluralization rules
you can extend the rules with the Add* methods
```
func NewDefaultRuleset() *Ruleset
```
create a new ruleset and load it with the default
set of common English pluralization rules
```
func NewRuleset() *Ruleset
```
create a blank ruleset. Unless you are going to
build your own rules from scratch you probably
won't need this and can just use the defaultRuleset
via the global inflect.* methods
```
func (rs *Ruleset) AddAcronym(word string)
```
if you use acronym you may need to add them to the ruleset
to prevent Underscored words of things like "HTML" coming out
as "h_t_m_l"
```
func (rs *Ruleset) AddHuman(suffix, replacement string)
```
Human rules are applied by humanize to show more friendly
versions of words
```
func (rs *Ruleset) AddIrregular(singular, plural string)
```
Add any inconsistent pluralizing/singularizing rules
to the set here.
```
func (rs *Ruleset) AddPlural(suffix, replacement string)
```
add a pluralization rule
```
func (rs *Ruleset) AddPluralExact(suffix, replacement string, exact bool)
```
add a pluralization rule with full string match
```
func (rs *Ruleset) AddSingular(suffix, replacement string)
```
add a singular rule
```
func (rs *Ruleset) AddSingularExact(suffix, replacement string, exact bool)
```
same as AddSingular but you can set `exact` to force
a full string match
```
func (rs *Ruleset) AddUncountable(word string)
```
add a word to this ruleset that has the same singular and plural form
for example: "rice"
```
func (rs *Ruleset) Asciify(word string) string
```
transforms Latin characters like é -> e
```
func (rs *Ruleset) Camelize(word string) string
```
"dino_party" -> "DinoParty"
```
func (rs *Ruleset) CamelizeDownFirst(word string) string
```
same as Camelcase but with first letter downcased
```
func (rs *Ruleset) Capitalize(word string) string
```
uppercase first character
```
func (rs *Ruleset) Dasherize(word string) string
```
"SomeText" -> "some-text"
```
func (rs *Ruleset) ForeignKey(word string) string
```
an underscored foreign key name "Person" -> "person_id"
```
func (rs *Ruleset) ForeignKeyCondensed(word string) string
```
a foreign key (with an underscore) "Person" -> "personid"
```
func (rs *Ruleset) Humanize(word string) string
```
First letter of sentence capitalized
Uses custom friendly replacements via AddHuman()
```
func (rs *Ruleset) Ordinalize(str string) string
```
"1031" -> "1031st"
```
func (rs *Ruleset) Parameterize(word string) string
```
param safe dasherized names like "my-param"
```
func (rs *Ruleset) ParameterizeJoin(word, sep string) string
```
param safe dasherized names with custom separator
```
func (rs *Ruleset) Pluralize(word string) string
```
returns the plural form of a singular word
```
func (rs *Ruleset) Singularize(word string) string
```
returns the singular form of a plural word
```
func (rs *Ruleset) Tableize(word string) string
```
Rails style pluralized table names: "SuperPerson" -> "super_people"
```
func (rs *Ruleset) Titleize(word string) string
```
Capitalize every word in sentence "hello there" -> "Hello There"
```
func (rs *Ruleset) Typeify(word string) string
```
"something_like_this" -> "SomethingLikeThis"
```
func (rs *Ruleset) Uncountables() map[string]bool
```
```
func (rs *Ruleset) Underscore(word string) string
```
lowercase underscore version "BigBen" -> "big_ben"

6
vendor/github.com/markbates/inflect/go.mod generated vendored Normal file
View File

@ -0,0 +1,6 @@
module github.com/markbates/inflect
require (
github.com/gobuffalo/envy v1.6.5
github.com/stretchr/testify v1.2.2
)

10
vendor/github.com/markbates/inflect/go.sum generated vendored Normal file
View File

@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gobuffalo/envy v1.6.5 h1:X3is06x7v0nW2xiy2yFbbIjwHz57CD6z6MkvqULTCm8=
github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=

19
vendor/github.com/markbates/inflect/helpers.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
package inflect
//Helpers is a map of the helper names with its corresponding inflect function
var Helpers = map[string]interface{}{
"asciffy": Asciify,
"camelize": Camelize,
"camelize_down_first": CamelizeDownFirst,
"capitalize": Capitalize,
"dasherize": Dasherize,
"humanize": Humanize,
"ordinalize": Ordinalize,
"parameterize": Parameterize,
"pluralize": Pluralize,
"pluralize_with_size": PluralizeWithSize,
"singularize": Singularize,
"tableize": Tableize,
"typeify": Typeify,
"underscore": Underscore,
}

892
vendor/github.com/markbates/inflect/inflect.go generated vendored Normal file
View File

@ -0,0 +1,892 @@
package inflect
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"unicode"
"unicode/utf8"
)
// baseAcronyms comes from https://en.wikipedia.org/wiki/List_of_information_technology_acronymss
const baseAcronyms = `JSON,JWT,ID,UUID,SQL,ACK,ACL,ADSL,AES,ANSI,API,ARP,ATM,BGP,BSS,CAT,CCITT,CHAP,CIDR,CIR,CLI,CPE,CPU,CRC,CRT,CSMA,CMOS,DCE,DEC,DES,DHCP,DNS,DRAM,DSL,DSLAM,DTE,DMI,EHA,EIA,EIGRP,EOF,ESS,FCC,FCS,FDDI,FTP,GBIC,gbps,GEPOF,HDLC,HTTP,HTTPS,IANA,ICMP,IDF,IDS,IEEE,IETF,IMAP,IP,IPS,ISDN,ISP,kbps,LACP,LAN,LAPB,LAPF,LLC,MAC,MAN,Mbps,MC,MDF,MIB,MoCA,MPLS,MTU,NAC,NAT,NBMA,NIC,NRZ,NRZI,NVRAM,OSI,OSPF,OUI,PAP,PAT,PC,PIM,PIM,PCM,PDU,POP3,POP,POTS,PPP,PPTP,PTT,PVST,RADIUS,RAM,RARP,RFC,RIP,RLL,ROM,RSTP,RTP,RCP,SDLC,SFD,SFP,SLARP,SLIP,SMTP,SNA,SNAP,SNMP,SOF,SRAM,SSH,SSID,STP,SYN,TDM,TFTP,TIA,TOFU,UDP,URL,URI,USB,UTP,VC,VLAN,VLSM,VPN,W3C,WAN,WEP,WiFi,WPA,WWW`
// Rule used by rulesets
type Rule struct {
suffix string
replacement string
exact bool
}
// Ruleset a Ruleset is the config of pluralization rules
// you can extend the rules with the Add* methods
type Ruleset struct {
uncountables map[string]bool
plurals []*Rule
singulars []*Rule
humans []*Rule
acronyms []*Rule
}
// NewRuleset creates a blank ruleset. Unless you are going to
// build your own rules from scratch you probably
// won't need this and can just use the defaultRuleset
// via the global inflect.* methods
func NewRuleset() *Ruleset {
rs := new(Ruleset)
rs.uncountables = make(map[string]bool)
rs.plurals = make([]*Rule, 0)
rs.singulars = make([]*Rule, 0)
rs.humans = make([]*Rule, 0)
rs.acronyms = make([]*Rule, 0)
return rs
}
// NewDefaultRuleset creates a new ruleset and load it with the default
// set of common English pluralization rules
func NewDefaultRuleset() *Ruleset {
rs := NewRuleset()
rs.AddPlural("movie", "movies")
rs.AddPlural("s", "s")
rs.AddPlural("testis", "testes")
rs.AddPlural("axis", "axes")
rs.AddPlural("octopus", "octopi")
rs.AddPlural("virus", "viri")
rs.AddPlural("octopi", "octopi")
rs.AddPlural("viri", "viri")
rs.AddPlural("alias", "aliases")
rs.AddPlural("status", "statuses")
rs.AddPlural("Status", "Statuses")
rs.AddPlural("campus", "campuses")
rs.AddPlural("bus", "buses")
rs.AddPlural("buffalo", "buffaloes")
rs.AddPlural("tomato", "tomatoes")
rs.AddPlural("tum", "ta")
rs.AddPlural("ium", "ia")
rs.AddPlural("ta", "ta")
rs.AddPlural("ia", "ia")
rs.AddPlural("sis", "ses")
rs.AddPlural("lf", "lves")
rs.AddPlural("rf", "rves")
rs.AddPlural("afe", "aves")
rs.AddPlural("bfe", "bves")
rs.AddPlural("cfe", "cves")
rs.AddPlural("dfe", "dves")
rs.AddPlural("efe", "eves")
rs.AddPlural("gfe", "gves")
rs.AddPlural("hfe", "hves")
rs.AddPlural("ife", "ives")
rs.AddPlural("jfe", "jves")
rs.AddPlural("kfe", "kves")
rs.AddPlural("lfe", "lves")
rs.AddPlural("mfe", "mves")
rs.AddPlural("nfe", "nves")
rs.AddPlural("ofe", "oves")
rs.AddPlural("pfe", "pves")
rs.AddPlural("qfe", "qves")
rs.AddPlural("rfe", "rves")
rs.AddPlural("sfe", "sves")
rs.AddPlural("tfe", "tves")
rs.AddPlural("ufe", "uves")
rs.AddPlural("vfe", "vves")
rs.AddPlural("wfe", "wves")
rs.AddPlural("xfe", "xves")
rs.AddPlural("yfe", "yves")
rs.AddPlural("zfe", "zves")
rs.AddPlural("hive", "hives")
rs.AddPlural("quy", "quies")
rs.AddPlural("by", "bies")
rs.AddPlural("cy", "cies")
rs.AddPlural("dy", "dies")
rs.AddPlural("fy", "fies")
rs.AddPlural("gy", "gies")
rs.AddPlural("hy", "hies")
rs.AddPlural("jy", "jies")
rs.AddPlural("ky", "kies")
rs.AddPlural("ly", "lies")
rs.AddPlural("my", "mies")
rs.AddPlural("ny", "nies")
rs.AddPlural("py", "pies")
rs.AddPlural("qy", "qies")
rs.AddPlural("ry", "ries")
rs.AddPlural("sy", "sies")
rs.AddPlural("ty", "ties")
rs.AddPlural("vy", "vies")
rs.AddPlural("wy", "wies")
rs.AddPlural("xy", "xies")
rs.AddPlural("zy", "zies")
rs.AddPlural("x", "xes")
rs.AddPlural("ch", "ches")
rs.AddPlural("ss", "sses")
rs.AddPlural("sh", "shes")
rs.AddPlural("matrix", "matrices")
rs.AddPlural("vertix", "vertices")
rs.AddPlural("indix", "indices")
rs.AddPlural("matrex", "matrices")
rs.AddPlural("vertex", "vertices")
rs.AddPlural("index", "indices")
rs.AddPlural("mouse", "mice")
rs.AddPlural("louse", "lice")
rs.AddPlural("mice", "mice")
rs.AddPlural("lice", "lice")
rs.AddPlural("ress", "resses")
rs.AddPluralExact("ox", "oxen", true)
rs.AddPluralExact("oxen", "oxen", true)
rs.AddPluralExact("quiz", "quizzes", true)
rs.AddSingular("s", "")
rs.AddSingular("ss", "ss")
rs.AddSingular("news", "news")
rs.AddSingular("ta", "tum")
rs.AddSingular("ia", "ium")
rs.AddSingular("analyses", "analysis")
rs.AddSingular("bases", "basis")
rs.AddSingularExact("basis", "basis", true)
rs.AddSingular("diagnoses", "diagnosis")
rs.AddSingularExact("diagnosis", "diagnosis", true)
rs.AddSingular("parentheses", "parenthesis")
rs.AddSingular("prognoses", "prognosis")
rs.AddSingular("synopses", "synopsis")
rs.AddSingular("theses", "thesis")
rs.AddSingular("analyses", "analysis")
rs.AddSingularExact("analysis", "analysis", true)
rs.AddSingular("ovies", "ovie")
rs.AddSingular("aves", "afe")
rs.AddSingular("bves", "bfe")
rs.AddSingular("cves", "cfe")
rs.AddSingular("dves", "dfe")
rs.AddSingular("eves", "efe")
rs.AddSingular("gves", "gfe")
rs.AddSingular("hves", "hfe")
rs.AddSingular("ives", "ife")
rs.AddSingular("jves", "jfe")
rs.AddSingular("kves", "kfe")
rs.AddSingular("lves", "lfe")
rs.AddSingular("mves", "mfe")
rs.AddSingular("nves", "nfe")
rs.AddSingular("oves", "ofe")
rs.AddSingular("pves", "pfe")
rs.AddSingular("qves", "qfe")
rs.AddSingular("rves", "rfe")
rs.AddSingular("sves", "sfe")
rs.AddSingular("tves", "tfe")
rs.AddSingular("uves", "ufe")
rs.AddSingular("vves", "vfe")
rs.AddSingular("wves", "wfe")
rs.AddSingular("xves", "xfe")
rs.AddSingular("yves", "yfe")
rs.AddSingular("zves", "zfe")
rs.AddSingular("hives", "hive")
rs.AddSingular("tives", "tive")
rs.AddSingular("lves", "lf")
rs.AddSingular("rves", "rf")
rs.AddSingular("quies", "quy")
rs.AddSingular("bies", "by")
rs.AddSingular("cies", "cy")
rs.AddSingular("dies", "dy")
rs.AddSingular("fies", "fy")
rs.AddSingular("gies", "gy")
rs.AddSingular("hies", "hy")
rs.AddSingular("jies", "jy")
rs.AddSingular("kies", "ky")
rs.AddSingular("lies", "ly")
rs.AddSingular("mies", "my")
rs.AddSingular("nies", "ny")
rs.AddSingular("pies", "py")
rs.AddSingular("qies", "qy")
rs.AddSingular("ries", "ry")
rs.AddSingular("sies", "sy")
rs.AddSingular("ties", "ty")
// rs.AddSingular("vies", "vy")
rs.AddSingular("wies", "wy")
rs.AddSingular("xies", "xy")
rs.AddSingular("zies", "zy")
rs.AddSingular("series", "series")
rs.AddSingular("xes", "x")
rs.AddSingular("ches", "ch")
rs.AddSingular("sses", "ss")
rs.AddSingular("shes", "sh")
rs.AddSingular("mice", "mouse")
rs.AddSingular("lice", "louse")
rs.AddSingular("buses", "bus")
rs.AddSingularExact("bus", "bus", true)
rs.AddSingular("oes", "o")
rs.AddSingular("shoes", "shoe")
rs.AddSingular("crises", "crisis")
rs.AddSingularExact("crisis", "crisis", true)
rs.AddSingular("axes", "axis")
rs.AddSingularExact("axis", "axis", true)
rs.AddSingular("testes", "testis")
rs.AddSingularExact("testis", "testis", true)
rs.AddSingular("octopi", "octopus")
rs.AddSingularExact("octopus", "octopus", true)
rs.AddSingular("viri", "virus")
rs.AddSingularExact("virus", "virus", true)
rs.AddSingular("statuses", "status")
rs.AddSingular("Statuses", "Status")
rs.AddSingular("campuses", "campus")
rs.AddSingularExact("status", "status", true)
rs.AddSingularExact("Status", "Status", true)
rs.AddSingularExact("campus", "campus", true)
rs.AddSingular("aliases", "alias")
rs.AddSingularExact("alias", "alias", true)
rs.AddSingularExact("oxen", "ox", true)
rs.AddSingular("vertices", "vertex")
rs.AddSingular("indices", "index")
rs.AddSingular("matrices", "matrix")
rs.AddSingularExact("quizzes", "quiz", true)
rs.AddSingular("databases", "database")
rs.AddSingular("resses", "ress")
rs.AddSingular("ress", "ress")
rs.AddIrregular("person", "people")
rs.AddIrregular("man", "men")
rs.AddIrregular("child", "children")
rs.AddIrregular("sex", "sexes")
rs.AddIrregular("move", "moves")
rs.AddIrregular("zombie", "zombies")
rs.AddIrregular("Status", "Statuses")
rs.AddIrregular("status", "statuses")
rs.AddIrregular("campus", "campuses")
rs.AddIrregular("human", "humans")
rs.AddUncountable("equipment")
rs.AddUncountable("information")
rs.AddUncountable("rice")
rs.AddUncountable("money")
rs.AddUncountable("species")
rs.AddUncountable("series")
rs.AddUncountable("fish")
rs.AddUncountable("sheep")
rs.AddUncountable("jeans")
rs.AddUncountable("police")
acronyms := strings.Split(baseAcronyms, ",")
for _, acr := range acronyms {
rs.AddAcronym(acr)
}
return rs
}
// Uncountables returns a map of uncountables in the ruleset
func (rs *Ruleset) Uncountables() map[string]bool {
return rs.uncountables
}
// AddPlural add a pluralization rule
func (rs *Ruleset) AddPlural(suffix, replacement string) {
rs.AddPluralExact(suffix, replacement, false)
}
// AddPluralExact add a pluralization rule with full string match
func (rs *Ruleset) AddPluralExact(suffix, replacement string, exact bool) {
// remove uncountable
delete(rs.uncountables, suffix)
// create rule
r := new(Rule)
r.suffix = suffix
r.replacement = replacement
r.exact = exact
// prepend
rs.plurals = append([]*Rule{r}, rs.plurals...)
}
// AddSingular add a singular rule
func (rs *Ruleset) AddSingular(suffix, replacement string) {
rs.AddSingularExact(suffix, replacement, false)
}
// AddSingularExact same as AddSingular but you can set `exact` to force
// a full string match
func (rs *Ruleset) AddSingularExact(suffix, replacement string, exact bool) {
// remove from uncountable
delete(rs.uncountables, suffix)
// create rule
r := new(Rule)
r.suffix = suffix
r.replacement = replacement
r.exact = exact
rs.singulars = append([]*Rule{r}, rs.singulars...)
}
// AddHuman Human rules are applied by humanize to show more friendly
// versions of words
func (rs *Ruleset) AddHuman(suffix, replacement string) {
r := new(Rule)
r.suffix = suffix
r.replacement = replacement
rs.humans = append([]*Rule{r}, rs.humans...)
}
// AddIrregular Add any inconsistent pluralizing/singularizing rules
// to the set here.
func (rs *Ruleset) AddIrregular(singular, plural string) {
delete(rs.uncountables, singular)
delete(rs.uncountables, plural)
rs.AddPlural(singular, plural)
rs.AddPlural(plural, plural)
rs.AddSingular(plural, singular)
}
// AddAcronym if you use acronym you may need to add them to the ruleset
// to prevent Underscored words of things like "HTML" coming out
// as "h_t_m_l"
func (rs *Ruleset) AddAcronym(word string) {
r := new(Rule)
r.suffix = word
r.replacement = rs.Titleize(strings.ToLower(word))
rs.acronyms = append(rs.acronyms, r)
}
// AddUncountable add a word to this ruleset that has the same singular and plural form
// for example: "rice"
func (rs *Ruleset) AddUncountable(word string) {
rs.uncountables[strings.ToLower(word)] = true
}
func (rs *Ruleset) isUncountable(word string) bool {
// handle multiple words by using the last one
words := strings.Split(word, " ")
if _, exists := rs.uncountables[strings.ToLower(words[len(words)-1])]; exists {
return true
}
return false
}
//isAcronym returns if a word is acronym or not.
func (rs *Ruleset) isAcronym(word string) bool {
for _, rule := range rs.acronyms {
if strings.ToUpper(rule.suffix) == strings.ToUpper(word) {
return true
}
}
return false
}
//PluralizeWithSize pluralize with taking number into account
func (rs *Ruleset) PluralizeWithSize(word string, size int) string {
if size == 1 {
return rs.Singularize(word)
}
return rs.Pluralize(word)
}
// Pluralize returns the plural form of a singular word
func (rs *Ruleset) Pluralize(word string) string {
if len(word) == 0 {
return word
}
lWord := strings.ToLower(word)
if rs.isUncountable(lWord) {
return word
}
var candidate string
for _, rule := range rs.plurals {
if rule.exact {
if lWord == rule.suffix {
// Capitalized word
if lWord[0] != word[0] && lWord[1:] == word[1:] {
return rs.Capitalize(rule.replacement)
}
return rule.replacement
}
continue
}
if strings.EqualFold(word, rule.suffix) {
candidate = rule.replacement
}
if strings.HasSuffix(word, rule.suffix) {
return replaceLast(word, rule.suffix, rule.replacement)
}
}
if candidate != "" {
return candidate
}
return word + "s"
}
//Singularize returns the singular form of a plural word
func (rs *Ruleset) Singularize(word string) string {
if len(word) <= 1 {
return word
}
lWord := strings.ToLower(word)
if rs.isUncountable(lWord) {
return word
}
var candidate string
for _, rule := range rs.singulars {
if rule.exact {
if lWord == rule.suffix {
// Capitalized word
if lWord[0] != word[0] && lWord[1:] == word[1:] {
return rs.Capitalize(rule.replacement)
}
return rule.replacement
}
continue
}
if strings.EqualFold(word, rule.suffix) {
candidate = rule.replacement
}
if strings.HasSuffix(word, rule.suffix) {
return replaceLast(word, rule.suffix, rule.replacement)
}
}
if candidate != "" {
return candidate
}
return word
}
//Capitalize uppercase first character
func (rs *Ruleset) Capitalize(word string) string {
if rs.isAcronym(word) {
return strings.ToUpper(word)
}
return strings.ToUpper(word[:1]) + word[1:]
}
//Camelize "dino_party" -> "DinoParty"
func (rs *Ruleset) Camelize(word string) string {
if rs.isAcronym(word) {
return strings.ToUpper(word)
}
words := splitAtCaseChangeWithTitlecase(word)
return strings.Join(words, "")
}
//CamelizeDownFirst same as Camelcase but with first letter downcased
func (rs *Ruleset) CamelizeDownFirst(word string) string {
word = Camelize(word)
return strings.ToLower(word[:1]) + word[1:]
}
//Titleize Capitalize every word in sentence "hello there" -> "Hello There"
func (rs *Ruleset) Titleize(word string) string {
words := splitAtCaseChangeWithTitlecase(word)
result := strings.Join(words, " ")
var acronymWords []string
for index, word := range words {
if len(word) == 1 {
acronymWords = append(acronymWords, word)
}
if len(word) > 1 || index == len(words)-1 || len(acronymWords) > 1 {
acronym := strings.Join(acronymWords, "")
if !rs.isAcronym(acronym) {
acronymWords = acronymWords[:len(acronymWords)]
continue
}
result = strings.Replace(result, strings.Join(acronymWords, " "), acronym, 1)
acronymWords = []string{}
}
}
return result
}
func (rs *Ruleset) safeCaseAcronyms(word string) string {
// convert an acronym like HTML into Html
for _, rule := range rs.acronyms {
word = strings.Replace(word, rule.suffix, rule.replacement, -1)
}
return word
}
func (rs *Ruleset) separatedWords(word, sep string) string {
word = rs.safeCaseAcronyms(word)
words := splitAtCaseChange(word)
return strings.Join(words, sep)
}
//Underscore lowercase underscore version "BigBen" -> "big_ben"
func (rs *Ruleset) Underscore(word string) string {
return rs.separatedWords(word, "_")
}
//Humanize First letter of sentence capitalized
// Uses custom friendly replacements via AddHuman()
func (rs *Ruleset) Humanize(word string) string {
word = replaceLast(word, "_id", "") // strip foreign key kinds
// replace and strings in humans list
for _, rule := range rs.humans {
word = strings.Replace(word, rule.suffix, rule.replacement, -1)
}
sentence := rs.separatedWords(word, " ")
r, n := utf8.DecodeRuneInString(sentence)
return string(unicode.ToUpper(r)) + sentence[n:]
}
//ForeignKey an underscored foreign key name "Person" -> "person_id"
func (rs *Ruleset) ForeignKey(word string) string {
return rs.Underscore(rs.Singularize(word)) + "_id"
}
//ForeignKeyCondensed a foreign key (with an underscore) "Person" -> "personid"
func (rs *Ruleset) ForeignKeyCondensed(word string) string {
return rs.Underscore(word) + "id"
}
//Tableize Rails style pluralized table names: "SuperPerson" -> "super_people"
func (rs *Ruleset) Tableize(word string) string {
return rs.Pluralize(rs.Underscore(rs.Typeify(word)))
}
var notUrlSafe *regexp.Regexp = regexp.MustCompile(`[^\w\d\-_ ]`)
//Parameterize param safe dasherized names like "my-param"
func (rs *Ruleset) Parameterize(word string) string {
return ParameterizeJoin(word, "-")
}
//ParameterizeJoin param safe dasherized names with custom separator
func (rs *Ruleset) ParameterizeJoin(word, sep string) string {
word = strings.ToLower(word)
word = rs.Asciify(word)
word = notUrlSafe.ReplaceAllString(word, "")
word = strings.Replace(word, " ", sep, -1)
if len(sep) > 0 {
squash, err := regexp.Compile(sep + "+")
if err == nil {
word = squash.ReplaceAllString(word, sep)
}
}
word = strings.Trim(word, sep+" ")
return word
}
var lookalikes = map[string]*regexp.Regexp{
"A": regexp.MustCompile(`À|Á|Â|Ã|Ä|Å`),
"AE": regexp.MustCompile(`Æ`),
"C": regexp.MustCompile(`Ç`),
"E": regexp.MustCompile(`È|É|Ê|Ë`),
"G": regexp.MustCompile(`Ğ`),
"I": regexp.MustCompile(`Ì|Í|Î|Ï|İ`),
"N": regexp.MustCompile(`Ñ`),
"O": regexp.MustCompile(`Ò|Ó|Ô|Õ|Ö|Ø`),
"S": regexp.MustCompile(`Ş`),
"U": regexp.MustCompile(`Ù|Ú|Û|Ü`),
"Y": regexp.MustCompile(`Ý`),
"ss": regexp.MustCompile(`ß`),
"a": regexp.MustCompile(`à|á|â|ã|ä|å`),
"ae": regexp.MustCompile(`æ`),
"c": regexp.MustCompile(`ç`),
"e": regexp.MustCompile(`è|é|ê|ë`),
"g": regexp.MustCompile(`ğ`),
"i": regexp.MustCompile(`ì|í|î|ï|ı`),
"n": regexp.MustCompile(`ñ`),
"o": regexp.MustCompile(`ò|ó|ô|õ|ö|ø`),
"s": regexp.MustCompile(`ş`),
"u": regexp.MustCompile(`ù|ú|û|ü|ũ|ū|ŭ|ů|ű|ų`),
"y": regexp.MustCompile(`ý|ÿ`),
}
//Asciify transforms Latin characters like é -> e
func (rs *Ruleset) Asciify(word string) string {
for repl, regex := range lookalikes {
word = regex.ReplaceAllString(word, repl)
}
return word
}
var tablePrefix = regexp.MustCompile(`^[^.]*\.`)
//Typeify "something_like_this" -> "SomethingLikeThis"
func (rs *Ruleset) Typeify(word string) string {
word = tablePrefix.ReplaceAllString(word, "")
return rs.Camelize(rs.Singularize(word))
}
//Dasherize "SomeText" -> "some-text"
func (rs *Ruleset) Dasherize(word string) string {
return rs.separatedWords(word, "-")
}
//Ordinalize "1031" -> "1031st"
func (rs *Ruleset) Ordinalize(str string) string {
number, err := strconv.Atoi(str)
if err != nil {
return str
}
switch abs(number) % 100 {
case 11, 12, 13:
return fmt.Sprintf("%dth", number)
default:
switch abs(number) % 10 {
case 1:
return fmt.Sprintf("%dst", number)
case 2:
return fmt.Sprintf("%dnd", number)
case 3:
return fmt.Sprintf("%drd", number)
}
}
return fmt.Sprintf("%dth", number)
}
//ForeignKeyToAttribute returns the attribute name from the foreign key
func (rs *Ruleset) ForeignKeyToAttribute(str string) string {
w := rs.Camelize(str)
if strings.HasSuffix(w, "Id") {
return strings.TrimSuffix(w, "Id") + "ID"
}
return w
}
//LoadReader loads rules from io.Reader param
func (rs *Ruleset) LoadReader(r io.Reader) error {
m := map[string]string{}
err := json.NewDecoder(r).Decode(&m)
if err != nil {
return fmt.Errorf("could not decode inflection JSON from reader: %s", err)
}
for s, p := range m {
defaultRuleset.AddIrregular(s, p)
}
return nil
}
/////////////////////////////////////////
// the default global ruleset
//////////////////////////////////////////
var defaultRuleset *Ruleset
//LoadReader loads rules from io.Reader param
func LoadReader(r io.Reader) error {
return defaultRuleset.LoadReader(r)
}
func init() {
defaultRuleset = NewDefaultRuleset()
pwd, _ := os.Getwd()
cfg := filepath.Join(pwd, "inflections.json")
if p := os.Getenv("INFLECT_PATH"); p != "" {
cfg = p
}
if _, err := os.Stat(cfg); err == nil {
b, err := ioutil.ReadFile(cfg)
if err != nil {
fmt.Printf("could not read inflection file %s (%s)\n", cfg, err)
return
}
if err = defaultRuleset.LoadReader(bytes.NewReader(b)); err != nil {
fmt.Println(err)
}
}
}
//Uncountables returns a list of uncountables rules
func Uncountables() map[string]bool {
return defaultRuleset.Uncountables()
}
//AddPlural adds plural to the ruleset
func AddPlural(suffix, replacement string) {
defaultRuleset.AddPlural(suffix, replacement)
}
//AddSingular adds singular to the ruleset
func AddSingular(suffix, replacement string) {
defaultRuleset.AddSingular(suffix, replacement)
}
//AddHuman adds human
func AddHuman(suffix, replacement string) {
defaultRuleset.AddHuman(suffix, replacement)
}
func AddIrregular(singular, plural string) {
defaultRuleset.AddIrregular(singular, plural)
}
func AddAcronym(word string) {
defaultRuleset.AddAcronym(word)
}
func AddUncountable(word string) {
defaultRuleset.AddUncountable(word)
}
func Pluralize(word string) string {
return defaultRuleset.Pluralize(word)
}
func PluralizeWithSize(word string, size int) string {
return defaultRuleset.PluralizeWithSize(word, size)
}
func Singularize(word string) string {
return defaultRuleset.Singularize(word)
}
func Capitalize(word string) string {
return defaultRuleset.Capitalize(word)
}
func Camelize(word string) string {
return defaultRuleset.Camelize(word)
}
func CamelizeDownFirst(word string) string {
return defaultRuleset.CamelizeDownFirst(word)
}
func Titleize(word string) string {
return defaultRuleset.Titleize(word)
}
func Underscore(word string) string {
return defaultRuleset.Underscore(word)
}
func Humanize(word string) string {
return defaultRuleset.Humanize(word)
}
func ForeignKey(word string) string {
return defaultRuleset.ForeignKey(word)
}
func ForeignKeyCondensed(word string) string {
return defaultRuleset.ForeignKeyCondensed(word)
}
func Tableize(word string) string {
return defaultRuleset.Tableize(word)
}
func Parameterize(word string) string {
return defaultRuleset.Parameterize(word)
}
func ParameterizeJoin(word, sep string) string {
return defaultRuleset.ParameterizeJoin(word, sep)
}
func Typeify(word string) string {
return defaultRuleset.Typeify(word)
}
func Dasherize(word string) string {
return defaultRuleset.Dasherize(word)
}
func Ordinalize(word string) string {
return defaultRuleset.Ordinalize(word)
}
func Asciify(word string) string {
return defaultRuleset.Asciify(word)
}
func ForeignKeyToAttribute(word string) string {
return defaultRuleset.ForeignKeyToAttribute(word)
}
// helper funcs
func reverse(s string) string {
o := make([]rune, utf8.RuneCountInString(s))
i := len(o)
for _, c := range s {
i--
o[i] = c
}
return string(o)
}
func isSpacerChar(c rune) bool {
switch {
case c == rune("_"[0]):
return true
case c == rune(" "[0]):
return true
case c == rune(":"[0]):
return true
case c == rune("-"[0]):
return true
}
return false
}
func splitAtCaseChange(s string) []string {
words := make([]string, 0)
word := make([]rune, 0)
for _, c := range s {
spacer := isSpacerChar(c)
if len(word) > 0 {
if unicode.IsUpper(c) || spacer {
words = append(words, string(word))
word = make([]rune, 0)
}
}
if !spacer {
word = append(word, unicode.ToLower(c))
}
}
words = append(words, string(word))
return words
}
func splitAtCaseChangeWithTitlecase(s string) []string {
words := make([]string, 0)
word := make([]rune, 0)
for _, c := range s {
spacer := isSpacerChar(c)
if len(word) > 0 {
if unicode.IsUpper(c) || spacer {
words = append(words, string(word))
word = make([]rune, 0)
}
}
if !spacer {
if len(word) > 0 {
word = append(word, unicode.ToLower(c))
} else {
word = append(word, unicode.ToUpper(c))
}
}
}
words = append(words, string(word))
return words
}
func replaceLast(s, match, repl string) string {
// reverse strings
srev := reverse(s)
mrev := reverse(match)
rrev := reverse(repl)
// match first and reverse back
return reverse(strings.Replace(srev, mrev, rrev, 1))
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}

4
vendor/github.com/markbates/inflect/inflections.json generated vendored Normal file
View File

@ -0,0 +1,4 @@
{
"feedback": "feedback",
"buffalo!": "buffalos!"
}

163
vendor/github.com/markbates/inflect/name.go generated vendored Normal file
View File

@ -0,0 +1,163 @@
package inflect
import (
"fmt"
"path/filepath"
"strings"
"github.com/gobuffalo/envy"
)
// Name is a string that represents the "name" of a thing, like an app, model, etc...
type Name string
// Title version of a name. ie. "foo_bar" => "Foo Bar"
func (n Name) Title() string {
x := strings.Split(string(n), "/")
for i, s := range x {
x[i] = Titleize(s)
}
return strings.Join(x, " ")
}
// Underscore version of a name. ie. "FooBar" => "foo_bar"
func (n Name) Underscore() string {
w := string(n)
if strings.ToUpper(w) == w {
return strings.ToLower(w)
}
return Underscore(w)
}
// Plural version of a name
func (n Name) Plural() string {
return Pluralize(string(n))
}
// Singular version of a name
func (n Name) Singular() string {
return Singularize(string(n))
}
// Camel version of a name
func (n Name) Camel() string {
c := Camelize(string(n))
if strings.HasSuffix(c, "Id") {
c = strings.TrimSuffix(c, "Id")
c += "ID"
}
return c
}
// Model version of a name. ie. "user" => "User"
func (n Name) Model() string {
x := strings.Split(string(n), "/")
for i, s := range x {
x[i] = Camelize(Singularize(s))
}
return strings.Join(x, "")
}
// Resource version of a name
func (n Name) Resource() string {
name := n.Underscore()
x := strings.FieldsFunc(name, func(r rune) bool {
return r == '_' || r == '/'
})
for i, w := range x {
if i == len(x)-1 {
x[i] = Camelize(Pluralize(strings.ToLower(w)))
continue
}
x[i] = Camelize(w)
}
return strings.Join(x, "")
}
// ModelPlural version of a name. ie. "user" => "Users"
func (n Name) ModelPlural() string {
return Camelize(Pluralize(n.Model()))
}
// File version of a name
func (n Name) File() string {
return Underscore(Camelize(string(n)))
}
// Table version of a name
func (n Name) Table() string {
return Underscore(Pluralize(string(n)))
}
// UnderSingular version of a name
func (n Name) UnderSingular() string {
return Underscore(Singularize(string(n)))
}
// PluralCamel version of a name
func (n Name) PluralCamel() string {
return Pluralize(Camelize(string(n)))
}
// PluralUnder version of a name
func (n Name) PluralUnder() string {
return Pluralize(Underscore(string(n)))
}
// URL version of a name
func (n Name) URL() string {
return n.PluralUnder()
}
// CamelSingular version of a name
func (n Name) CamelSingular() string {
return Camelize(Singularize(string(n)))
}
// VarCaseSingular version of a name. ie. "FooBar" => "fooBar"
func (n Name) VarCaseSingular() string {
return CamelizeDownFirst(Singularize(Underscore(n.Resource())))
}
// VarCasePlural version of a name. ie. "FooBar" => "fooBar"
func (n Name) VarCasePlural() string {
return CamelizeDownFirst(n.Resource())
}
// Lower case version of a string
func (n Name) Lower() string {
return strings.ToLower(string(n))
}
// ParamID returns foo_bar_id
func (n Name) ParamID() string {
return fmt.Sprintf("%s_id", strings.Replace(n.UnderSingular(), "/", "_", -1))
}
// Package returns go package
func (n Name) Package() string {
key := string(n)
for _, gp := range envy.GoPaths() {
key = strings.TrimPrefix(key, filepath.Join(gp, "src"))
key = strings.TrimPrefix(key, gp)
}
key = strings.TrimPrefix(key, string(filepath.Separator))
key = strings.Replace(key, "\\", "/", -1)
return key
}
// Char returns first character in lower case, this is useful for methods inside a struct.
func (n Name) Char() string {
return strings.ToLower(string(n[0]))
}
func (n Name) String() string {
return string(n)
}

12
vendor/github.com/markbates/inflect/shoulders.md generated vendored Normal file
View File

@ -0,0 +1,12 @@
# github.com/markbates/inflect Stands on the Shoulders of Giants
github.com/markbates/inflect does not try to reinvent the wheel! Instead, it uses the already great wheels developed by the Go community and puts them all together in the best way possible. Without these giants this project would not be possible. Please make sure to check them out and thank them for all of their hard work.
Thank you to the following **GIANTS**:
* [github.com/gobuffalo/envy](https://godoc.org/github.com/gobuffalo/envy)
* [github.com/joho/godotenv](https://godoc.org/github.com/joho/godotenv)
* [github.com/markbates/inflect](https://godoc.org/github.com/markbates/inflect)

3
vendor/github.com/markbates/inflect/version.go generated vendored Normal file
View File

@ -0,0 +1,3 @@
package inflect
const Version = "v1.0.4"

22
vendor/github.com/robfig/cron/.gitignore generated vendored Normal file
View File

@ -0,0 +1,22 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe

1
vendor/github.com/robfig/cron/.travis.yml generated vendored Normal file
View File

@ -0,0 +1 @@
language: go

21
vendor/github.com/robfig/cron/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
Copyright (C) 2012 Rob Figueiredo
All Rights Reserved.
MIT LICENSE
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

6
vendor/github.com/robfig/cron/README.md generated vendored Normal file
View File

@ -0,0 +1,6 @@
[![GoDoc](http://godoc.org/github.com/robfig/cron?status.png)](http://godoc.org/github.com/robfig/cron)
[![Build Status](https://travis-ci.org/robfig/cron.svg?branch=master)](https://travis-ci.org/robfig/cron)
# cron
Documentation here: https://godoc.org/github.com/robfig/cron

27
vendor/github.com/robfig/cron/constantdelay.go generated vendored Normal file
View File

@ -0,0 +1,27 @@
package cron
import "time"
// ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes".
// It does not support jobs more frequent than once a second.
type ConstantDelaySchedule struct {
Delay time.Duration
}
// Every returns a crontab Schedule that activates once every duration.
// Delays of less than a second are not supported (will round up to 1 second).
// Any fields less than a Second are truncated.
func Every(duration time.Duration) ConstantDelaySchedule {
if duration < time.Second {
duration = time.Second
}
return ConstantDelaySchedule{
Delay: duration - time.Duration(duration.Nanoseconds())%time.Second,
}
}
// Next returns the next time this should be run.
// This rounds so that the next activation time will be on the second.
func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time {
return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond)
}

259
vendor/github.com/robfig/cron/cron.go generated vendored Normal file
View File

@ -0,0 +1,259 @@
package cron
import (
"log"
"runtime"
"sort"
"time"
)
// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
entries []*Entry
stop chan struct{}
add chan *Entry
snapshot chan []*Entry
running bool
ErrorLog *log.Logger
location *time.Location
}
// Job is an interface for submitted cron jobs.
type Job interface {
Run()
}
// The Schedule describes a job's duty cycle.
type Schedule interface {
// Return the next activation time, later than the given time.
// Next is invoked initially, and then each time the job is run.
Next(time.Time) time.Time
}
// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
// The schedule on which this job should be run.
Schedule Schedule
// The next time the job will run. This is the zero time if Cron has not been
// started or this entry's schedule is unsatisfiable
Next time.Time
// The last time this job was run. This is the zero time if the job has never
// been run.
Prev time.Time
// The Job to run.
Job Job
}
// byTime is a wrapper for sorting the entry array by time
// (with zero time at the end).
type byTime []*Entry
func (s byTime) Len() int { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
// Two zero times should return false.
// Otherwise, zero is "greater" than any other time.
// (To sort it at the end of the list.)
if s[i].Next.IsZero() {
return false
}
if s[j].Next.IsZero() {
return true
}
return s[i].Next.Before(s[j].Next)
}
// New returns a new Cron job runner, in the Local time zone.
func New() *Cron {
return NewWithLocation(time.Now().Location())
}
// NewWithLocation returns a new Cron job runner.
func NewWithLocation(location *time.Location) *Cron {
return &Cron{
entries: nil,
add: make(chan *Entry),
stop: make(chan struct{}),
snapshot: make(chan []*Entry),
running: false,
ErrorLog: nil,
location: location,
}
}
// A wrapper that turns a func() into a cron.Job
type FuncJob func()
func (f FuncJob) Run() { f() }
// AddFunc adds a func to the Cron to be run on the given schedule.
func (c *Cron) AddFunc(spec string, cmd func()) error {
return c.AddJob(spec, FuncJob(cmd))
}
// AddJob adds a Job to the Cron to be run on the given schedule.
func (c *Cron) AddJob(spec string, cmd Job) error {
schedule, err := Parse(spec)
if err != nil {
return err
}
c.Schedule(schedule, cmd)
return nil
}
// Schedule adds a Job to the Cron to be run on the given schedule.
func (c *Cron) Schedule(schedule Schedule, cmd Job) {
entry := &Entry{
Schedule: schedule,
Job: cmd,
}
if !c.running {
c.entries = append(c.entries, entry)
return
}
c.add <- entry
}
// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []*Entry {
if c.running {
c.snapshot <- nil
x := <-c.snapshot
return x
}
return c.entrySnapshot()
}
// Location gets the time zone location
func (c *Cron) Location() *time.Location {
return c.location
}
// Start the cron scheduler in its own go-routine, or no-op if already started.
func (c *Cron) Start() {
if c.running {
return
}
c.running = true
go c.run()
}
// Run the cron scheduler, or no-op if already running.
func (c *Cron) Run() {
if c.running {
return
}
c.running = true
c.run()
}
func (c *Cron) runWithRecovery(j Job) {
defer func() {
if r := recover(); r != nil {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
c.logf("cron: panic running job: %v\n%s", r, buf)
}
}()
j.Run()
}
// Run the scheduler. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
// Figure out the next activation times for each entry.
now := c.now()
for _, entry := range c.entries {
entry.Next = entry.Schedule.Next(now)
}
for {
// Determine the next entry to run.
sort.Sort(byTime(c.entries))
var timer *time.Timer
if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
// If there are no entries yet, just sleep - it still handles new entries
// and stop requests.
timer = time.NewTimer(100000 * time.Hour)
} else {
timer = time.NewTimer(c.entries[0].Next.Sub(now))
}
for {
select {
case now = <-timer.C:
now = now.In(c.location)
// Run every entry whose next time was less than now
for _, e := range c.entries {
if e.Next.After(now) || e.Next.IsZero() {
break
}
go c.runWithRecovery(e.Job)
e.Prev = e.Next
e.Next = e.Schedule.Next(now)
}
case newEntry := <-c.add:
timer.Stop()
now = c.now()
newEntry.Next = newEntry.Schedule.Next(now)
c.entries = append(c.entries, newEntry)
case <-c.snapshot:
c.snapshot <- c.entrySnapshot()
continue
case <-c.stop:
timer.Stop()
return
}
break
}
}
}
// Logs an error to stderr or to the configured error log
func (c *Cron) logf(format string, args ...interface{}) {
if c.ErrorLog != nil {
c.ErrorLog.Printf(format, args...)
} else {
log.Printf(format, args...)
}
}
// Stop stops the cron scheduler if it is running; otherwise it does nothing.
func (c *Cron) Stop() {
if !c.running {
return
}
c.stop <- struct{}{}
c.running = false
}
// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []*Entry {
entries := []*Entry{}
for _, e := range c.entries {
entries = append(entries, &Entry{
Schedule: e.Schedule,
Next: e.Next,
Prev: e.Prev,
Job: e.Job,
})
}
return entries
}
// now returns current time in c location
func (c *Cron) now() time.Time {
return time.Now().In(c.location)
}

129
vendor/github.com/robfig/cron/doc.go generated vendored Normal file
View File

@ -0,0 +1,129 @@
/*
Package cron implements a cron spec parser and job runner.
Usage
Callers may register Funcs to be invoked on a given schedule. Cron will run
them in their own goroutines.
c := cron.New()
c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("@hourly", func() { fmt.Println("Every hour") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") })
c.Start()
..
// Funcs are invoked in their own goroutine, asynchronously.
...
// Funcs may also be added to a running Cron
c.AddFunc("@daily", func() { fmt.Println("Every day") })
..
// Inspect the cron job entries' next and previous run times.
inspect(c.Entries())
..
c.Stop() // Stop the scheduler (does not stop any jobs already running).
CRON Expression Format
A cron expression represents a set of times, using 6 space-separated fields.
Field name | Mandatory? | Allowed values | Allowed special characters
---------- | ---------- | -------------- | --------------------------
Seconds | Yes | 0-59 | * / , -
Minutes | Yes | 0-59 | * / , -
Hours | Yes | 0-23 | * / , -
Day of month | Yes | 1-31 | * / , - ?
Month | Yes | 1-12 or JAN-DEC | * / , -
Day of week | Yes | 0-6 or SUN-SAT | * / , - ?
Note: Month and Day-of-week field values are case insensitive. "SUN", "Sun",
and "sun" are equally accepted.
Special Characters
Asterisk ( * )
The asterisk indicates that the cron expression will match for all values of the
field; e.g., using an asterisk in the 5th field (month) would indicate every
month.
Slash ( / )
Slashes are used to describe increments of ranges. For example 3-59/15 in the
1st field (minutes) would indicate the 3rd minute of the hour and every 15
minutes thereafter. The form "*\/..." is equivalent to the form "first-last/...",
that is, an increment over the largest possible range of the field. The form
"N/..." is accepted as meaning "N-MAX/...", that is, starting at N, use the
increment until the end of that specific range. It does not wrap around.
Comma ( , )
Commas are used to separate items of a list. For example, using "MON,WED,FRI" in
the 5th field (day of week) would mean Mondays, Wednesdays and Fridays.
Hyphen ( - )
Hyphens are used to define ranges. For example, 9-17 would indicate every
hour between 9am and 5pm inclusive.
Question mark ( ? )
Question mark may be used instead of '*' for leaving either day-of-month or
day-of-week blank.
Predefined schedules
You may use one of several pre-defined schedules in place of a cron expression.
Entry | Description | Equivalent To
----- | ----------- | -------------
@yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 *
@monthly | Run once a month, midnight, first of month | 0 0 0 1 * *
@weekly | Run once a week, midnight between Sat/Sun | 0 0 0 * * 0
@daily (or @midnight) | Run once a day, midnight | 0 0 0 * * *
@hourly | Run once an hour, beginning of hour | 0 0 * * * *
Intervals
You may also schedule a job to execute at fixed intervals, starting at the time it's added
or cron is run. This is supported by formatting the cron spec like this:
@every <duration>
where "duration" is a string accepted by time.ParseDuration
(http://golang.org/pkg/time/#ParseDuration).
For example, "@every 1h30m10s" would indicate a schedule that activates after
1 hour, 30 minutes, 10 seconds, and then every interval after that.
Note: The interval does not take the job runtime into account. For example,
if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes,
it will have only 2 minutes of idle time between each run.
Time zones
All interpretation and scheduling is done in the machine's local time zone (as
provided by the Go time package (http://www.golang.org/pkg/time).
Be aware that jobs scheduled during daylight-savings leap-ahead transitions will
not be run!
Thread safety
Since the Cron service runs concurrently with the calling code, some amount of
care must be taken to ensure proper synchronization.
All cron methods are designed to be correctly synchronized as long as the caller
ensures that invocations have a clear happens-before ordering between them.
Implementation
Cron entries are stored in an array, sorted by their next activation time. Cron
sleeps until the next job is due to be run.
Upon waking:
- it runs each entry that is active on that second
- it calculates the next run times for the jobs that were run
- it re-sorts the array of entries by next activation time.
- it goes to sleep until the soonest job.
*/
package cron

380
vendor/github.com/robfig/cron/parser.go generated vendored Normal file
View File

@ -0,0 +1,380 @@
package cron
import (
"fmt"
"math"
"strconv"
"strings"
"time"
)
// Configuration options for creating a parser. Most options specify which
// fields should be included, while others enable features. If a field is not
// included the parser will assume a default value. These options do not change
// the order fields are parse in.
type ParseOption int
const (
Second ParseOption = 1 << iota // Seconds field, default 0
Minute // Minutes field, default 0
Hour // Hours field, default 0
Dom // Day of month field, default *
Month // Month field, default *
Dow // Day of week field, default *
DowOptional // Optional day of week field, default *
Descriptor // Allow descriptors such as @monthly, @weekly, etc.
)
var places = []ParseOption{
Second,
Minute,
Hour,
Dom,
Month,
Dow,
}
var defaults = []string{
"0",
"0",
"0",
"*",
"*",
"*",
}
// A custom Parser that can be configured.
type Parser struct {
options ParseOption
optionals int
}
// Creates a custom Parser with custom options.
//
// // Standard parser without descriptors
// specParser := NewParser(Minute | Hour | Dom | Month | Dow)
// sched, err := specParser.Parse("0 0 15 */3 *")
//
// // Same as above, just excludes time fields
// subsParser := NewParser(Dom | Month | Dow)
// sched, err := specParser.Parse("15 */3 *")
//
// // Same as above, just makes Dow optional
// subsParser := NewParser(Dom | Month | DowOptional)
// sched, err := specParser.Parse("15 */3")
//
func NewParser(options ParseOption) Parser {
optionals := 0
if options&DowOptional > 0 {
options |= Dow
optionals++
}
return Parser{options, optionals}
}
// Parse returns a new crontab schedule representing the given spec.
// It returns a descriptive error if the spec is not valid.
// It accepts crontab specs and features configured by NewParser.
func (p Parser) Parse(spec string) (Schedule, error) {
if len(spec) == 0 {
return nil, fmt.Errorf("Empty spec string")
}
if spec[0] == '@' && p.options&Descriptor > 0 {
return parseDescriptor(spec)
}
// Figure out how many fields we need
max := 0
for _, place := range places {
if p.options&place > 0 {
max++
}
}
min := max - p.optionals
// Split fields on whitespace
fields := strings.Fields(spec)
// Validate number of fields
if count := len(fields); count < min || count > max {
if min == max {
return nil, fmt.Errorf("Expected exactly %d fields, found %d: %s", min, count, spec)
}
return nil, fmt.Errorf("Expected %d to %d fields, found %d: %s", min, max, count, spec)
}
// Fill in missing fields
fields = expandFields(fields, p.options)
var err error
field := func(field string, r bounds) uint64 {
if err != nil {
return 0
}
var bits uint64
bits, err = getField(field, r)
return bits
}
var (
second = field(fields[0], seconds)
minute = field(fields[1], minutes)
hour = field(fields[2], hours)
dayofmonth = field(fields[3], dom)
month = field(fields[4], months)
dayofweek = field(fields[5], dow)
)
if err != nil {
return nil, err
}
return &SpecSchedule{
Second: second,
Minute: minute,
Hour: hour,
Dom: dayofmonth,
Month: month,
Dow: dayofweek,
}, nil
}
func expandFields(fields []string, options ParseOption) []string {
n := 0
count := len(fields)
expFields := make([]string, len(places))
copy(expFields, defaults)
for i, place := range places {
if options&place > 0 {
expFields[i] = fields[n]
n++
}
if n == count {
break
}
}
return expFields
}
var standardParser = NewParser(
Minute | Hour | Dom | Month | Dow | Descriptor,
)
// ParseStandard returns a new crontab schedule representing the given standardSpec
// (https://en.wikipedia.org/wiki/Cron). It differs from Parse requiring to always
// pass 5 entries representing: minute, hour, day of month, month and day of week,
// in that order. It returns a descriptive error if the spec is not valid.
//
// It accepts
// - Standard crontab specs, e.g. "* * * * ?"
// - Descriptors, e.g. "@midnight", "@every 1h30m"
func ParseStandard(standardSpec string) (Schedule, error) {
return standardParser.Parse(standardSpec)
}
var defaultParser = NewParser(
Second | Minute | Hour | Dom | Month | DowOptional | Descriptor,
)
// Parse returns a new crontab schedule representing the given spec.
// It returns a descriptive error if the spec is not valid.
//
// It accepts
// - Full crontab specs, e.g. "* * * * * ?"
// - Descriptors, e.g. "@midnight", "@every 1h30m"
func Parse(spec string) (Schedule, error) {
return defaultParser.Parse(spec)
}
// getField returns an Int with the bits set representing all of the times that
// the field represents or error parsing field value. A "field" is a comma-separated
// list of "ranges".
func getField(field string, r bounds) (uint64, error) {
var bits uint64
ranges := strings.FieldsFunc(field, func(r rune) bool { return r == ',' })
for _, expr := range ranges {
bit, err := getRange(expr, r)
if err != nil {
return bits, err
}
bits |= bit
}
return bits, nil
}
// getRange returns the bits indicated by the given expression:
// number | number "-" number [ "/" number ]
// or error parsing range.
func getRange(expr string, r bounds) (uint64, error) {
var (
start, end, step uint
rangeAndStep = strings.Split(expr, "/")
lowAndHigh = strings.Split(rangeAndStep[0], "-")
singleDigit = len(lowAndHigh) == 1
err error
)
var extra uint64
if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" {
start = r.min
end = r.max
extra = starBit
} else {
start, err = parseIntOrName(lowAndHigh[0], r.names)
if err != nil {
return 0, err
}
switch len(lowAndHigh) {
case 1:
end = start
case 2:
end, err = parseIntOrName(lowAndHigh[1], r.names)
if err != nil {
return 0, err
}
default:
return 0, fmt.Errorf("Too many hyphens: %s", expr)
}
}
switch len(rangeAndStep) {
case 1:
step = 1
case 2:
step, err = mustParseInt(rangeAndStep[1])
if err != nil {
return 0, err
}
// Special handling: "N/step" means "N-max/step".
if singleDigit {
end = r.max
}
default:
return 0, fmt.Errorf("Too many slashes: %s", expr)
}
if start < r.min {
return 0, fmt.Errorf("Beginning of range (%d) below minimum (%d): %s", start, r.min, expr)
}
if end > r.max {
return 0, fmt.Errorf("End of range (%d) above maximum (%d): %s", end, r.max, expr)
}
if start > end {
return 0, fmt.Errorf("Beginning of range (%d) beyond end of range (%d): %s", start, end, expr)
}
if step == 0 {
return 0, fmt.Errorf("Step of range should be a positive number: %s", expr)
}
return getBits(start, end, step) | extra, nil
}
// parseIntOrName returns the (possibly-named) integer contained in expr.
func parseIntOrName(expr string, names map[string]uint) (uint, error) {
if names != nil {
if namedInt, ok := names[strings.ToLower(expr)]; ok {
return namedInt, nil
}
}
return mustParseInt(expr)
}
// mustParseInt parses the given expression as an int or returns an error.
func mustParseInt(expr string) (uint, error) {
num, err := strconv.Atoi(expr)
if err != nil {
return 0, fmt.Errorf("Failed to parse int from %s: %s", expr, err)
}
if num < 0 {
return 0, fmt.Errorf("Negative number (%d) not allowed: %s", num, expr)
}
return uint(num), nil
}
// getBits sets all bits in the range [min, max], modulo the given step size.
func getBits(min, max, step uint) uint64 {
var bits uint64
// If step is 1, use shifts.
if step == 1 {
return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min)
}
// Else, use a simple loop.
for i := min; i <= max; i += step {
bits |= 1 << i
}
return bits
}
// all returns all bits within the given bounds. (plus the star bit)
func all(r bounds) uint64 {
return getBits(r.min, r.max, 1) | starBit
}
// parseDescriptor returns a predefined schedule for the expression, or error if none matches.
func parseDescriptor(descriptor string) (Schedule, error) {
switch descriptor {
case "@yearly", "@annually":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: 1 << dom.min,
Month: 1 << months.min,
Dow: all(dow),
}, nil
case "@monthly":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: 1 << dom.min,
Month: all(months),
Dow: all(dow),
}, nil
case "@weekly":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: all(dom),
Month: all(months),
Dow: 1 << dow.min,
}, nil
case "@daily", "@midnight":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: all(dom),
Month: all(months),
Dow: all(dow),
}, nil
case "@hourly":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: all(hours),
Dom: all(dom),
Month: all(months),
Dow: all(dow),
}, nil
}
const every = "@every "
if strings.HasPrefix(descriptor, every) {
duration, err := time.ParseDuration(descriptor[len(every):])
if err != nil {
return nil, fmt.Errorf("Failed to parse duration %s: %s", descriptor, err)
}
return Every(duration), nil
}
return nil, fmt.Errorf("Unrecognized descriptor: %s", descriptor)
}

158
vendor/github.com/robfig/cron/spec.go generated vendored Normal file
View File

@ -0,0 +1,158 @@
package cron
import "time"
// SpecSchedule specifies a duty cycle (to the second granularity), based on a
// traditional crontab specification. It is computed initially and stored as bit sets.
type SpecSchedule struct {
Second, Minute, Hour, Dom, Month, Dow uint64
}
// bounds provides a range of acceptable values (plus a map of name to value).
type bounds struct {
min, max uint
names map[string]uint
}
// The bounds for each field.
var (
seconds = bounds{0, 59, nil}
minutes = bounds{0, 59, nil}
hours = bounds{0, 23, nil}
dom = bounds{1, 31, nil}
months = bounds{1, 12, map[string]uint{
"jan": 1,
"feb": 2,
"mar": 3,
"apr": 4,
"may": 5,
"jun": 6,
"jul": 7,
"aug": 8,
"sep": 9,
"oct": 10,
"nov": 11,
"dec": 12,
}}
dow = bounds{0, 6, map[string]uint{
"sun": 0,
"mon": 1,
"tue": 2,
"wed": 3,
"thu": 4,
"fri": 5,
"sat": 6,
}}
)
const (
// Set the top bit if a star was included in the expression.
starBit = 1 << 63
)
// Next returns the next time this schedule is activated, greater than the given
// time. If no time can be found to satisfy the schedule, return the zero time.
func (s *SpecSchedule) Next(t time.Time) time.Time {
// General approach:
// For Month, Day, Hour, Minute, Second:
// Check if the time value matches. If yes, continue to the next field.
// If the field doesn't match the schedule, then increment the field until it matches.
// While incrementing the field, a wrap-around brings it back to the beginning
// of the field list (since it is necessary to re-verify previous field
// values)
// Start at the earliest possible time (the upcoming second).
t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond)
// This flag indicates whether a field has been incremented.
added := false
// If no time is found within five years, return zero.
yearLimit := t.Year() + 5
WRAP:
if t.Year() > yearLimit {
return time.Time{}
}
// Find the first applicable month.
// If it's this month, then do nothing.
for 1<<uint(t.Month())&s.Month == 0 {
// If we have to add a month, reset the other parts to 0.
if !added {
added = true
// Otherwise, set the date at the beginning (since the current time is irrelevant).
t = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location())
}
t = t.AddDate(0, 1, 0)
// Wrapped around.
if t.Month() == time.January {
goto WRAP
}
}
// Now get a day in that month.
for !dayMatches(s, t) {
if !added {
added = true
t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
}
t = t.AddDate(0, 0, 1)
if t.Day() == 1 {
goto WRAP
}
}
for 1<<uint(t.Hour())&s.Hour == 0 {
if !added {
added = true
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, t.Location())
}
t = t.Add(1 * time.Hour)
if t.Hour() == 0 {
goto WRAP
}
}
for 1<<uint(t.Minute())&s.Minute == 0 {
if !added {
added = true
t = t.Truncate(time.Minute)
}
t = t.Add(1 * time.Minute)
if t.Minute() == 0 {
goto WRAP
}
}
for 1<<uint(t.Second())&s.Second == 0 {
if !added {
added = true
t = t.Truncate(time.Second)
}
t = t.Add(1 * time.Second)
if t.Second() == 0 {
goto WRAP
}
}
return t
}
// dayMatches returns true if the schedule's day-of-week and day-of-month
// restrictions are satisfied by the given time.
func dayMatches(s *SpecSchedule, t time.Time) bool {
var (
domMatch bool = 1<<uint(t.Day())&s.Dom > 0
dowMatch bool = 1<<uint(t.Weekday())&s.Dow > 0
)
if s.Dom&starBit > 0 || s.Dow&starBit > 0 {
return domMatch && dowMatch
}
return domMatch || dowMatch
}

66
vendor/golang.org/x/sync/errgroup/errgroup.go generated vendored Normal file
View File

@ -0,0 +1,66 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package errgroup provides synchronization, error propagation, and Context
// cancelation for groups of goroutines working on subtasks of a common task.
package errgroup
import (
"context"
"sync"
)
// A Group is a collection of goroutines working on subtasks that are part of
// the same overall task.
//
// A zero Group is valid and does not cancel on error.
type Group struct {
cancel func()
wg sync.WaitGroup
errOnce sync.Once
err error
}
// WithContext returns a new Group and an associated Context derived from ctx.
//
// The derived Context is canceled the first time a function passed to Go
// returns a non-nil error or the first time Wait returns, whichever occurs
// first.
func WithContext(ctx context.Context) (*Group, context.Context) {
ctx, cancel := context.WithCancel(ctx)
return &Group{cancel: cancel}, ctx
}
// Wait blocks until all function calls from the Go method have returned, then
// returns the first non-nil error (if any) from them.
func (g *Group) Wait() error {
g.wg.Wait()
if g.cancel != nil {
g.cancel()
}
return g.err
}
// Go calls the given function in a new goroutine.
//
// The first call to return a non-nil error cancels the group; its error will be
// returned by Wait.
func (g *Group) Go(f func() error) {
g.wg.Add(1)
go func() {
defer g.wg.Done()
if err := f(); err != nil {
g.errOnce.Do(func() {
g.err = err
if g.cancel != nil {
g.cancel()
}
})
}
}()
}

23
vendor/k8s.io/api/admission/v1beta1/doc.go generated vendored Normal file
View File

@ -0,0 +1,23 @@
/*
Copyright 2017 The Kubernetes 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.
*/
// +k8s:deepcopy-gen=package
// +k8s:protobuf-gen=package
// +k8s:openapi-gen=false
// +groupName=admission.k8s.io
package v1beta1 // import "k8s.io/api/admission/v1beta1"

1573
vendor/k8s.io/api/admission/v1beta1/generated.pb.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

160
vendor/k8s.io/api/admission/v1beta1/generated.proto generated vendored Normal file
View File

@ -0,0 +1,160 @@
/*
Copyright The Kubernetes 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.
*/
// This file was autogenerated by go-to-protobuf. Do not edit it manually!
syntax = 'proto2';
package k8s.io.api.admission.v1beta1;
import "k8s.io/api/authentication/v1/generated.proto";
import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "v1beta1";
// AdmissionRequest describes the admission.Attributes for the admission request.
message AdmissionRequest {
// UID is an identifier for the individual request/response. It allows us to distinguish instances of requests which are
// otherwise identical (parallel requests, requests when earlier requests did not modify etc)
// The UID is meant to track the round trip (request/response) between the KAS and the WebHook, not the user request.
// It is suitable for correlating log entries between the webhook and apiserver, for either auditing or debugging.
optional string uid = 1;
// Kind is the fully-qualified type of object being submitted (for example, v1.Pod or autoscaling.v1.Scale)
optional k8s.io.apimachinery.pkg.apis.meta.v1.GroupVersionKind kind = 2;
// Resource is the fully-qualified resource being requested (for example, v1.pods)
optional k8s.io.apimachinery.pkg.apis.meta.v1.GroupVersionResource resource = 3;
// SubResource is the subresource being requested, if any (for example, "status" or "scale")
// +optional
optional string subResource = 4;
// RequestKind is the fully-qualified type of the original API request (for example, v1.Pod or autoscaling.v1.Scale).
// If this is specified and differs from the value in "kind", an equivalent match and conversion was performed.
//
// For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
// `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
// an API request to apps/v1beta1 deployments would be converted and sent to the webhook
// with `kind: {group:"apps", version:"v1", kind:"Deployment"}` (matching the rule the webhook registered for),
// and `requestKind: {group:"apps", version:"v1beta1", kind:"Deployment"}` (indicating the kind of the original API request).
//
// See documentation for the "matchPolicy" field in the webhook configuration type for more details.
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.GroupVersionKind requestKind = 13;
// RequestResource is the fully-qualified resource of the original API request (for example, v1.pods).
// If this is specified and differs from the value in "resource", an equivalent match and conversion was performed.
//
// For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
// `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
// an API request to apps/v1beta1 deployments would be converted and sent to the webhook
// with `resource: {group:"apps", version:"v1", resource:"deployments"}` (matching the resource the webhook registered for),
// and `requestResource: {group:"apps", version:"v1beta1", resource:"deployments"}` (indicating the resource of the original API request).
//
// See documentation for the "matchPolicy" field in the webhook configuration type.
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.GroupVersionResource requestResource = 14;
// RequestSubResource is the name of the subresource of the original API request, if any (for example, "status" or "scale")
// If this is specified and differs from the value in "subResource", an equivalent match and conversion was performed.
// See documentation for the "matchPolicy" field in the webhook configuration type.
// +optional
optional string requestSubResource = 15;
// Name is the name of the object as presented in the request. On a CREATE operation, the client may omit name and
// rely on the server to generate the name. If that is the case, this method will return the empty string.
// +optional
optional string name = 5;
// Namespace is the namespace associated with the request (if any).
// +optional
optional string namespace = 6;
// Operation is the operation being performed. This may be different than the operation
// requested. e.g. a patch can result in either a CREATE or UPDATE Operation.
optional string operation = 7;
// UserInfo is information about the requesting user
optional k8s.io.api.authentication.v1.UserInfo userInfo = 8;
// Object is the object from the incoming request prior to default values being applied
// +optional
optional k8s.io.apimachinery.pkg.runtime.RawExtension object = 9;
// OldObject is the existing object. Only populated for UPDATE requests.
// +optional
optional k8s.io.apimachinery.pkg.runtime.RawExtension oldObject = 10;
// DryRun indicates that modifications will definitely not be persisted for this request.
// Defaults to false.
// +optional
optional bool dryRun = 11;
// Options is the operation option structure of the operation being performed.
// e.g. `meta.k8s.io/v1.DeleteOptions` or `meta.k8s.io/v1.CreateOptions`. This may be
// different than the options the caller provided. e.g. for a patch request the performed
// Operation might be a CREATE, in which case the Options will a
// `meta.k8s.io/v1.CreateOptions` even though the caller provided `meta.k8s.io/v1.PatchOptions`.
// +optional
optional k8s.io.apimachinery.pkg.runtime.RawExtension options = 12;
}
// AdmissionResponse describes an admission response.
message AdmissionResponse {
// UID is an identifier for the individual request/response.
// This should be copied over from the corresponding AdmissionRequest.
optional string uid = 1;
// Allowed indicates whether or not the admission request was permitted.
optional bool allowed = 2;
// Result contains extra details into why an admission request was denied.
// This field IS NOT consulted in any way if "Allowed" is "true".
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.Status status = 3;
// The patch body. Currently we only support "JSONPatch" which implements RFC 6902.
// +optional
optional bytes patch = 4;
// The type of Patch. Currently we only allow "JSONPatch".
// +optional
optional string patchType = 5;
// AuditAnnotations is an unstructured key value map set by remote admission controller (e.g. error=image-blacklisted).
// MutatingAdmissionWebhook and ValidatingAdmissionWebhook admission controller will prefix the keys with
// admission webhook name (e.g. imagepolicy.example.com/error=image-blacklisted). AuditAnnotations will be provided by
// the admission webhook to add additional context to the audit log for this request.
// +optional
map<string, string> auditAnnotations = 6;
}
// AdmissionReview describes an admission review request/response.
message AdmissionReview {
// Request describes the attributes for the admission request.
// +optional
optional AdmissionRequest request = 1;
// Response describes the attributes for the admission response.
// +optional
optional AdmissionResponse response = 2;
}

51
vendor/k8s.io/api/admission/v1beta1/register.go generated vendored Normal file
View File

@ -0,0 +1,51 @@
/*
Copyright 2017 The Kubernetes 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.
*/
package v1beta1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupName is the group name for this API.
const GroupName = "admission.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&AdmissionReview{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

162
vendor/k8s.io/api/admission/v1beta1/types.go generated vendored Normal file
View File

@ -0,0 +1,162 @@
/*
Copyright 2017 The Kubernetes 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.
*/
package v1beta1
import (
authenticationv1 "k8s.io/api/authentication/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// AdmissionReview describes an admission review request/response.
type AdmissionReview struct {
metav1.TypeMeta `json:",inline"`
// Request describes the attributes for the admission request.
// +optional
Request *AdmissionRequest `json:"request,omitempty" protobuf:"bytes,1,opt,name=request"`
// Response describes the attributes for the admission response.
// +optional
Response *AdmissionResponse `json:"response,omitempty" protobuf:"bytes,2,opt,name=response"`
}
// AdmissionRequest describes the admission.Attributes for the admission request.
type AdmissionRequest struct {
// UID is an identifier for the individual request/response. It allows us to distinguish instances of requests which are
// otherwise identical (parallel requests, requests when earlier requests did not modify etc)
// The UID is meant to track the round trip (request/response) between the KAS and the WebHook, not the user request.
// It is suitable for correlating log entries between the webhook and apiserver, for either auditing or debugging.
UID types.UID `json:"uid" protobuf:"bytes,1,opt,name=uid"`
// Kind is the fully-qualified type of object being submitted (for example, v1.Pod or autoscaling.v1.Scale)
Kind metav1.GroupVersionKind `json:"kind" protobuf:"bytes,2,opt,name=kind"`
// Resource is the fully-qualified resource being requested (for example, v1.pods)
Resource metav1.GroupVersionResource `json:"resource" protobuf:"bytes,3,opt,name=resource"`
// SubResource is the subresource being requested, if any (for example, "status" or "scale")
// +optional
SubResource string `json:"subResource,omitempty" protobuf:"bytes,4,opt,name=subResource"`
// RequestKind is the fully-qualified type of the original API request (for example, v1.Pod or autoscaling.v1.Scale).
// If this is specified and differs from the value in "kind", an equivalent match and conversion was performed.
//
// For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
// `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
// an API request to apps/v1beta1 deployments would be converted and sent to the webhook
// with `kind: {group:"apps", version:"v1", kind:"Deployment"}` (matching the rule the webhook registered for),
// and `requestKind: {group:"apps", version:"v1beta1", kind:"Deployment"}` (indicating the kind of the original API request).
//
// See documentation for the "matchPolicy" field in the webhook configuration type for more details.
// +optional
RequestKind *metav1.GroupVersionKind `json:"requestKind,omitempty" protobuf:"bytes,13,opt,name=requestKind"`
// RequestResource is the fully-qualified resource of the original API request (for example, v1.pods).
// If this is specified and differs from the value in "resource", an equivalent match and conversion was performed.
//
// For example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of
// `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]` and `matchPolicy: Equivalent`,
// an API request to apps/v1beta1 deployments would be converted and sent to the webhook
// with `resource: {group:"apps", version:"v1", resource:"deployments"}` (matching the resource the webhook registered for),
// and `requestResource: {group:"apps", version:"v1beta1", resource:"deployments"}` (indicating the resource of the original API request).
//
// See documentation for the "matchPolicy" field in the webhook configuration type.
// +optional
RequestResource *metav1.GroupVersionResource `json:"requestResource,omitempty" protobuf:"bytes,14,opt,name=requestResource"`
// RequestSubResource is the name of the subresource of the original API request, if any (for example, "status" or "scale")
// If this is specified and differs from the value in "subResource", an equivalent match and conversion was performed.
// See documentation for the "matchPolicy" field in the webhook configuration type.
// +optional
RequestSubResource string `json:"requestSubResource,omitempty" protobuf:"bytes,15,opt,name=requestSubResource"`
// Name is the name of the object as presented in the request. On a CREATE operation, the client may omit name and
// rely on the server to generate the name. If that is the case, this method will return the empty string.
// +optional
Name string `json:"name,omitempty" protobuf:"bytes,5,opt,name=name"`
// Namespace is the namespace associated with the request (if any).
// +optional
Namespace string `json:"namespace,omitempty" protobuf:"bytes,6,opt,name=namespace"`
// Operation is the operation being performed. This may be different than the operation
// requested. e.g. a patch can result in either a CREATE or UPDATE Operation.
Operation Operation `json:"operation" protobuf:"bytes,7,opt,name=operation"`
// UserInfo is information about the requesting user
UserInfo authenticationv1.UserInfo `json:"userInfo" protobuf:"bytes,8,opt,name=userInfo"`
// Object is the object from the incoming request prior to default values being applied
// +optional
Object runtime.RawExtension `json:"object,omitempty" protobuf:"bytes,9,opt,name=object"`
// OldObject is the existing object. Only populated for UPDATE requests.
// +optional
OldObject runtime.RawExtension `json:"oldObject,omitempty" protobuf:"bytes,10,opt,name=oldObject"`
// DryRun indicates that modifications will definitely not be persisted for this request.
// Defaults to false.
// +optional
DryRun *bool `json:"dryRun,omitempty" protobuf:"varint,11,opt,name=dryRun"`
// Options is the operation option structure of the operation being performed.
// e.g. `meta.k8s.io/v1.DeleteOptions` or `meta.k8s.io/v1.CreateOptions`. This may be
// different than the options the caller provided. e.g. for a patch request the performed
// Operation might be a CREATE, in which case the Options will a
// `meta.k8s.io/v1.CreateOptions` even though the caller provided `meta.k8s.io/v1.PatchOptions`.
// +optional
Options runtime.RawExtension `json:"options,omitempty" protobuf:"bytes,12,opt,name=options"`
}
// AdmissionResponse describes an admission response.
type AdmissionResponse struct {
// UID is an identifier for the individual request/response.
// This should be copied over from the corresponding AdmissionRequest.
UID types.UID `json:"uid" protobuf:"bytes,1,opt,name=uid"`
// Allowed indicates whether or not the admission request was permitted.
Allowed bool `json:"allowed" protobuf:"varint,2,opt,name=allowed"`
// Result contains extra details into why an admission request was denied.
// This field IS NOT consulted in any way if "Allowed" is "true".
// +optional
Result *metav1.Status `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
// The patch body. Currently we only support "JSONPatch" which implements RFC 6902.
// +optional
Patch []byte `json:"patch,omitempty" protobuf:"bytes,4,opt,name=patch"`
// The type of Patch. Currently we only allow "JSONPatch".
// +optional
PatchType *PatchType `json:"patchType,omitempty" protobuf:"bytes,5,opt,name=patchType"`
// AuditAnnotations is an unstructured key value map set by remote admission controller (e.g. error=image-blacklisted).
// MutatingAdmissionWebhook and ValidatingAdmissionWebhook admission controller will prefix the keys with
// admission webhook name (e.g. imagepolicy.example.com/error=image-blacklisted). AuditAnnotations will be provided by
// the admission webhook to add additional context to the audit log for this request.
// +optional
AuditAnnotations map[string]string `json:"auditAnnotations,omitempty" protobuf:"bytes,6,opt,name=auditAnnotations"`
}
// PatchType is the type of patch being used to represent the mutated object
type PatchType string
// PatchType constants.
const (
PatchTypeJSONPatch PatchType = "JSONPatch"
)
// Operation is the type of resource operation being checked for admission control
type Operation string
// Operation constants
const (
Create Operation = "CREATE"
Update Operation = "UPDATE"
Delete Operation = "DELETE"
Connect Operation = "CONNECT"
)

View File

@ -0,0 +1,77 @@
/*
Copyright The Kubernetes 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.
*/
package v1beta1
// This file contains a collection of methods that can be used from go-restful to
// generate Swagger API documentation for its models. Please read this PR for more
// information on the implementation: https://github.com/emicklei/go-restful/pull/215
//
// TODOs are ignored from the parser (e.g. TODO(andronat):... || TODO:...) if and only if
// they are on one line! For multiple line or blocks that you want to ignore use ---.
// Any context after a --- is ignored.
//
// Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
var map_AdmissionRequest = map[string]string{
"": "AdmissionRequest describes the admission.Attributes for the admission request.",
"uid": "UID is an identifier for the individual request/response. It allows us to distinguish instances of requests which are otherwise identical (parallel requests, requests when earlier requests did not modify etc) The UID is meant to track the round trip (request/response) between the KAS and the WebHook, not the user request. It is suitable for correlating log entries between the webhook and apiserver, for either auditing or debugging.",
"kind": "Kind is the fully-qualified type of object being submitted (for example, v1.Pod or autoscaling.v1.Scale)",
"resource": "Resource is the fully-qualified resource being requested (for example, v1.pods)",
"subResource": "SubResource is the subresource being requested, if any (for example, \"status\" or \"scale\")",
"requestKind": "RequestKind is the fully-qualified type of the original API request (for example, v1.Pod or autoscaling.v1.Scale). If this is specified and differs from the value in \"kind\", an equivalent match and conversion was performed.\n\nFor example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]` and `matchPolicy: Equivalent`, an API request to apps/v1beta1 deployments would be converted and sent to the webhook with `kind: {group:\"apps\", version:\"v1\", kind:\"Deployment\"}` (matching the rule the webhook registered for), and `requestKind: {group:\"apps\", version:\"v1beta1\", kind:\"Deployment\"}` (indicating the kind of the original API request).\n\nSee documentation for the \"matchPolicy\" field in the webhook configuration type for more details.",
"requestResource": "RequestResource is the fully-qualified resource of the original API request (for example, v1.pods). If this is specified and differs from the value in \"resource\", an equivalent match and conversion was performed.\n\nFor example, if deployments can be modified via apps/v1 and apps/v1beta1, and a webhook registered a rule of `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]` and `matchPolicy: Equivalent`, an API request to apps/v1beta1 deployments would be converted and sent to the webhook with `resource: {group:\"apps\", version:\"v1\", resource:\"deployments\"}` (matching the resource the webhook registered for), and `requestResource: {group:\"apps\", version:\"v1beta1\", resource:\"deployments\"}` (indicating the resource of the original API request).\n\nSee documentation for the \"matchPolicy\" field in the webhook configuration type.",
"requestSubResource": "RequestSubResource is the name of the subresource of the original API request, if any (for example, \"status\" or \"scale\") If this is specified and differs from the value in \"subResource\", an equivalent match and conversion was performed. See documentation for the \"matchPolicy\" field in the webhook configuration type.",
"name": "Name is the name of the object as presented in the request. On a CREATE operation, the client may omit name and rely on the server to generate the name. If that is the case, this method will return the empty string.",
"namespace": "Namespace is the namespace associated with the request (if any).",
"operation": "Operation is the operation being performed. This may be different than the operation requested. e.g. a patch can result in either a CREATE or UPDATE Operation.",
"userInfo": "UserInfo is information about the requesting user",
"object": "Object is the object from the incoming request prior to default values being applied",
"oldObject": "OldObject is the existing object. Only populated for UPDATE requests.",
"dryRun": "DryRun indicates that modifications will definitely not be persisted for this request. Defaults to false.",
"options": "Options is the operation option structure of the operation being performed. e.g. `meta.k8s.io/v1.DeleteOptions` or `meta.k8s.io/v1.CreateOptions`. This may be different than the options the caller provided. e.g. for a patch request the performed Operation might be a CREATE, in which case the Options will a `meta.k8s.io/v1.CreateOptions` even though the caller provided `meta.k8s.io/v1.PatchOptions`.",
}
func (AdmissionRequest) SwaggerDoc() map[string]string {
return map_AdmissionRequest
}
var map_AdmissionResponse = map[string]string{
"": "AdmissionResponse describes an admission response.",
"uid": "UID is an identifier for the individual request/response. This should be copied over from the corresponding AdmissionRequest.",
"allowed": "Allowed indicates whether or not the admission request was permitted.",
"status": "Result contains extra details into why an admission request was denied. This field IS NOT consulted in any way if \"Allowed\" is \"true\".",
"patch": "The patch body. Currently we only support \"JSONPatch\" which implements RFC 6902.",
"patchType": "The type of Patch. Currently we only allow \"JSONPatch\".",
"auditAnnotations": "AuditAnnotations is an unstructured key value map set by remote admission controller (e.g. error=image-blacklisted). MutatingAdmissionWebhook and ValidatingAdmissionWebhook admission controller will prefix the keys with admission webhook name (e.g. imagepolicy.example.com/error=image-blacklisted). AuditAnnotations will be provided by the admission webhook to add additional context to the audit log for this request.",
}
func (AdmissionResponse) SwaggerDoc() map[string]string {
return map_AdmissionResponse
}
var map_AdmissionReview = map[string]string{
"": "AdmissionReview describes an admission review request/response.",
"request": "Request describes the attributes for the admission request.",
"response": "Response describes the attributes for the admission response.",
}
func (AdmissionReview) SwaggerDoc() map[string]string {
return map_AdmissionReview
}
// AUTO-GENERATED FUNCTIONS END HERE

View File

@ -0,0 +1,136 @@
// +build !ignore_autogenerated
/*
Copyright The Kubernetes 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.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1beta1
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AdmissionRequest) DeepCopyInto(out *AdmissionRequest) {
*out = *in
out.Kind = in.Kind
out.Resource = in.Resource
if in.RequestKind != nil {
in, out := &in.RequestKind, &out.RequestKind
*out = new(v1.GroupVersionKind)
**out = **in
}
if in.RequestResource != nil {
in, out := &in.RequestResource, &out.RequestResource
*out = new(v1.GroupVersionResource)
**out = **in
}
in.UserInfo.DeepCopyInto(&out.UserInfo)
in.Object.DeepCopyInto(&out.Object)
in.OldObject.DeepCopyInto(&out.OldObject)
if in.DryRun != nil {
in, out := &in.DryRun, &out.DryRun
*out = new(bool)
**out = **in
}
in.Options.DeepCopyInto(&out.Options)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdmissionRequest.
func (in *AdmissionRequest) DeepCopy() *AdmissionRequest {
if in == nil {
return nil
}
out := new(AdmissionRequest)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AdmissionResponse) DeepCopyInto(out *AdmissionResponse) {
*out = *in
if in.Result != nil {
in, out := &in.Result, &out.Result
*out = new(v1.Status)
(*in).DeepCopyInto(*out)
}
if in.Patch != nil {
in, out := &in.Patch, &out.Patch
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.PatchType != nil {
in, out := &in.PatchType, &out.PatchType
*out = new(PatchType)
**out = **in
}
if in.AuditAnnotations != nil {
in, out := &in.AuditAnnotations, &out.AuditAnnotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdmissionResponse.
func (in *AdmissionResponse) DeepCopy() *AdmissionResponse {
if in == nil {
return nil
}
out := new(AdmissionResponse)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AdmissionReview) DeepCopyInto(out *AdmissionReview) {
*out = *in
out.TypeMeta = in.TypeMeta
if in.Request != nil {
in, out := &in.Request, &out.Request
*out = new(AdmissionRequest)
(*in).DeepCopyInto(*out)
}
if in.Response != nil {
in, out := &in.Response, &out.Response
*out = new(AdmissionResponse)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdmissionReview.
func (in *AdmissionReview) DeepCopy() *AdmissionReview {
if in == nil {
return nil
}
out := new(AdmissionReview)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *AdmissionReview) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

9
vendor/knative.dev/eventing/AUTHORS vendored Normal file
View File

@ -0,0 +1,9 @@
# This is the list of Knative authors for copyright purposes.
#
# This does not necessarily list everyone who has contributed code, since in
# some cases, their employer may be the copyright holder. To see the full list
# of contributors, see the revision history in source control.
Google LLC
Pivotal Software, Inc.
Red Hat, Inc.
IBM Corp

201
vendor/knative.dev/eventing/LICENSE vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -0,0 +1,33 @@
/*
Copyright 2019 The Knative 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.
*/
package duck
import (
appsv1 "k8s.io/api/apps/v1"
)
// DeploymentIsAvailable determines if the provided deployment is available. Note that if it cannot
// determine the Deployment's availability, it returns `def` (short for default).
func DeploymentIsAvailable(d *appsv1.DeploymentStatus, def bool) bool {
// Check if the Deployment is available.
for _, cond := range d.Conditions {
if cond.Type == appsv1.DeploymentAvailable {
return cond.Status == "True"
}
}
return def
}

View File

@ -0,0 +1,37 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
// TODO This should be placed in channel_defaults.go within messaging once Subscription is moved to messaging.
// Context: there is a cyclic dependency between eventing and messaging if we place this in messaging. Broker needs to
// depend on this, which is fine. But the problem arises due to messaging depending on eventing, mainly on
// Subscription-related objects for the Sequence type. We should first move Subscription down to messaging and then we
// can move this down. See https://github.com/knative/eventing/issues/1562.
//
// ChannelDefaulter sets the default Channel CRD and Arguments on Channels that do not
// specify any implementation.
type ChannelDefaulter interface {
// GetDefault determines the default Channel CRD for the given namespace.
GetDefault(namespace string) *ChannelTemplateSpec
}
var (
// ChannelDefaulterSingleton is the global singleton used to default Channels that do not
// specify a Channel CRD.
ChannelDefaulterSingleton ChannelDefaulter
)

View File

@ -0,0 +1,47 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ChannelTemplateSpec struct {
metav1.TypeMeta `json:",inline"`
// Spec defines the Spec to use for each channel created. Passed
// in verbatim to the Channel CRD as Spec section.
// +optional
Spec *runtime.RawExtension `json:"spec,omitempty"`
}
// ChannelTemplateSpecInternal is an internal only version that includes ObjectMeta so that
// we can easily create new Channels off of it.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ChannelTemplateSpecInternal struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec defines the Spec to use for each channel created. Passed
// in verbatim to the Channel CRD as Spec section.
// +optional
Spec *runtime.RawExtension `json:"spec,omitempty"`
}

View File

@ -0,0 +1,167 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"knative.dev/pkg/apis"
"knative.dev/pkg/apis/duck"
duckv1 "knative.dev/pkg/apis/duck/v1"
"knative.dev/pkg/apis/duck/v1alpha1"
duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1"
apisv1alpha1 "knative.dev/pkg/apis/v1alpha1"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Channelable is a skeleton type wrapping Subscribable and Addressable in the manner we expect resource writers
// defining compatible resources to embed it. We will typically use this type to deserialize
// Channelable ObjectReferences and access their subscription and address data. This is not a real resource.
type Channelable struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec is the part where the Channelable fulfills the Subscribable contract.
Spec ChannelableSpec `json:"spec,omitempty"`
Status ChannelableStatus `json:"status,omitempty"`
}
// ChannelableSpec contains Spec of the Channelable object
type ChannelableSpec struct {
SubscribableTypeSpec `json:",inline"`
// DeliverySpec contains options controlling the event delivery
// +optional
Delivery *DeliverySpec `json:"delivery,omitempty"`
}
// ChannelableStatus contains the Status of a Channelable object.
type ChannelableStatus struct {
// inherits duck/v1 Status, which currently provides:
// * ObservedGeneration - the 'Generation' of the Service that was last processed by the controller.
// * Conditions - the latest available observations of a resource's current state.
duckv1.Status `json:",inline"`
// AddressStatus is the part where the Channelable fulfills the Addressable contract.
v1alpha1.AddressStatus `json:",inline"`
// Subscribers is populated with the statuses of each of the Channelable's subscribers.
SubscribableTypeStatus `json:",inline"`
// ErrorChannel is set by the channel when it supports native error handling via a channel
// +optional
ErrorChannel *corev1.ObjectReference `json:"errorChannel,omitempty"`
}
var (
// Verify Channelable resources meet duck contracts.
_ duck.Populatable = (*Channelable)(nil)
_ apis.Listable = (*Channelable)(nil)
)
// Populate implements duck.Populatable
func (c *Channelable) Populate() {
c.Spec.Subscribable = &Subscribable{
// Populate ALL fields
Subscribers: []SubscriberSpec{{
UID: "2f9b5e8e-deb6-11e8-9f32-f2801f1b9fd1",
Generation: 1,
SubscriberURI: "call1",
ReplyURI: "sink2",
}, {
UID: "34c5aec8-deb6-11e8-9f32-f2801f1b9fd1",
Generation: 2,
SubscriberURI: "call2",
ReplyURI: "sink2",
}},
}
retry := int32(5)
linear := BackoffPolicyLinear
delay := "5s"
c.Spec.Delivery = &DeliverySpec{
DeadLetterSink: &apisv1alpha1.Destination{
Ref: &corev1.ObjectReference{
Name: "aname",
},
URI: &apis.URL{
Scheme: "http",
Host: "test-error-domain",
},
},
Retry: &retry,
BackoffPolicy: &linear,
BackoffDelay: &delay,
}
c.Status = ChannelableStatus{
AddressStatus: v1alpha1.AddressStatus{
Address: &v1alpha1.Addressable{
// Populate ALL fields
Addressable: duckv1beta1.Addressable{
URL: &apis.URL{
Scheme: "http",
Host: "test-domain",
},
},
Hostname: "test-domain",
},
},
SubscribableTypeStatus: SubscribableTypeStatus{
DeprecatedSubscribableStatus: &SubscribableStatus{
Subscribers: []SubscriberStatus{{
UID: "2f9b5e8e-deb6-11e8-9f32-f2801f1b9fd1",
ObservedGeneration: 1,
Ready: corev1.ConditionTrue,
Message: "Some message",
}, {
UID: "34c5aec8-deb6-11e8-9f32-f2801f1b9fd1",
ObservedGeneration: 2,
Ready: corev1.ConditionFalse,
Message: "Some message",
}},
},
SubscribableStatus: &SubscribableStatus{
Subscribers: []SubscriberStatus{{
UID: "2f9b5e8e-deb6-11e8-9f32-f2801f1b9fd1",
ObservedGeneration: 1,
Ready: corev1.ConditionTrue,
Message: "Some message",
}, {
UID: "34c5aec8-deb6-11e8-9f32-f2801f1b9fd1",
ObservedGeneration: 2,
Ready: corev1.ConditionFalse,
Message: "Some message",
}},
},
},
}
}
// GetListType implements apis.Listable
func (c *Channelable) GetListType() runtime.Object {
return &ChannelableList{}
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ChannelableList is a list of Channelable resources.
type ChannelableList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Channelable `json:"items"`
}

View File

@ -0,0 +1,67 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
apisv1alpha1 "knative.dev/pkg/apis/v1alpha1"
)
// DeliverySpec contains the delivery options for event senders,
// such as channelable and source.
type DeliverySpec struct {
// DeadLetterSink is the sink receiving event that couldn't be sent to
// a destination.
// +optional
DeadLetterSink *apisv1alpha1.Destination `json:"deadLetterSink,omitempty"`
// Retry is the minimum number of retries the sender should attempt when
// sending an event before moving it to the dead letter sink.
// +optional
Retry *int32 `json:"retry,omitempty"`
// BackoffPolicy is the retry backoff policy (linear, exponential)
// +optional
BackoffPolicy *BackoffPolicyType `json:"backoffPolicy,omitempty"`
// BackoffDelay is the delay before retrying.
// More information on Duration format: https://www.ietf.org/rfc/rfc3339.txt
//
// For linear policy, backoff delay is the time interval between retries.
// For exponential policy , backoff delay is backoffDelay*2^<numberOfRetries>
// +optional
BackoffDelay *string
}
// BackoffPolicyType is the type for backoff policies
type BackoffPolicyType string
const (
// Linear backoff policy
BackoffPolicyLinear BackoffPolicyType = "linear"
// Exponential backoff policy
BackoffPolicyExponential BackoffPolicyType = "exponential"
)
// DeliveryStatus contains the Status of an object supporting delivery options.
type DeliveryStatus struct {
// DeadLetterChannel is the reference to the native, platform specific channel
// where failed events are sent to.
// +optional
DeadLetterChannel *corev1.ObjectReference `json:"deadLetterChannel,omitempty"`
}

View File

@ -0,0 +1,24 @@
/*
Copyright 2019 The Knative 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.
*/
// Api versions allow the api contract for a resource to be changed while keeping
// backward compatibility by support multiple concurrent versions
// of the same resource
// Package v1alpha1 is the v1alpha1 version of the API.
// +k8s:deepcopy-gen=package
// +groupName=duck.knative.dev
package v1alpha1

View File

@ -0,0 +1,51 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"knative.dev/pkg/apis"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Resource is a skeleton type wrapping all Kubernetes resources. It is typically used to watch
// arbitrary other resources (such as any Source or Addressable). This is not a real resource.
type Resource struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
}
// Ensure Resource satisfies apis.Listable
var _ apis.Listable = (*Resource)(nil)
// GetListType implements apis.Listable.
func (*Resource) GetListType() runtime.Object {
return &ResourceList{}
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ResourceList is a list of KResource resources
type ResourceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Resource `json:"items"`
}

View File

@ -0,0 +1,201 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"knative.dev/pkg/apis"
"knative.dev/pkg/apis/duck"
)
// Subscribable is the schema for the subscribable portion of the spec
// section of the resource.
type Subscribable struct {
// This is the list of subscriptions for this subscribable.
// +patchMergeKey=uid
// +patchStrategy=merge
Subscribers []SubscriberSpec `json:"subscribers,omitempty" patchStrategy:"merge" patchMergeKey:"uid"`
}
// Subscribable is an Implementable "duck type".
var _ duck.Implementable = (*Subscribable)(nil)
// SubscriberSpec defines a single subscriber to a Subscribable.
// Ref is a reference to the Subscription this SubscriberSpec was created for
// SubscriberURI is the endpoint for the subscriber
// ReplyURI is the endpoint for the reply
// At least one of SubscriberURI and ReplyURI must be present
type SubscriberSpec struct {
// Deprecated: use UID.
// +optional
DeprecatedRef *corev1.ObjectReference `json:"ref,omitempty" yaml:"ref,omitempty"`
// UID is used to understand the origin of the subscriber.
// +optional
UID types.UID `json:"uid,omitempty"`
// Generation of the origin of the subscriber with uid:UID.
// +optional
Generation int64 `json:"generation,omitempty"`
// +optional
SubscriberURI string `json:"subscriberURI,omitempty"`
// +optional
ReplyURI string `json:"replyURI,omitempty"`
}
// SubscribableStatus is the schema for the subscribable's status portion of the status
// section of the resource.
type SubscribableStatus struct {
// This is the list of subscription's statuses for this channel.
// +patchMergeKey=uid
// +patchStrategy=merge
Subscribers []SubscriberStatus `json:"subscribers,omitempty" patchStrategy:"merge" patchMergeKey:"uid"`
}
// SubscriberStatus defines the status of a single subscriber to a Channel.
type SubscriberStatus struct {
// UID is used to understand the origin of the subscriber.
// +optional
UID types.UID `json:"uid,omitempty"`
// Generation of the origin of the subscriber with uid:UID.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// Status of the subscriber.
// +optional
Ready corev1.ConditionStatus `json:"ready,omitempty"`
// A human readable message indicating details of Ready status.
// +optional
Message string `json:"message,omitempty"`
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// SubscribableType is a skeleton type wrapping Subscribable in the manner we expect resource writers
// defining compatible resources to embed it. We will typically use this type to deserialize
// SubscribableType ObjectReferences and access the Subscription data. This is not a real resource.
type SubscribableType struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// SubscribableTypeSpec is the part where Subscribable object is
// configured as to be compatible with Subscribable contract.
Spec SubscribableTypeSpec `json:"spec"`
// SubscribableTypeStatus is the part where SubscribableStatus object is
// configured as to be compatible with Subscribable contract.
Status SubscribableTypeStatus `json:"status"`
}
// SubscribableTypeSpec shows how we expect folks to embed Subscribable in their Spec field.
type SubscribableTypeSpec struct {
Subscribable *Subscribable `json:"subscribable,omitempty"`
}
// SubscribableTypeStatus shows how we expect folks to embed Subscribable in their Status field.
type SubscribableTypeStatus struct {
DeprecatedSubscribableStatus *SubscribableStatus `json:"subscribablestatus,omitempty"`
SubscribableStatus *SubscribableStatus `json:"subscribableStatus,omitempty"`
}
var (
// Verify SubscribableType resources meet duck contracts.
_ duck.Populatable = (*SubscribableType)(nil)
_ apis.Listable = (*SubscribableType)(nil)
)
// GetSubscribableTypeStatus method Returns the Default SubscribableStatus in this case it's SubscribableStatus
// This is w.r.t https://github.com/knative/eventing/pull/1685#discussion_r314797276
// Due to change in the API, we support reading of SubscribableTypeStatus#DeprecatedSubscribableStatus in a logical way
// where we read the V2 value first and if the value is absent then we read the V1 value,
// Having this function here makes it convinient to read the default value at runtime.
func (s *SubscribableTypeStatus) GetSubscribableTypeStatus() *SubscribableStatus {
if s.SubscribableStatus == nil {
return s.DeprecatedSubscribableStatus
}
return s.SubscribableStatus
}
// SetSubscribableTypeStatus method sets the SubscribableStatus Values in th SubscribableTypeStatus structs
// This helper function ensures that we set both the values (SubscribableStatus and DeprecatedSubscribableStatus)
func (s *SubscribableTypeStatus) SetSubscribableTypeStatus(subscriberStatus SubscribableStatus) {
s.SubscribableStatus = &subscriberStatus
s.DeprecatedSubscribableStatus = &subscriberStatus
}
// AddSubscriberToSubscribableStatus method is a Helper method for type SubscribableTypeStatus, if Subscribable Status needs to be appended
// with Subscribers, use this function, so that the value is reflected in both the duplicate fields residing
// in SubscribableTypeStatus
func (s *SubscribableTypeStatus) AddSubscriberToSubscribableStatus(subscriberStatus SubscriberStatus) {
subscribers := append(s.GetSubscribableTypeStatus().Subscribers, subscriberStatus)
s.SubscribableStatus.Subscribers = subscribers
s.DeprecatedSubscribableStatus.Subscribers = subscribers
}
// GetFullType implements duck.Implementable
func (s *Subscribable) GetFullType() duck.Populatable {
return &SubscribableType{}
}
// Populate implements duck.Populatable
func (c *SubscribableType) Populate() {
c.Spec.Subscribable = &Subscribable{
// Populate ALL fields
Subscribers: []SubscriberSpec{{
UID: "2f9b5e8e-deb6-11e8-9f32-f2801f1b9fd1",
Generation: 1,
SubscriberURI: "call1",
ReplyURI: "sink2",
}, {
UID: "34c5aec8-deb6-11e8-9f32-f2801f1b9fd1",
Generation: 2,
SubscriberURI: "call2",
ReplyURI: "sink2",
}},
}
c.Status.SetSubscribableTypeStatus(SubscribableStatus{
// Populate ALL fields
Subscribers: []SubscriberStatus{{
UID: "2f9b5e8e-deb6-11e8-9f32-f2801f1b9fd1",
ObservedGeneration: 1,
Ready: corev1.ConditionTrue,
Message: "Some message",
}, {
UID: "34c5aec8-deb6-11e8-9f32-f2801f1b9fd1",
ObservedGeneration: 2,
Ready: corev1.ConditionFalse,
Message: "Some message",
}},
})
}
// GetListType implements apis.Listable
func (c *SubscribableType) GetListType() runtime.Object {
return &SubscribableTypeList{}
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// SubscribableTypeList is a list of SubscribableType resources
type SubscribableTypeList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []SubscribableType `json:"items"`
}

View File

@ -0,0 +1,500 @@
// +build !ignore_autogenerated
/*
Copyright 2019 The Knative 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.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1alpha1
import (
v1 "k8s.io/api/core/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
apisv1alpha1 "knative.dev/pkg/apis/v1alpha1"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ChannelTemplateSpec) DeepCopyInto(out *ChannelTemplateSpec) {
*out = *in
out.TypeMeta = in.TypeMeta
if in.Spec != nil {
in, out := &in.Spec, &out.Spec
*out = new(runtime.RawExtension)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelTemplateSpec.
func (in *ChannelTemplateSpec) DeepCopy() *ChannelTemplateSpec {
if in == nil {
return nil
}
out := new(ChannelTemplateSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ChannelTemplateSpec) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ChannelTemplateSpecInternal) DeepCopyInto(out *ChannelTemplateSpecInternal) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
if in.Spec != nil {
in, out := &in.Spec, &out.Spec
*out = new(runtime.RawExtension)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelTemplateSpecInternal.
func (in *ChannelTemplateSpecInternal) DeepCopy() *ChannelTemplateSpecInternal {
if in == nil {
return nil
}
out := new(ChannelTemplateSpecInternal)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ChannelTemplateSpecInternal) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Channelable) DeepCopyInto(out *Channelable) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Channelable.
func (in *Channelable) DeepCopy() *Channelable {
if in == nil {
return nil
}
out := new(Channelable)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Channelable) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ChannelableList) DeepCopyInto(out *ChannelableList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Channelable, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelableList.
func (in *ChannelableList) DeepCopy() *ChannelableList {
if in == nil {
return nil
}
out := new(ChannelableList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ChannelableList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ChannelableSpec) DeepCopyInto(out *ChannelableSpec) {
*out = *in
in.SubscribableTypeSpec.DeepCopyInto(&out.SubscribableTypeSpec)
if in.Delivery != nil {
in, out := &in.Delivery, &out.Delivery
*out = new(DeliverySpec)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelableSpec.
func (in *ChannelableSpec) DeepCopy() *ChannelableSpec {
if in == nil {
return nil
}
out := new(ChannelableSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ChannelableStatus) DeepCopyInto(out *ChannelableStatus) {
*out = *in
in.Status.DeepCopyInto(&out.Status)
in.AddressStatus.DeepCopyInto(&out.AddressStatus)
in.SubscribableTypeStatus.DeepCopyInto(&out.SubscribableTypeStatus)
if in.ErrorChannel != nil {
in, out := &in.ErrorChannel, &out.ErrorChannel
*out = new(v1.ObjectReference)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelableStatus.
func (in *ChannelableStatus) DeepCopy() *ChannelableStatus {
if in == nil {
return nil
}
out := new(ChannelableStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeliverySpec) DeepCopyInto(out *DeliverySpec) {
*out = *in
if in.DeadLetterSink != nil {
in, out := &in.DeadLetterSink, &out.DeadLetterSink
*out = new(apisv1alpha1.Destination)
(*in).DeepCopyInto(*out)
}
if in.Retry != nil {
in, out := &in.Retry, &out.Retry
*out = new(int32)
**out = **in
}
if in.BackoffPolicy != nil {
in, out := &in.BackoffPolicy, &out.BackoffPolicy
*out = new(BackoffPolicyType)
**out = **in
}
if in.BackoffDelay != nil {
in, out := &in.BackoffDelay, &out.BackoffDelay
*out = new(string)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeliverySpec.
func (in *DeliverySpec) DeepCopy() *DeliverySpec {
if in == nil {
return nil
}
out := new(DeliverySpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeliveryStatus) DeepCopyInto(out *DeliveryStatus) {
*out = *in
if in.DeadLetterChannel != nil {
in, out := &in.DeadLetterChannel, &out.DeadLetterChannel
*out = new(v1.ObjectReference)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeliveryStatus.
func (in *DeliveryStatus) DeepCopy() *DeliveryStatus {
if in == nil {
return nil
}
out := new(DeliveryStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Resource) DeepCopyInto(out *Resource) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resource.
func (in *Resource) DeepCopy() *Resource {
if in == nil {
return nil
}
out := new(Resource)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Resource) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceList) DeepCopyInto(out *ResourceList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Resource, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceList.
func (in *ResourceList) DeepCopy() *ResourceList {
if in == nil {
return nil
}
out := new(ResourceList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ResourceList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Subscribable) DeepCopyInto(out *Subscribable) {
*out = *in
if in.Subscribers != nil {
in, out := &in.Subscribers, &out.Subscribers
*out = make([]SubscriberSpec, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subscribable.
func (in *Subscribable) DeepCopy() *Subscribable {
if in == nil {
return nil
}
out := new(Subscribable)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SubscribableStatus) DeepCopyInto(out *SubscribableStatus) {
*out = *in
if in.Subscribers != nil {
in, out := &in.Subscribers, &out.Subscribers
*out = make([]SubscriberStatus, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscribableStatus.
func (in *SubscribableStatus) DeepCopy() *SubscribableStatus {
if in == nil {
return nil
}
out := new(SubscribableStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SubscribableType) DeepCopyInto(out *SubscribableType) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscribableType.
func (in *SubscribableType) DeepCopy() *SubscribableType {
if in == nil {
return nil
}
out := new(SubscribableType)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *SubscribableType) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SubscribableTypeList) DeepCopyInto(out *SubscribableTypeList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]SubscribableType, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscribableTypeList.
func (in *SubscribableTypeList) DeepCopy() *SubscribableTypeList {
if in == nil {
return nil
}
out := new(SubscribableTypeList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *SubscribableTypeList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SubscribableTypeSpec) DeepCopyInto(out *SubscribableTypeSpec) {
*out = *in
if in.Subscribable != nil {
in, out := &in.Subscribable, &out.Subscribable
*out = new(Subscribable)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscribableTypeSpec.
func (in *SubscribableTypeSpec) DeepCopy() *SubscribableTypeSpec {
if in == nil {
return nil
}
out := new(SubscribableTypeSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SubscribableTypeStatus) DeepCopyInto(out *SubscribableTypeStatus) {
*out = *in
if in.DeprecatedSubscribableStatus != nil {
in, out := &in.DeprecatedSubscribableStatus, &out.DeprecatedSubscribableStatus
*out = new(SubscribableStatus)
(*in).DeepCopyInto(*out)
}
if in.SubscribableStatus != nil {
in, out := &in.SubscribableStatus, &out.SubscribableStatus
*out = new(SubscribableStatus)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscribableTypeStatus.
func (in *SubscribableTypeStatus) DeepCopy() *SubscribableTypeStatus {
if in == nil {
return nil
}
out := new(SubscribableTypeStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SubscriberSpec) DeepCopyInto(out *SubscriberSpec) {
*out = *in
if in.DeprecatedRef != nil {
in, out := &in.DeprecatedRef, &out.DeprecatedRef
*out = new(v1.ObjectReference)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscriberSpec.
func (in *SubscriberSpec) DeepCopy() *SubscriberSpec {
if in == nil {
return nil
}
out := new(SubscriberSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SubscriberStatus) DeepCopyInto(out *SubscriberStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscriberStatus.
func (in *SubscriberStatus) DeepCopy() *SubscriberStatus {
if in == nil {
return nil
}
out := new(SubscriberStatus)
in.DeepCopyInto(out)
return out
}

View File

@ -0,0 +1,21 @@
/*
Copyright 2018 The Knative 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.
*/
package eventing
const (
GroupName = "eventing.knative.dev"
)

View File

@ -0,0 +1,40 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"context"
eventingduckv1alpha1 "knative.dev/eventing/pkg/apis/duck/v1alpha1"
)
func (b *Broker) SetDefaults(ctx context.Context) {
// If we haven't configured the new channelTemplate,
// then set the default channel to the new channelTemplate.
if b != nil && b.Spec.ChannelTemplate == nil {
// The singleton may not have been set, if so ignore it and validation will reject the Broker.
if cd := eventingduckv1alpha1.ChannelDefaulterSingleton; cd != nil {
channelTemplate := cd.GetDefault(b.Namespace)
b.Spec.ChannelTemplate = channelTemplate
}
}
b.Spec.SetDefaults(ctx)
}
func (bs *BrokerSpec) SetDefaults(ctx context.Context) {
// None
}

View File

@ -0,0 +1,155 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
appsv1 "k8s.io/api/apps/v1"
"knative.dev/pkg/apis"
"knative.dev/eventing/pkg/apis/duck"
duckv1alpha1 "knative.dev/eventing/pkg/apis/duck/v1alpha1"
messagingv1alpha1 "knative.dev/eventing/pkg/apis/messaging/v1alpha1"
)
var brokerCondSet = apis.NewLivingConditionSet(
BrokerConditionIngress,
BrokerConditionTriggerChannel,
BrokerConditionIngressChannel,
BrokerConditionFilter,
BrokerConditionAddressable,
BrokerConditionIngressSubscription,
)
const (
BrokerConditionReady = apis.ConditionReady
BrokerConditionIngress apis.ConditionType = "IngressReady"
BrokerConditionTriggerChannel apis.ConditionType = "TriggerChannelReady"
BrokerConditionIngressChannel apis.ConditionType = "IngressChannelReady"
BrokerConditionIngressSubscription apis.ConditionType = "IngressSubscriptionReady"
BrokerConditionFilter apis.ConditionType = "FilterReady"
BrokerConditionAddressable apis.ConditionType = "Addressable"
)
// GetCondition returns the condition currently associated with the given type, or nil.
func (bs *BrokerStatus) GetCondition(t apis.ConditionType) *apis.Condition {
return brokerCondSet.Manage(bs).GetCondition(t)
}
// IsReady returns true if the resource is ready overall.
func (bs *BrokerStatus) IsReady() bool {
return brokerCondSet.Manage(bs).IsHappy()
}
// InitializeConditions sets relevant unset conditions to Unknown state.
func (bs *BrokerStatus) InitializeConditions() {
brokerCondSet.Manage(bs).InitializeConditions()
}
func (bs *BrokerStatus) MarkIngressFailed(reason, format string, args ...interface{}) {
brokerCondSet.Manage(bs).MarkFalse(BrokerConditionIngress, reason, format, args...)
}
func (bs *BrokerStatus) PropagateIngressDeploymentAvailability(d *appsv1.Deployment) {
if duck.DeploymentIsAvailable(&d.Status, true) {
brokerCondSet.Manage(bs).MarkTrue(BrokerConditionIngress)
} else {
// I don't know how to propagate the status well, so just give the name of the Deployment
// for now.
bs.MarkIngressFailed("DeploymentUnavailable", "The Deployment '%s' is unavailable.", d.Name)
}
}
func (bs *BrokerStatus) MarkTriggerChannelFailed(reason, format string, args ...interface{}) {
brokerCondSet.Manage(bs).MarkFalse(BrokerConditionTriggerChannel, reason, format, args...)
}
func (bs *BrokerStatus) PropagateTriggerChannelReadiness(cs *duckv1alpha1.ChannelableStatus) {
// TODO: Once you can get a Ready status from Channelable in a generic way, use it here...
address := cs.AddressStatus.Address
if address != nil {
brokerCondSet.Manage(bs).MarkTrue(BrokerConditionTriggerChannel)
} else {
bs.MarkTriggerChannelFailed("ChannelNotReady", "trigger Channel is not ready: not addressalbe")
}
}
func (bs *BrokerStatus) MarkIngressChannelFailed(reason, format string, args ...interface{}) {
brokerCondSet.Manage(bs).MarkFalse(BrokerConditionIngressChannel, reason, format, args...)
}
func (bs *BrokerStatus) PropagateIngressChannelReadiness(cs *duckv1alpha1.ChannelableStatus) {
// TODO: Once you can get a Ready status from Channelable in a generic way, use it here...
address := cs.AddressStatus.Address
if address != nil {
brokerCondSet.Manage(bs).MarkTrue(BrokerConditionIngressChannel)
} else {
bs.MarkIngressChannelFailed("ChannelNotReady", "ingress Channel is not ready: not addressable")
}
}
func (bs *BrokerStatus) MarkIngressSubscriptionFailed(reason, format string, args ...interface{}) {
brokerCondSet.Manage(bs).MarkFalse(BrokerConditionIngressSubscription, reason, format, args...)
}
func (bs *BrokerStatus) MarkIngressSubscriptionNotOwned(sub *messagingv1alpha1.Subscription) {
bs.MarkIngressSubscriptionFailed("SubscriptionNotOwned", "Subscription %q is not owned by this Broker.", sub.Name)
}
func (bs *BrokerStatus) PropagateIngressSubscriptionReadiness(ss *messagingv1alpha1.SubscriptionStatus) {
if ss.IsReady() {
brokerCondSet.Manage(bs).MarkTrue(BrokerConditionIngressSubscription)
} else {
msg := "nil"
if sc := ss.GetCondition(messagingv1alpha1.SubscriptionConditionReady); sc != nil {
msg = sc.Message
}
bs.MarkIngressSubscriptionFailed("SubscriptionNotReady", "ingress Subscription is not ready: %s", msg)
}
}
func (bs *BrokerStatus) MarkFilterFailed(reason, format string, args ...interface{}) {
brokerCondSet.Manage(bs).MarkFalse(BrokerConditionFilter, reason, format, args...)
}
func (bs *BrokerStatus) PropagateFilterDeploymentAvailability(d *appsv1.Deployment) {
if duck.DeploymentIsAvailable(&d.Status, true) {
brokerCondSet.Manage(bs).MarkTrue(BrokerConditionFilter)
} else {
// I don't know how to propagate the status well, so just give the name of the Deployment
// for now.
bs.MarkFilterFailed("DeploymentUnavailable", "The Deployment '%s' is unavailable.", d.Name)
}
}
// SetAddress makes this Broker addressable by setting the hostname. It also
// sets the BrokerConditionAddressable to true.
func (bs *BrokerStatus) SetAddress(url *apis.URL) {
if url != nil {
bs.Address.Hostname = url.Host
bs.Address.URL = url
brokerCondSet.Manage(bs).MarkTrue(BrokerConditionAddressable)
} else {
bs.Address.Hostname = ""
bs.Address.URL = nil
brokerCondSet.Manage(bs).MarkFalse(BrokerConditionAddressable, "emptyHostname", "hostname is the empty string")
}
}

View File

@ -0,0 +1,117 @@
/*
* Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
eventingduckv1alpha1 "knative.dev/eventing/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
duckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/kmeta"
"knative.dev/pkg/webhook"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Broker collects a pool of events that are consumable using Triggers. Brokers
// provide a well-known endpoint for event delivery that senders can use with
// minimal knowledge of the event routing strategy. Receivers use Triggers to
// request delivery of events from a Broker's pool to a specific URL or
// Addressable endpoint.
type Broker struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec defines the desired state of the Broker.
Spec BrokerSpec `json:"spec,omitempty"`
// Status represents the current state of the Broker. This data may be out of
// date.
// +optional
Status BrokerStatus `json:"status,omitempty"`
}
var (
// Check that Broker can be validated, can be defaulted, and has immutable fields.
_ apis.Validatable = (*Broker)(nil)
_ apis.Defaultable = (*Broker)(nil)
_ apis.Immutable = (*Broker)(nil)
// Check that Broker can return its spec untyped.
_ apis.HasSpec = (*Broker)(nil)
_ runtime.Object = (*Broker)(nil)
_ webhook.GenericCRD = (*Broker)(nil)
// Check that we can create OwnerReferences to a Broker.
_ kmeta.OwnerRefable = (*Broker)(nil)
)
type BrokerSpec struct {
// ChannelTemplate specifies which Channel CRD to use to create all the Channels used internally by the
// Broker. If left unspecified, it is set to the default Channel CRD for the namespace (or cluster, in case there
// are no defaults for the namespace).
// +optional
ChannelTemplate *eventingduckv1alpha1.ChannelTemplateSpec `json:"channelTemplateSpec,omitempty"`
}
// BrokerStatus represents the current state of a Broker.
type BrokerStatus struct {
// inherits duck/v1 Status, which currently provides:
// * ObservedGeneration - the 'Generation' of the Service that was last processed by the controller.
// * Conditions - the latest available observations of a resource's current state.
duckv1.Status `json:",inline"`
// Broker is Addressable. It currently exposes the endpoint as a
// fully-qualified DNS name which will distribute traffic over the
// provided targets from inside the cluster.
//
// It generally has the form {broker}-router.{namespace}.svc.{cluster domain name}
Address duckv1alpha1.Addressable `json:"address,omitempty"`
// TriggerChannel is an objectref to the object for the TriggerChannel
TriggerChannel *corev1.ObjectReference `json:"triggerChannel,omitempty"`
// IngressChannel is an objectref to the object for the IngressChannel
IngressChannel *corev1.ObjectReference `json:"IngressChannel,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// BrokerList is a collection of Brokers.
type BrokerList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty"`
Items []Broker `json:"items"`
}
// GetGroupVersionKind returns GroupVersionKind for Brokers
func (t *Broker) GetGroupVersionKind() schema.GroupVersionKind {
return SchemeGroupVersion.WithKind("Broker")
}
// GetUntypedSpec returns the spec of the Broker.
func (b *Broker) GetUntypedSpec() interface{} {
return b.Spec
}

View File

@ -0,0 +1,81 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"context"
eventingduckv1alpha1 "knative.dev/eventing/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/apis"
"knative.dev/pkg/kmp"
)
func (b *Broker) Validate(ctx context.Context) *apis.FieldError {
return b.Spec.Validate(ctx).ViaField("spec")
}
func (bs *BrokerSpec) Validate(ctx context.Context) *apis.FieldError {
var errs *apis.FieldError
// Validate the new channelTemplate.
if cte := isValidChannelTemplate(bs.ChannelTemplate); cte != nil {
errs = errs.Also(cte.ViaField("channelTemplateSpec"))
}
// TODO validate that the channelTemplate only specifies the provisioner and arguments.
return errs
}
func isValidChannelTemplate(dct *eventingduckv1alpha1.ChannelTemplateSpec) *apis.FieldError {
var errs *apis.FieldError
if dct == nil {
return nil
}
if dct.Kind == "" {
errs = errs.Also(apis.ErrMissingField("kind"))
}
if dct.APIVersion == "" {
errs = errs.Also(apis.ErrMissingField("apiVersion"))
}
return errs
}
func (b *Broker) CheckImmutableFields(ctx context.Context, og apis.Immutable) *apis.FieldError {
if og == nil {
return nil
}
original, ok := og.(*Broker)
if !ok {
return &apis.FieldError{Message: "The provided original was not a Broker"}
}
if diff, err := kmp.ShortDiff(original.Spec.ChannelTemplate, b.Spec.ChannelTemplate); err != nil {
return &apis.FieldError{
Message: "Failed to diff Broker",
Paths: []string{"spec"},
Details: err.Error(),
}
} else if diff != "" {
return &apis.FieldError{
Message: "Immutable fields changed (-old +new)",
Paths: []string{"spec", "channelTemplate"},
Details: diff,
}
}
return nil
}

View File

@ -0,0 +1,20 @@
/*
Copyright 2019 The Knative 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.
*/
// Package v1alpha1 is the v1alpha1 version of the API.
// +k8s:deepcopy-gen=package
// +groupName=eventing.knative.dev
package v1alpha1

View File

@ -0,0 +1,29 @@
/*
Copyright 2018 The Knative 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.
*/
package v1alpha1
import "context"
func (et *EventType) SetDefaults(ctx context.Context) {
et.Spec.SetDefaults(ctx)
}
func (ets *EventTypeSpec) SetDefaults(ctx context.Context) {
if ets.Broker == "" {
ets.Broker = "default"
}
}

View File

@ -0,0 +1,60 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"knative.dev/pkg/apis"
)
var eventTypeCondSet = apis.NewLivingConditionSet(EventTypeConditionBrokerExists, EventTypeConditionBrokerReady)
const (
EventTypeConditionReady = apis.ConditionReady
EventTypeConditionBrokerExists apis.ConditionType = "BrokerExists"
EventTypeConditionBrokerReady apis.ConditionType = "BrokerReady"
)
// GetCondition returns the condition currently associated with the given type, or nil.
func (et *EventTypeStatus) GetCondition(t apis.ConditionType) *apis.Condition {
return eventTypeCondSet.Manage(et).GetCondition(t)
}
// IsReady returns true if the resource is ready overall.
func (et *EventTypeStatus) IsReady() bool {
return eventTypeCondSet.Manage(et).IsHappy()
}
// InitializeConditions sets relevant unset conditions to Unknown state.
func (et *EventTypeStatus) InitializeConditions() {
eventTypeCondSet.Manage(et).InitializeConditions()
}
func (et *EventTypeStatus) MarkBrokerExists() {
eventTypeCondSet.Manage(et).MarkTrue(EventTypeConditionBrokerExists)
}
func (et *EventTypeStatus) MarkBrokerDoesNotExist() {
eventTypeCondSet.Manage(et).MarkFalse(EventTypeConditionBrokerExists, "BrokerDoesNotExist", "Broker does not exist")
}
func (et *EventTypeStatus) MarkBrokerReady() {
eventTypeCondSet.Manage(et).MarkTrue(EventTypeConditionBrokerReady)
}
func (et *EventTypeStatus) MarkBrokerNotReady() {
eventTypeCondSet.Manage(et).MarkFalse(EventTypeConditionBrokerReady, "BrokerNotReady", "Broker is not ready")
}

View File

@ -0,0 +1,104 @@
/*
* Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
"knative.dev/pkg/kmeta"
"knative.dev/pkg/webhook"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type EventType struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec defines the desired state of the EventType.
Spec EventTypeSpec `json:"spec,omitempty"`
// Status represents the current state of the EventType.
// This data may be out of date.
// +optional
Status EventTypeStatus `json:"status,omitempty"`
}
var (
// Check that EventType can be validated, can be defaulted, and has immutable fields.
_ apis.Validatable = (*EventType)(nil)
_ apis.Defaultable = (*EventType)(nil)
_ apis.Immutable = (*EventType)(nil)
// Check that EventType can return its spec untyped.
_ apis.HasSpec = (*EventType)(nil)
_ runtime.Object = (*EventType)(nil)
_ webhook.GenericCRD = (*EventType)(nil)
// Check that we can create OwnerReferences to an EventType.
_ kmeta.OwnerRefable = (*EventType)(nil)
)
type EventTypeSpec struct {
// Type represents the CloudEvents type. It is authoritative.
Type string `json:"type"`
// Source is a URI, it represents the CloudEvents source.
Source string `json:"source"`
// Schema is a URI, it represents the CloudEvents schemaurl extension attribute.
// It may be a JSON schema, a protobuf schema, etc. It is optional.
// +optional
Schema string `json:"schema,omitempty"`
// Broker refers to the Broker that can provide the EventType.
Broker string `json:"broker"`
// Description is an optional field used to describe the EventType, in any meaningful way.
// +optional
Description string `json:"description,omitempty"`
}
// EventTypeStatus represents the current state of a EventType.
type EventTypeStatus struct {
// inherits duck/v1 Status, which currently provides:
// * ObservedGeneration - the 'Generation' of the Service that was last processed by the controller.
// * Conditions - the latest available observations of a resource's current state.
duckv1.Status `json:",inline"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// EventTypeList is a collection of EventTypes.
type EventTypeList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty"`
Items []EventType `json:"items"`
}
// GetGroupVersionKind returns GroupVersionKind for EventType
func (p *EventType) GetGroupVersionKind() schema.GroupVersionKind {
return SchemeGroupVersion.WithKind("EventType")
}
// GetUntypedSpec returns the spec of the EventType.
func (e *EventType) GetUntypedSpec() interface{} {
return e.Spec
}

View File

@ -0,0 +1,77 @@
/*
Copyright 2018 The Knative 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.
*/
package v1alpha1
import (
"context"
"github.com/google/go-cmp/cmp/cmpopts"
"knative.dev/pkg/apis"
"knative.dev/pkg/kmp"
)
func (et *EventType) Validate(ctx context.Context) *apis.FieldError {
return et.Spec.Validate(ctx).ViaField("spec")
}
func (ets *EventTypeSpec) Validate(ctx context.Context) *apis.FieldError {
var errs *apis.FieldError
if ets.Type == "" {
fe := apis.ErrMissingField("type")
errs = errs.Also(fe)
}
if ets.Source == "" {
// TODO validate is a valid URI.
fe := apis.ErrMissingField("source")
errs = errs.Also(fe)
}
if ets.Broker == "" {
fe := apis.ErrMissingField("broker")
errs = errs.Also(fe)
}
// TODO validate Schema is a valid URI.
return errs
}
func (et *EventType) CheckImmutableFields(ctx context.Context, og apis.Immutable) *apis.FieldError {
if og == nil {
return nil
}
original, ok := og.(*EventType)
if !ok {
return &apis.FieldError{Message: "The provided original was not an EventType"}
}
// All but Description field immutable.
ignoreArguments := cmpopts.IgnoreFields(EventTypeSpec{}, "Description")
if diff, err := kmp.ShortDiff(original.Spec, et.Spec, ignoreArguments); err != nil {
return &apis.FieldError{
Message: "Failed to diff EventType",
Paths: []string{"spec"},
Details: err.Error(),
}
} else if diff != "" {
return &apis.FieldError{
Message: "Immutable fields changed (-old +new)",
Paths: []string{"spec"},
Details: diff,
}
}
return nil
}

View File

@ -0,0 +1,57 @@
/*
Copyright 2018 The Knative 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.
*/
package v1alpha1
import (
"knative.dev/eventing/pkg/apis/eventing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: eventing.GroupName, Version: "v1alpha1"}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Broker{},
&BrokerList{},
&EventType{},
&EventTypeList{},
&Trigger{},
&TriggerList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View File

@ -0,0 +1,117 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
v1 "k8s.io/api/apps/v1"
"knative.dev/pkg/apis"
pkgduckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1"
duckv1alpha1 "knative.dev/eventing/pkg/apis/duck/v1alpha1"
messagingv1alpha1 "knative.dev/eventing/pkg/apis/messaging/v1alpha1"
duckv1 "knative.dev/pkg/apis/duck/v1"
duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1"
)
type testHelper struct{}
// TestHelper contains helpers for unit tests.
var TestHelper = testHelper{}
func (testHelper) ReadyChannelStatus() *duckv1alpha1.ChannelableStatus {
cs := &duckv1alpha1.ChannelableStatus{
Status: duckv1.Status{},
AddressStatus: pkgduckv1alpha1.AddressStatus{
Address: &pkgduckv1alpha1.Addressable{
Addressable: duckv1beta1.Addressable{
URL: &apis.URL{Scheme: "http", Host: "foo"},
},
Hostname: "foo",
},
},
SubscribableTypeStatus: duckv1alpha1.SubscribableTypeStatus{}}
return cs
}
func (t testHelper) NotReadyChannelStatus() *duckv1alpha1.ChannelableStatus {
return &duckv1alpha1.ChannelableStatus{}
}
func (testHelper) ReadySubscriptionStatus() *messagingv1alpha1.SubscriptionStatus {
ss := &messagingv1alpha1.SubscriptionStatus{}
ss.MarkChannelReady()
ss.MarkReferencesResolved()
ss.MarkAddedToChannel()
return ss
}
func (testHelper) NotReadySubscriptionStatus() *messagingv1alpha1.SubscriptionStatus {
ss := &messagingv1alpha1.SubscriptionStatus{}
ss.MarkReferencesResolved()
ss.MarkChannelNotReady("testInducedError", "test induced %s", "error")
return ss
}
func (t testHelper) ReadyBrokerStatus() *BrokerStatus {
bs := &BrokerStatus{}
bs.PropagateIngressDeploymentAvailability(t.AvailableDeployment())
bs.PropagateIngressChannelReadiness(t.ReadyChannelStatus())
bs.PropagateTriggerChannelReadiness(t.ReadyChannelStatus())
bs.PropagateIngressSubscriptionReadiness(t.ReadySubscriptionStatus())
bs.PropagateFilterDeploymentAvailability(t.AvailableDeployment())
bs.SetAddress(&apis.URL{Scheme: "http", Host: "foo"})
return bs
}
func (t testHelper) NotReadyBrokerStatus() *BrokerStatus {
bs := &BrokerStatus{}
bs.PropagateIngressChannelReadiness(&duckv1alpha1.ChannelableStatus{})
return bs
}
func (t testHelper) ReadyTriggerStatus() *TriggerStatus {
ts := &TriggerStatus{}
ts.InitializeConditions()
ts.SubscriberURI = "http://foo/"
ts.PropagateBrokerStatus(t.ReadyBrokerStatus())
ts.PropagateSubscriptionStatus(t.ReadySubscriptionStatus())
return ts
}
func (testHelper) UnavailableDeployment() *v1.Deployment {
d := &v1.Deployment{}
d.Name = "unavailable"
d.Status.Conditions = []v1.DeploymentCondition{
{
Type: v1.DeploymentAvailable,
Status: "False",
},
}
return d
}
func (t testHelper) AvailableDeployment() *v1.Deployment {
d := t.UnavailableDeployment()
d.Name = "available"
d.Status.Conditions = []v1.DeploymentCondition{
{
Type: v1.DeploymentAvailable,
Status: "True",
},
}
return d
}

View File

@ -0,0 +1,47 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"context"
)
const (
brokerLabel = "eventing.knative.dev/broker"
)
func (t *Trigger) SetDefaults(ctx context.Context) {
t.Spec.SetDefaults(ctx)
setLabels(t)
}
func (ts *TriggerSpec) SetDefaults(ctx context.Context) {
if ts.Broker == "" {
ts.Broker = "default"
}
// Make a default filter that allows anything.
if ts.Filter == nil {
ts.Filter = &TriggerFilter{}
}
}
func setLabels(t *Trigger) {
if len(t.Labels) == 0 {
t.Labels = map[string]string{}
}
t.Labels[brokerLabel] = t.Spec.Broker
}

View File

@ -0,0 +1,115 @@
/*
* Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
messagingv1alpha1 "knative.dev/eventing/pkg/apis/messaging/v1alpha1"
"knative.dev/pkg/apis"
duckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1"
)
var triggerCondSet = apis.NewLivingConditionSet(TriggerConditionBroker, TriggerConditionSubscribed, TriggerConditionDependency)
const (
// TriggerConditionReady has status True when all subconditions below have been set to True.
TriggerConditionReady = apis.ConditionReady
TriggerConditionBroker apis.ConditionType = "Broker"
TriggerConditionSubscribed apis.ConditionType = "Subscribed"
TriggerConditionDependency apis.ConditionType = "Dependency"
// TriggerAnyFilter Constant to represent that we should allow anything.
TriggerAnyFilter = ""
)
// GetCondition returns the condition currently associated with the given type, or nil.
func (ts *TriggerStatus) GetCondition(t apis.ConditionType) *apis.Condition {
return triggerCondSet.Manage(ts).GetCondition(t)
}
// IsReady returns true if the resource is ready overall.
func (ts *TriggerStatus) IsReady() bool {
return triggerCondSet.Manage(ts).IsHappy()
}
// InitializeConditions sets relevant unset conditions to Unknown state.
func (ts *TriggerStatus) InitializeConditions() {
triggerCondSet.Manage(ts).InitializeConditions()
}
func (ts *TriggerStatus) PropagateBrokerStatus(bs *BrokerStatus) {
if bs.IsReady() {
triggerCondSet.Manage(ts).MarkTrue(TriggerConditionBroker)
} else {
msg := "nil"
if bc := brokerCondSet.Manage(bs).GetCondition(BrokerConditionReady); bc != nil {
msg = bc.Message
}
ts.MarkBrokerFailed("BrokerNotReady", "Broker is not ready: %s", msg)
}
}
func (ts *TriggerStatus) MarkBrokerFailed(reason, messageFormat string, messageA ...interface{}) {
triggerCondSet.Manage(ts).MarkFalse(TriggerConditionBroker, reason, messageFormat, messageA...)
}
func (ts *TriggerStatus) PropagateSubscriptionStatus(ss *messagingv1alpha1.SubscriptionStatus) {
if ss.IsReady() {
triggerCondSet.Manage(ts).MarkTrue(TriggerConditionSubscribed)
} else {
msg := "nil"
if sc := ss.Status.GetCondition(messagingv1alpha1.SubscriptionConditionReady); sc != nil {
msg = sc.Message
}
ts.MarkNotSubscribed("SubscriptionNotReady", "Subscription is not ready: %s", msg)
}
}
func (ts *TriggerStatus) MarkNotSubscribed(reason, messageFormat string, messageA ...interface{}) {
triggerCondSet.Manage(ts).MarkFalse(TriggerConditionSubscribed, reason, messageFormat, messageA...)
}
func (ts *TriggerStatus) MarkSubscriptionNotOwned(sub *messagingv1alpha1.Subscription) {
triggerCondSet.Manage(ts).MarkFalse(TriggerConditionSubscribed, "SubscriptionNotOwned", "Subscription %q is not owned by this Trigger.", sub.Name)
}
func (ts *TriggerStatus) MarkDependencySucceeded() {
triggerCondSet.Manage(ts).MarkTrue(TriggerConditionDependency)
}
func (ts *TriggerStatus) MarkDependencyFailed(reason, messageFormat string, messageA ...interface{}) {
triggerCondSet.Manage(ts).MarkFalse(TriggerConditionDependency, reason, messageFormat, messageA...)
}
func (ts *TriggerStatus) MarkDependencyUnknown(reason, messageFormat string, messageA ...interface{}) {
triggerCondSet.Manage(ts).MarkUnknown(TriggerConditionDependency, reason, messageFormat, messageA...)
}
func (ts *TriggerStatus) PropagateDependencyStatus(ks *duckv1alpha1.KResource) {
kc := ks.Status.GetCondition(duckv1alpha1.ConditionReady)
if kc != nil && kc.IsTrue() {
ts.MarkDependencySucceeded()
} else {
msg := "nil"
if kc != nil {
msg = kc.Message
}
ts.MarkDependencyFailed("DependencyNotReady", "Dependency is not ready: %s", msg)
}
}

View File

@ -0,0 +1,151 @@
/*
* Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
apisv1alpha1 "knative.dev/pkg/apis/v1alpha1"
"knative.dev/pkg/kmeta"
"knative.dev/pkg/webhook"
)
const (
// DependencyAnnotation is the annotation key used to mark the sources that the Trigger depends on.
// This will be used when the kn client creates an importer and trigger pair for the user such that the trigger only receives events produced by the paired importer.
DependencyAnnotation = "knative.dev/dependency"
// InjectionAnnotation is the annotation key used to enable knative eventing injection for a namespace and automatically create a default broker.
// This will be used when the client creates a trigger paired with default broker and the default broker doesn't exist in the namespace
InjectionAnnotation = "knative-eventing-injection"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Trigger represents a request to have events delivered to a consumer from a
// Broker's event pool.
type Trigger struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec defines the desired state of the Trigger.
Spec TriggerSpec `json:"spec,omitempty"`
// Status represents the current state of the Trigger. This data may be out of
// date.
// +optional
Status TriggerStatus `json:"status,omitempty"`
}
var (
// Check that Trigger can be validated, can be defaulted, and has immutable fields.
_ apis.Validatable = (*Trigger)(nil)
_ apis.Defaultable = (*Trigger)(nil)
_ apis.Immutable = (*Trigger)(nil)
// Check that Trigger can return its spec untyped.
_ apis.HasSpec = (*Trigger)(nil)
_ runtime.Object = (*Trigger)(nil)
_ webhook.GenericCRD = (*Trigger)(nil)
// Check that we can create OwnerReferences to a Trigger.
_ kmeta.OwnerRefable = (*Trigger)(nil)
)
type TriggerSpec struct {
// Broker is the broker that this trigger receives events from. If not specified, will default
// to 'default'.
Broker string `json:"broker,omitempty"`
// Filter is the filter to apply against all events from the Broker. Only events that pass this
// filter will be sent to the Subscriber. If not specified, will default to allowing all events.
//
// +optional
Filter *TriggerFilter `json:"filter,omitempty"`
// Subscriber is the addressable that receives events from the Broker that pass the Filter. It
// is required.
Subscriber *apisv1alpha1.Destination `json:"subscriber,omitempty"`
}
type TriggerFilter struct {
// DeprecatedSourceAndType filters events based on exact matches on the
// CloudEvents type and source attributes. This field has been replaced by the
// Attributes field.
//
// +optional
DeprecatedSourceAndType *TriggerFilterSourceAndType `json:"sourceAndType,omitempty"`
// Attributes filters events by exact match on event context attributes.
// Each key in the map is compared with the equivalent key in the event
// context. An event passes the filter if all values are equal to the
// specified values.
//
// Nested context attributes are not supported as keys. Only string values are supported.
//
// +optional
Attributes *TriggerFilterAttributes `json:"attributes,omitempty"`
}
// TriggerFilterSourceAndType filters events based on exact matches on the cloud event's type and
// source attributes. Only exact matches will pass the filter. Either or both type and source can
// use the value '' to indicate all strings match.
type TriggerFilterSourceAndType struct {
Type string `json:"type,omitempty"`
Source string `json:"source,omitempty"`
}
// TriggerFilterAttributes is a map of context attribute names to values for
// filtering by equality. Only exact matches will pass the filter. You can use the value ''
// to indicate all strings match.
type TriggerFilterAttributes map[string]string
// TriggerStatus represents the current state of a Trigger.
type TriggerStatus struct {
// inherits duck/v1 Status, which currently provides:
// * ObservedGeneration - the 'Generation' of the Service that was last processed by the controller.
// * Conditions - the latest available observations of a resource's current state.
duckv1.Status `json:",inline"`
// SubscriberURI is the resolved URI of the receiver for this Trigger.
SubscriberURI string `json:"subscriberURI,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// TriggerList is a collection of Triggers.
type TriggerList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty"`
Items []Trigger `json:"items"`
}
// GetGroupVersionKind returns GroupVersionKind for Triggers
func (t *Trigger) GetGroupVersionKind() schema.GroupVersionKind {
return SchemeGroupVersion.WithKind("Trigger")
}
// GetUntypedSpec returns the spec of the Trigger.
func (t *Trigger) GetUntypedSpec() interface{} {
return t.Spec
}

View File

@ -0,0 +1,191 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"context"
"encoding/json"
"fmt"
"regexp"
"knative.dev/pkg/apis"
"knative.dev/pkg/kmp"
corev1 "k8s.io/api/core/v1"
)
var (
// Only allow lowercase alphanumeric, starting with letters.
validAttributeName = regexp.MustCompile(`^[a-z][a-z0-9]*$`)
)
// Validate the Trigger.
func (t *Trigger) Validate(ctx context.Context) *apis.FieldError {
errs := t.Spec.Validate(ctx).ViaField("spec")
errs = t.validateAnnotation(errs, DependencyAnnotation, t.validateDependencyAnnotation)
errs = t.validateAnnotation(errs, InjectionAnnotation, t.validateInjectionAnnotation)
return errs
}
// Validate the TriggerSpec.
func (ts *TriggerSpec) Validate(ctx context.Context) *apis.FieldError {
var errs *apis.FieldError
if ts.Broker == "" {
fe := apis.ErrMissingField("broker")
errs = errs.Also(fe)
}
if ts.Filter == nil {
fe := apis.ErrMissingField("filter")
errs = errs.Also(fe)
}
if ts.Filter != nil {
filtersSpecified := make([]string, 0)
if ts.Filter.DeprecatedSourceAndType != nil {
filtersSpecified = append(filtersSpecified, "filter.sourceAndType")
}
if ts.Filter.Attributes != nil {
filtersSpecified = append(filtersSpecified, "filter.attributes")
if len(*ts.Filter.Attributes) == 0 {
fe := &apis.FieldError{
Message: "At least one filtered attribute must be specified",
Paths: []string{"filter.attributes"},
}
errs = errs.Also(fe)
} else {
attrs := map[string]string(*ts.Filter.Attributes)
for attr := range attrs {
if !validAttributeName.MatchString(attr) {
fe := &apis.FieldError{
Message: fmt.Sprintf("Invalid attribute name: %q", attr),
Paths: []string{"filter.attributes"},
}
errs = errs.Also(fe)
}
}
}
}
if len(filtersSpecified) > 1 {
fe := apis.ErrMultipleOneOf(filtersSpecified...)
errs = errs.Also(fe)
}
}
if ts.Subscriber == nil {
fe := apis.ErrMissingField("subscriber")
errs = errs.Also(fe)
} else if fe := ts.Subscriber.ValidateDisallowDeprecated(ctx); fe != nil {
errs = errs.Also(fe.ViaField("subscriber"))
}
return errs
}
// CheckImmutableFields checks that any immutable fields were not changed.
func (t *Trigger) CheckImmutableFields(ctx context.Context, og apis.Immutable) *apis.FieldError {
if og == nil {
return nil
}
original, ok := og.(*Trigger)
if !ok {
return &apis.FieldError{Message: "The provided original was not a Trigger"}
}
if diff, err := kmp.ShortDiff(original.Spec.Broker, t.Spec.Broker); err != nil {
return &apis.FieldError{
Message: "Failed to diff Trigger",
Paths: []string{"spec"},
Details: err.Error(),
}
} else if diff != "" {
return &apis.FieldError{
Message: "Immutable fields changed (-old +new)",
Paths: []string{"spec", "broker"},
Details: diff,
}
}
return nil
}
func GetObjRefFromDependencyAnnotation(dependencyAnnotation string) (corev1.ObjectReference, error) {
var objectRef corev1.ObjectReference
if err := json.Unmarshal([]byte(dependencyAnnotation), &objectRef); err != nil {
return objectRef, err
}
return objectRef, nil
}
func (t *Trigger) validateAnnotation(errs *apis.FieldError, annotation string, function func(string) *apis.FieldError) *apis.FieldError {
if annotationValue, ok := t.GetAnnotations()[annotation]; ok {
annotationPrefix := fmt.Sprintf("metadata.annotations[%s]", annotation)
errs = errs.Also(function(annotationValue).ViaField(annotationPrefix))
}
return errs
}
func (t *Trigger) validateDependencyAnnotation(dependencyAnnotation string) *apis.FieldError {
depObjRef, err := GetObjRefFromDependencyAnnotation(dependencyAnnotation)
if err != nil {
return &apis.FieldError{
Message: fmt.Sprintf("The provided annotation was not a corev1.ObjectReference: %q", dependencyAnnotation),
Details: err.Error(),
Paths: []string{""},
}
}
var errs *apis.FieldError
if depObjRef.Namespace != "" && depObjRef.Namespace != t.GetNamespace() {
fe := &apis.FieldError{
Message: fmt.Sprintf("Namespace must be empty or equal to the trigger namespace %q", t.GetNamespace()),
Paths: []string{"namespace"},
}
errs = errs.Also(fe)
}
if depObjRef.Kind == "" {
fe := apis.ErrMissingField("kind")
errs = errs.Also(fe)
}
if depObjRef.Name == "" {
fe := apis.ErrMissingField("name")
errs = errs.Also(fe)
}
if depObjRef.APIVersion == "" {
fe := apis.ErrMissingField("apiVersion")
errs = errs.Also(fe)
}
return errs
}
func (t *Trigger) validateInjectionAnnotation(injectionAnnotation string) *apis.FieldError {
if injectionAnnotation != "enabled" {
return &apis.FieldError{
Message: fmt.Sprintf(`The provided injection annotation value can only be "enabled", not %q`, injectionAnnotation),
Paths: []string{""},
}
}
if t.Spec.Broker != "default" {
return &apis.FieldError{
Message: fmt.Sprintf("The provided injection annotation is only used for default broker, but non-default broker specified here: %q", t.Spec.Broker),
Paths: []string{""},
}
}
return nil
}

View File

@ -0,0 +1,406 @@
// +build !ignore_autogenerated
/*
Copyright 2019 The Knative 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.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1alpha1
import (
v1 "k8s.io/api/core/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
duckv1alpha1 "knative.dev/eventing/pkg/apis/duck/v1alpha1"
apisv1alpha1 "knative.dev/pkg/apis/v1alpha1"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Broker) DeepCopyInto(out *Broker) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Broker.
func (in *Broker) DeepCopy() *Broker {
if in == nil {
return nil
}
out := new(Broker)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Broker) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BrokerList) DeepCopyInto(out *BrokerList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Broker, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BrokerList.
func (in *BrokerList) DeepCopy() *BrokerList {
if in == nil {
return nil
}
out := new(BrokerList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BrokerList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BrokerSpec) DeepCopyInto(out *BrokerSpec) {
*out = *in
if in.ChannelTemplate != nil {
in, out := &in.ChannelTemplate, &out.ChannelTemplate
*out = new(duckv1alpha1.ChannelTemplateSpec)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BrokerSpec.
func (in *BrokerSpec) DeepCopy() *BrokerSpec {
if in == nil {
return nil
}
out := new(BrokerSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BrokerStatus) DeepCopyInto(out *BrokerStatus) {
*out = *in
in.Status.DeepCopyInto(&out.Status)
in.Address.DeepCopyInto(&out.Address)
if in.TriggerChannel != nil {
in, out := &in.TriggerChannel, &out.TriggerChannel
*out = new(v1.ObjectReference)
**out = **in
}
if in.IngressChannel != nil {
in, out := &in.IngressChannel, &out.IngressChannel
*out = new(v1.ObjectReference)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BrokerStatus.
func (in *BrokerStatus) DeepCopy() *BrokerStatus {
if in == nil {
return nil
}
out := new(BrokerStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EventType) DeepCopyInto(out *EventType) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventType.
func (in *EventType) DeepCopy() *EventType {
if in == nil {
return nil
}
out := new(EventType)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *EventType) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EventTypeList) DeepCopyInto(out *EventTypeList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]EventType, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventTypeList.
func (in *EventTypeList) DeepCopy() *EventTypeList {
if in == nil {
return nil
}
out := new(EventTypeList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *EventTypeList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EventTypeSpec) DeepCopyInto(out *EventTypeSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventTypeSpec.
func (in *EventTypeSpec) DeepCopy() *EventTypeSpec {
if in == nil {
return nil
}
out := new(EventTypeSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EventTypeStatus) DeepCopyInto(out *EventTypeStatus) {
*out = *in
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventTypeStatus.
func (in *EventTypeStatus) DeepCopy() *EventTypeStatus {
if in == nil {
return nil
}
out := new(EventTypeStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Trigger) DeepCopyInto(out *Trigger) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Trigger.
func (in *Trigger) DeepCopy() *Trigger {
if in == nil {
return nil
}
out := new(Trigger)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Trigger) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TriggerFilter) DeepCopyInto(out *TriggerFilter) {
*out = *in
if in.DeprecatedSourceAndType != nil {
in, out := &in.DeprecatedSourceAndType, &out.DeprecatedSourceAndType
*out = new(TriggerFilterSourceAndType)
**out = **in
}
if in.Attributes != nil {
in, out := &in.Attributes, &out.Attributes
*out = new(TriggerFilterAttributes)
if **in != nil {
in, out := *in, *out
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerFilter.
func (in *TriggerFilter) DeepCopy() *TriggerFilter {
if in == nil {
return nil
}
out := new(TriggerFilter)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in TriggerFilterAttributes) DeepCopyInto(out *TriggerFilterAttributes) {
{
in := &in
*out = make(TriggerFilterAttributes, len(*in))
for key, val := range *in {
(*out)[key] = val
}
return
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerFilterAttributes.
func (in TriggerFilterAttributes) DeepCopy() TriggerFilterAttributes {
if in == nil {
return nil
}
out := new(TriggerFilterAttributes)
in.DeepCopyInto(out)
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TriggerFilterSourceAndType) DeepCopyInto(out *TriggerFilterSourceAndType) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerFilterSourceAndType.
func (in *TriggerFilterSourceAndType) DeepCopy() *TriggerFilterSourceAndType {
if in == nil {
return nil
}
out := new(TriggerFilterSourceAndType)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TriggerList) DeepCopyInto(out *TriggerList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Trigger, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerList.
func (in *TriggerList) DeepCopy() *TriggerList {
if in == nil {
return nil
}
out := new(TriggerList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *TriggerList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TriggerSpec) DeepCopyInto(out *TriggerSpec) {
*out = *in
if in.Filter != nil {
in, out := &in.Filter, &out.Filter
*out = new(TriggerFilter)
(*in).DeepCopyInto(*out)
}
if in.Subscriber != nil {
in, out := &in.Subscriber, &out.Subscriber
*out = new(apisv1alpha1.Destination)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerSpec.
func (in *TriggerSpec) DeepCopy() *TriggerSpec {
if in == nil {
return nil
}
out := new(TriggerSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TriggerStatus) DeepCopyInto(out *TriggerStatus) {
*out = *in
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TriggerStatus.
func (in *TriggerStatus) DeepCopy() *TriggerStatus {
if in == nil {
return nil
}
out := new(TriggerStatus)
in.DeepCopyInto(out)
return out
}

View File

@ -0,0 +1,21 @@
/*
Copyright 2019 The Knative 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.
*/
package messaging
const (
GroupName = "messaging.knative.dev"
)

View File

@ -0,0 +1,37 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"context"
eventingduckv1alpha1 "knative.dev/eventing/pkg/apis/duck/v1alpha1"
)
func (c *Channel) SetDefaults(ctx context.Context) {
if c != nil && c.Spec.ChannelTemplate == nil {
// The singleton may not have been set, if so ignore it and validation will reject the
// Channel.
if cd := eventingduckv1alpha1.ChannelDefaulterSingleton; cd != nil {
channelTemplate := cd.GetDefault(c.Namespace)
c.Spec.ChannelTemplate = channelTemplate
}
}
c.Spec.SetDefaults(ctx)
}
func (cs *ChannelSpec) SetDefaults(ctx context.Context) {}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
eventingduck "knative.dev/eventing/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/apis"
"knative.dev/pkg/apis/duck/v1alpha1"
)
var chCondSet = apis.NewLivingConditionSet(ChannelConditionBackingChannelReady, ChannelConditionAddressable)
const (
// ChannelConditionReady has status True when all subconditions below have been set to True.
ChannelConditionReady = apis.ConditionReady
// ChannelConditionBackingChannelReady has status True when the backing Channel CRD is ready.
ChannelConditionBackingChannelReady apis.ConditionType = "BackingChannelReady"
// ChannelConditionAddressable has status true when this Channel meets
// the Addressable contract and has a non-empty hostname.
ChannelConditionAddressable apis.ConditionType = "Addressable"
)
// GetCondition returns the condition currently associated with the given type, or nil.
func (cs *ChannelStatus) GetCondition(t apis.ConditionType) *apis.Condition {
return chCondSet.Manage(cs).GetCondition(t)
}
// IsReady returns true if the resource is ready overall.
func (cs *ChannelStatus) IsReady() bool {
return chCondSet.Manage(cs).IsHappy()
}
// InitializeConditions sets relevant unset conditions to Unknown state.
func (cs *ChannelStatus) InitializeConditions() {
chCondSet.Manage(cs).InitializeConditions()
}
func (cs *ChannelStatus) SetAddress(address *v1alpha1.Addressable) {
if cs.Address == nil {
cs.Address = &v1alpha1.Addressable{}
}
if address != nil && address.URL != nil {
cs.Address.Hostname = address.URL.Host
cs.Address.URL = address.URL
chCondSet.Manage(cs).MarkTrue(ChannelConditionAddressable)
} else {
cs.Address.Hostname = ""
cs.Address.URL = nil
chCondSet.Manage(cs).MarkFalse(ChannelConditionAddressable, "EmptyHostname", "hostname is the empty string")
}
}
func (cs *ChannelStatus) MarkBackingChannelFailed(reason, messageFormat string, messageA ...interface{}) {
chCondSet.Manage(cs).MarkFalse(ChannelConditionBackingChannelReady, reason, messageFormat, messageA...)
}
func (cs *ChannelStatus) MarkBackingChannelReady() {
chCondSet.Manage(cs).MarkTrue(ChannelConditionBackingChannelReady)
}
func (cs *ChannelStatus) PropagateStatuses(chs *eventingduck.ChannelableStatus) {
// TODO: Once you can get a Ready status from Channelable in a generic way, use it here.
readyCondition := chs.Status.GetCondition(apis.ConditionReady)
if readyCondition != nil {
if readyCondition.Status != corev1.ConditionTrue {
cs.MarkBackingChannelFailed(readyCondition.Reason, readyCondition.Message)
} else {
cs.MarkBackingChannelReady()
}
}
// Set the address and update the Addressable conditions.
cs.SetAddress(chs.AddressStatus.Address)
// Set the subscribable status.
if subscribableTypeStatus := chs.GetSubscribableTypeStatus(); subscribableTypeStatus != nil {
cs.SetSubscribableTypeStatus(*subscribableTypeStatus)
}
}

View File

@ -0,0 +1,116 @@
/*
* Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
eventingduck "knative.dev/eventing/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
duckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/kmeta"
"knative.dev/pkg/webhook"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Channel represents a generic Channel. It is normally used when we want a Channel, but don't need a specific Channel implementation.
type Channel struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec defines the desired state of the Channel.
Spec ChannelSpec `json:"spec,omitempty"`
// Status represents the current state of the Channel. This data may be out of
// date.
// +optional
Status ChannelStatus `json:"status,omitempty"`
}
var (
// Check that Channel can be validated and defaulted.
_ apis.Validatable = (*Channel)(nil)
_ apis.Defaultable = (*Channel)(nil)
// Check that Channel can return its spec untyped.
_ apis.HasSpec = (*Channel)(nil)
_ runtime.Object = (*Channel)(nil)
_ webhook.GenericCRD = (*Channel)(nil)
// Check that we can create OwnerReferences to a Channel.
_ kmeta.OwnerRefable = (*Channel)(nil)
)
// ChannelSpec defines which subscribers have expressed interest in receiving events from this Channel.
// It also defines the ChannelTemplate to use in order to create the CRD Channel backing this Channel.
type ChannelSpec struct {
// ChannelTemplate specifies which Channel CRD to use to create the CRD Channel backing this Channel.
// This is immutable after creation. Normally this is set by the Channel defaulter, not directly by the user.
ChannelTemplate *eventingduck.ChannelTemplateSpec `json:"channelTemplate"`
// Channel conforms to Duck type Subscribable.
Subscribable *eventingduck.Subscribable `json:"subscribable,omitempty"`
}
// ChannelStatus represents the current state of a Channel.
type ChannelStatus struct {
// inherits duck/v1 Status, which currently provides:
// * ObservedGeneration - the 'Generation' of the Service that was last processed by the controller.
// * Conditions - the latest available observations of a resource's current state.
duckv1.Status `json:",inline"`
// Channel is Addressable. It currently exposes the endpoint as a
// fully-qualified DNS name which will distribute traffic over the
// provided targets from inside the cluster.
//
// It generally has the form {channel}.{namespace}.svc.{cluster domain name}
duckv1alpha1.AddressStatus `json:",inline"`
// Subscribers is populated with the statuses of each of the Channelable's subscribers.
eventingduck.SubscribableTypeStatus `json:",inline"`
// Channel is an ObjectReference to the Channel CRD backing this Channel.
Channel *corev1.ObjectReference `json:"channel,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ChannelList is a collection of Channels.
type ChannelList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty"`
Items []Channel `json:"items"`
}
// GetGroupVersionKind returns GroupVersionKind for Channels.
func (dc *Channel) GetGroupVersionKind() schema.GroupVersionKind {
return SchemeGroupVersion.WithKind("Channel")
}
// GetUntypedSpec returns the spec of the Channel.
func (c *Channel) GetUntypedSpec() interface{} {
return c.Spec
}

View File

@ -0,0 +1,95 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"context"
"fmt"
"github.com/google/go-cmp/cmp/cmpopts"
"knative.dev/pkg/kmp"
eventingduck "knative.dev/eventing/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/apis"
)
func (c *Channel) Validate(ctx context.Context) *apis.FieldError {
return c.Spec.Validate(ctx).ViaField("spec")
}
func (cs *ChannelSpec) Validate(ctx context.Context) *apis.FieldError {
var errs *apis.FieldError
if cs.ChannelTemplate == nil {
// The Channel defaulter is expected to set this, not the users.
errs = errs.Also(apis.ErrMissingField("channelTemplate"))
} else {
if cte := isValidChannelTemplate(cs.ChannelTemplate); cte != nil {
errs = errs.Also(cte.ViaField("channelTemplate"))
}
}
if cs.Subscribable != nil {
for i, subscriber := range cs.Subscribable.Subscribers {
if subscriber.ReplyURI == "" && subscriber.SubscriberURI == "" {
fe := apis.ErrMissingField("replyURI", "subscriberURI")
fe.Details = "expected at least one of, got none"
errs = errs.Also(fe.ViaField(fmt.Sprintf("subscriber[%d]", i)).ViaField("subscribable"))
}
}
}
return errs
}
func isValidChannelTemplate(ct *eventingduck.ChannelTemplateSpec) *apis.FieldError {
var errs *apis.FieldError
if ct.Kind == "" {
errs = errs.Also(apis.ErrMissingField("kind"))
}
if ct.APIVersion == "" {
errs = errs.Also(apis.ErrMissingField("apiVersion"))
}
return errs
}
func (c *Channel) CheckImmutableFields(ctx context.Context, og apis.Immutable) *apis.FieldError {
if og == nil {
return nil
}
original, ok := og.(*Channel)
if !ok {
return &apis.FieldError{Message: "The provided original was not a Channel"}
}
ignoreArguments := cmpopts.IgnoreFields(ChannelSpec{}, "Subscribable")
if diff, err := kmp.ShortDiff(original.Spec, c.Spec, ignoreArguments); err != nil {
return &apis.FieldError{
Message: "Failed to diff Channel",
Paths: []string{"spec"},
Details: err.Error(),
}
} else if diff != "" {
return &apis.FieldError{
Message: "Immutable fields changed (-old +new)",
Paths: []string{"spec"},
Details: diff,
}
}
return nil
}

View File

@ -0,0 +1,20 @@
/*
Copyright 2019 The Knative 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.
*/
// Package v1alpha1 is the v1alpha1 version of the API.
// +k8s:deepcopy-gen=package
// +groupName=messaging.knative.dev
package v1alpha1

View File

@ -0,0 +1,27 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import "context"
func (imc *InMemoryChannel) SetDefaults(ctx context.Context) {
imc.Spec.SetDefaults(ctx)
}
func (imcs *InMemoryChannelSpec) SetDefaults(ctx context.Context) {
// TODO: Nothing to default here...
}

View File

@ -0,0 +1,125 @@
/*
* Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"knative.dev/pkg/apis"
"knative.dev/pkg/apis/duck/v1alpha1"
)
var imcCondSet = apis.NewLivingConditionSet(InMemoryChannelConditionDispatcherReady, InMemoryChannelConditionServiceReady, InMemoryChannelConditionEndpointsReady, InMemoryChannelConditionAddressable, InMemoryChannelConditionChannelServiceReady)
const (
// InMemoryChannelConditionReady has status True when all subconditions below have been set to True.
InMemoryChannelConditionReady = apis.ConditionReady
// InMemoryChannelConditionDispatcherReady has status True when a Dispatcher deployment is ready
// Keyed off appsv1.DeploymentAvaialble, which means minimum available replicas required are up
// and running for at least minReadySeconds.
InMemoryChannelConditionDispatcherReady apis.ConditionType = "DispatcherReady"
// InMemoryChannelConditionServiceReady has status True when a k8s Service is ready. This
// basically just means it exists because there's no meaningful status in Service. See Endpoints
// below.
InMemoryChannelConditionServiceReady apis.ConditionType = "ServiceReady"
// InMemoryChannelConditionEndpointsReady has status True when a k8s Service Endpoints are backed
// by at least one endpoint.
InMemoryChannelConditionEndpointsReady apis.ConditionType = "EndpointsReady"
// InMemoryChannelConditionAddressable has status true when this InMemoryChannel meets
// the Addressable contract and has a non-empty hostname.
InMemoryChannelConditionAddressable apis.ConditionType = "Addressable"
// InMemoryChannelConditionServiceReady has status True when a k8s Service representing the channel is ready.
// Because this uses ExternalName, there are no endpoints to check.
InMemoryChannelConditionChannelServiceReady apis.ConditionType = "ChannelServiceReady"
)
// GetCondition returns the condition currently associated with the given type, or nil.
func (imcs *InMemoryChannelStatus) GetCondition(t apis.ConditionType) *apis.Condition {
return imcCondSet.Manage(imcs).GetCondition(t)
}
// IsReady returns true if the resource is ready overall.
func (imcs *InMemoryChannelStatus) IsReady() bool {
return imcCondSet.Manage(imcs).IsHappy()
}
// InitializeConditions sets relevant unset conditions to Unknown state.
func (imcs *InMemoryChannelStatus) InitializeConditions() {
imcCondSet.Manage(imcs).InitializeConditions()
}
// TODO: Use the new beta duck types.
func (imcs *InMemoryChannelStatus) SetAddress(url *apis.URL) {
if imcs.Address == nil {
imcs.Address = &v1alpha1.Addressable{}
}
if url != nil {
imcs.Address.Hostname = url.Host
imcs.Address.URL = url
imcCondSet.Manage(imcs).MarkTrue(InMemoryChannelConditionAddressable)
} else {
imcs.Address.Hostname = ""
imcs.Address.URL = nil
imcCondSet.Manage(imcs).MarkFalse(InMemoryChannelConditionAddressable, "emptyHostname", "hostname is the empty string")
}
}
func (imcs *InMemoryChannelStatus) MarkDispatcherFailed(reason, messageFormat string, messageA ...interface{}) {
imcCondSet.Manage(imcs).MarkFalse(InMemoryChannelConditionDispatcherReady, reason, messageFormat, messageA...)
}
// TODO: Unify this with the ones from Eventing. Say: Broker, Trigger.
func (imcs *InMemoryChannelStatus) PropagateDispatcherStatus(ds *appsv1.DeploymentStatus) {
for _, cond := range ds.Conditions {
if cond.Type == appsv1.DeploymentAvailable {
if cond.Status != corev1.ConditionTrue {
imcs.MarkDispatcherFailed("DispatcherNotReady", "Dispatcher Deployment is not ready: %s : %s", cond.Reason, cond.Message)
} else {
imcCondSet.Manage(imcs).MarkTrue(InMemoryChannelConditionDispatcherReady)
}
}
}
}
func (imcs *InMemoryChannelStatus) MarkServiceFailed(reason, messageFormat string, messageA ...interface{}) {
imcCondSet.Manage(imcs).MarkFalse(InMemoryChannelConditionServiceReady, reason, messageFormat, messageA...)
}
func (imcs *InMemoryChannelStatus) MarkServiceTrue() {
imcCondSet.Manage(imcs).MarkTrue(InMemoryChannelConditionServiceReady)
}
func (imcs *InMemoryChannelStatus) MarkChannelServiceFailed(reason, messageFormat string, messageA ...interface{}) {
imcCondSet.Manage(imcs).MarkFalse(InMemoryChannelConditionChannelServiceReady, reason, messageFormat, messageA...)
}
func (imcs *InMemoryChannelStatus) MarkChannelServiceTrue() {
imcCondSet.Manage(imcs).MarkTrue(InMemoryChannelConditionChannelServiceReady)
}
func (imcs *InMemoryChannelStatus) MarkEndpointsFailed(reason, messageFormat string, messageA ...interface{}) {
imcCondSet.Manage(imcs).MarkFalse(InMemoryChannelConditionEndpointsReady, reason, messageFormat, messageA...)
}
func (imcs *InMemoryChannelStatus) MarkEndpointsTrue() {
imcCondSet.Manage(imcs).MarkTrue(InMemoryChannelConditionEndpointsReady)
}

View File

@ -0,0 +1,108 @@
/*
* Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
eventingduck "knative.dev/eventing/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
duckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/kmeta"
"knative.dev/pkg/webhook"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// InMemoryChannel is a resource representing an in memory channel
type InMemoryChannel struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec defines the desired state of the Channel.
Spec InMemoryChannelSpec `json:"spec,omitempty"`
// Status represents the current state of the Channel. This data may be out of
// date.
// +optional
Status InMemoryChannelStatus `json:"status,omitempty"`
}
var (
// Check that InMemoryChannel can be validated and defaulted.
_ apis.Validatable = (*InMemoryChannel)(nil)
_ apis.Defaultable = (*InMemoryChannel)(nil)
// Check that InMemoryChannel can return its spec untyped.
_ apis.HasSpec = (*InMemoryChannel)(nil)
_ runtime.Object = (*InMemoryChannel)(nil)
_ webhook.GenericCRD = (*InMemoryChannel)(nil)
// Check that we can create OwnerReferences to an InMemoryChannel.
_ kmeta.OwnerRefable = (*InMemoryChannel)(nil)
)
// InMemoryChannelSpec defines which subscribers have expressed interest in
// receiving events from this InMemoryChannel.
// arguments for a Channel.
type InMemoryChannelSpec struct {
// Channel conforms to Duck type Subscribable.
Subscribable *eventingduck.Subscribable `json:"subscribable,omitempty"`
}
// ChannelStatus represents the current state of a Channel.
type InMemoryChannelStatus struct {
// inherits duck/v1 Status, which currently provides:
// * ObservedGeneration - the 'Generation' of the Service that was last processed by the controller.
// * Conditions - the latest available observations of a resource's current state.
duckv1.Status `json:",inline"`
// InMemoryChannel is Addressable. It currently exposes the endpoint as a
// fully-qualified DNS name which will distribute traffic over the
// provided targets from inside the cluster.
//
// It generally has the form {channel}.{namespace}.svc.{cluster domain name}
duckv1alpha1.AddressStatus `json:",inline"`
// Subscribers is populated with the statuses of each of the Channelable's subscribers.
eventingduck.SubscribableTypeStatus `json:",inline"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// InMemoryChannelList is a collection of in-memory channels.
type InMemoryChannelList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty"`
Items []InMemoryChannel `json:"items"`
}
// GetGroupVersionKind returns GroupVersionKind for InMemoryChannels
func (imc *InMemoryChannel) GetGroupVersionKind() schema.GroupVersionKind {
return SchemeGroupVersion.WithKind("InMemoryChannel")
}
// GetUntypedSpec returns the spec of the InMemoryChannel.
func (i *InMemoryChannel) GetUntypedSpec() interface{} {
return i.Spec
}

View File

@ -0,0 +1,44 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"context"
"fmt"
"knative.dev/pkg/apis"
)
func (imc *InMemoryChannel) Validate(ctx context.Context) *apis.FieldError {
return imc.Spec.Validate(ctx).ViaField("spec")
}
func (imcs *InMemoryChannelSpec) Validate(ctx context.Context) *apis.FieldError {
var errs *apis.FieldError
if imcs.Subscribable != nil {
for i, subscriber := range imcs.Subscribable.Subscribers {
if subscriber.ReplyURI == "" && subscriber.SubscriberURI == "" {
fe := apis.ErrMissingField("replyURI", "subscriberURI")
fe.Details = "expected at least one of, got none"
errs = errs.Also(fe.ViaField(fmt.Sprintf("subscriber[%d]", i)).ViaField("subscribable"))
}
}
}
return errs
}

View File

@ -0,0 +1,37 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"context"
eventingduckv1alpha1 "knative.dev/eventing/pkg/apis/duck/v1alpha1"
)
func (s *Parallel) SetDefaults(ctx context.Context) {
if s != nil && s.Spec.ChannelTemplate == nil {
// The singleton may not have been set, if so ignore it and validation will reject the
// Channel.
if cd := eventingduckv1alpha1.ChannelDefaulterSingleton; cd != nil {
channelTemplate := cd.GetDefault(s.Namespace)
s.Spec.ChannelTemplate = channelTemplate
}
}
s.Spec.SetDefaults(ctx)
}
func (ss *ParallelSpec) SetDefaults(ctx context.Context) {}

View File

@ -0,0 +1,237 @@
/*
* Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"time"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
duckv1alpha1 "knative.dev/eventing/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/apis"
pkgduckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1"
)
var pParallelCondSet = apis.NewLivingConditionSet(ParallelConditionReady, ParallelConditionChannelsReady, ParallelConditionSubscriptionsReady, ParallelConditionAddressable)
const (
// StatusConditionTypeDeprecated is the status.conditions.type used to provide deprecation
// warnings.
StatusConditionTypeDeprecated = "Deprecated"
)
const (
// ParallelConditionReady has status True when all subconditions below have been set to True.
ParallelConditionReady = apis.ConditionReady
// ParallelConditionChannelsReady has status True when all the channels created as part of
// this parallel are ready.
ParallelConditionChannelsReady apis.ConditionType = "ChannelsReady"
// ParallelConditionSubscriptionsReady has status True when all the subscriptions created as part of
// this parallel are ready.
ParallelConditionSubscriptionsReady apis.ConditionType = "SubscriptionsReady"
// ParallelConditionAddressable has status true when this Parallel meets
// the Addressable contract and has a non-empty hostname.
ParallelConditionAddressable apis.ConditionType = "Addressable"
)
// GetCondition returns the condition currently associated with the given type, or nil.
func (ps *ParallelStatus) GetCondition(t apis.ConditionType) *apis.Condition {
return pParallelCondSet.Manage(ps).GetCondition(t)
}
// IsReady returns true if the resource is ready overall.
func (ps *ParallelStatus) IsReady() bool {
return pParallelCondSet.Manage(ps).IsHappy()
}
// InitializeConditions sets relevant unset conditions to Unknown state.
func (ps *ParallelStatus) InitializeConditions() {
pParallelCondSet.Manage(ps).InitializeConditions()
}
// PropagateSubscriptionStatuses sets the ParallelConditionSubscriptionsReady based on
// the status of the incoming subscriptions.
func (ps *ParallelStatus) PropagateSubscriptionStatuses(filterSubscriptions []*Subscription, subscriptions []*Subscription) {
if ps.BranchStatuses == nil {
ps.BranchStatuses = make([]ParallelBranchStatus, len(subscriptions))
}
allReady := true
// If there are no subscriptions, treat that as a False branch. Could go either way, but this seems right.
if len(subscriptions) == 0 {
allReady = false
}
for i, s := range subscriptions {
ps.BranchStatuses[i].SubscriptionStatus = ParallelSubscriptionStatus{
Subscription: corev1.ObjectReference{
APIVersion: s.APIVersion,
Kind: s.Kind,
Name: s.Name,
Namespace: s.Namespace,
},
}
readyCondition := s.Status.GetCondition(SubscriptionConditionReady)
if readyCondition != nil {
ps.BranchStatuses[i].SubscriptionStatus.ReadyCondition = *readyCondition
if readyCondition.Status != corev1.ConditionTrue {
allReady = false
}
} else {
allReady = false
}
fs := filterSubscriptions[i]
ps.BranchStatuses[i].FilterSubscriptionStatus = ParallelSubscriptionStatus{
Subscription: corev1.ObjectReference{
APIVersion: fs.APIVersion,
Kind: fs.Kind,
Name: fs.Name,
Namespace: fs.Namespace,
},
}
readyCondition = fs.Status.GetCondition(SubscriptionConditionReady)
if readyCondition != nil {
ps.BranchStatuses[i].FilterSubscriptionStatus.ReadyCondition = *readyCondition
if readyCondition.Status != corev1.ConditionTrue {
allReady = false
}
} else {
allReady = false
}
}
if allReady {
pParallelCondSet.Manage(ps).MarkTrue(ParallelConditionSubscriptionsReady)
} else {
ps.MarkSubscriptionsNotReady("SubscriptionsNotReady", "Subscriptions are not ready yet, or there are none")
}
}
// PropagateChannelStatuses sets the ChannelStatuses and ParallelConditionChannelsReady based on the
// status of the incoming channels.
func (ps *ParallelStatus) PropagateChannelStatuses(ingressChannel *duckv1alpha1.Channelable, channels []*duckv1alpha1.Channelable) {
if ps.BranchStatuses == nil {
ps.BranchStatuses = make([]ParallelBranchStatus, len(channels))
}
allReady := true
ps.IngressChannelStatus.Channel = corev1.ObjectReference{
APIVersion: ingressChannel.APIVersion,
Kind: ingressChannel.Kind,
Name: ingressChannel.Name,
Namespace: ingressChannel.Namespace,
}
address := ingressChannel.Status.AddressStatus.Address
if address != nil {
ps.IngressChannelStatus.ReadyCondition = apis.Condition{Type: apis.ConditionReady, Status: corev1.ConditionTrue}
} else {
ps.IngressChannelStatus.ReadyCondition = apis.Condition{Type: apis.ConditionReady, Status: corev1.ConditionFalse, Reason: "NotAddressable", Message: "Channel is not addressable"}
allReady = false
}
// Propagate ingress channel address to Parallel
ps.setAddress(address)
for i, c := range channels {
ps.BranchStatuses[i].FilterChannelStatus = ParallelChannelStatus{
Channel: corev1.ObjectReference{
APIVersion: c.APIVersion,
Kind: c.Kind,
Name: c.Name,
Namespace: c.Namespace,
},
}
// TODO: Once the addressable has a real status to dig through, use that here instead of
// addressable, because it might be addressable but not ready.
address := c.Status.AddressStatus.Address
if address != nil {
ps.BranchStatuses[i].FilterChannelStatus.ReadyCondition = apis.Condition{Type: apis.ConditionReady, Status: corev1.ConditionTrue}
} else {
ps.BranchStatuses[i].FilterChannelStatus.ReadyCondition = apis.Condition{Type: apis.ConditionReady, Status: corev1.ConditionFalse, Reason: "NotAddressable", Message: "Channel is not addressable"}
allReady = false
}
}
if allReady {
pParallelCondSet.Manage(ps).MarkTrue(ParallelConditionChannelsReady)
} else {
ps.MarkChannelsNotReady("ChannelsNotReady", "Channels are not ready yet, or there are none")
}
}
func (ps *ParallelStatus) MarkChannelsNotReady(reason, messageFormat string, messageA ...interface{}) {
pParallelCondSet.Manage(ps).MarkFalse(ParallelConditionChannelsReady, reason, messageFormat, messageA...)
}
func (ps *ParallelStatus) MarkSubscriptionsNotReady(reason, messageFormat string, messageA ...interface{}) {
pParallelCondSet.Manage(ps).MarkFalse(ParallelConditionSubscriptionsReady, reason, messageFormat, messageA...)
}
func (ps *ParallelStatus) MarkAddressableNotReady(reason, messageFormat string, messageA ...interface{}) {
pParallelCondSet.Manage(ps).MarkFalse(ParallelConditionAddressable, reason, messageFormat, messageA...)
}
func (ps *ParallelStatus) setAddress(address *pkgduckv1alpha1.Addressable) {
ps.Address = address
if address == nil {
pParallelCondSet.Manage(ps).MarkFalse(ParallelConditionAddressable, "emptyHostname", "hostname is the empty string")
return
}
if address.URL != nil || address.Hostname != "" {
pParallelCondSet.Manage(ps).MarkTrue(ParallelConditionAddressable)
} else {
ps.Address.Hostname = ""
ps.Address.URL = nil
pParallelCondSet.Manage(ps).MarkFalse(ParallelConditionAddressable, "emptyHostname", "hostname is the empty string")
}
}
// MarkDeprecated adds a warning condition that this object's spec is using deprecated fields
// and will stop working in the future. Note that this does not affect the Ready condition.
func (ps *ParallelStatus) MarkDestinationDeprecatedRef(reason, msg string) {
dc := apis.Condition{
Type: StatusConditionTypeDeprecated,
Reason: reason,
Status: corev1.ConditionTrue,
Severity: apis.ConditionSeverityWarning,
Message: msg,
LastTransitionTime: apis.VolatileTime{Inner: metav1.NewTime(time.Now())},
}
for i, c := range ps.Conditions {
if c.Type == dc.Type {
ps.Conditions[i] = dc
return
}
}
ps.Conditions = append(ps.Conditions, dc)
}
// ClearDeprecated removes the StatusConditionTypeDeprecated warning condition. Note that this does not
// affect the Ready condition.
func (ps *ParallelStatus) ClearDeprecated() {
conds := make([]apis.Condition, 0, len(ps.Conditions))
for _, c := range ps.Conditions {
if c.Type != StatusConditionTypeDeprecated {
conds = append(conds, c)
}
}
ps.Conditions = conds
}

View File

@ -0,0 +1,163 @@
/*
* Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
eventingduckv1alpha1 "knative.dev/eventing/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
duckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/apis/v1alpha1"
"knative.dev/pkg/kmeta"
"knative.dev/pkg/webhook"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Parallel defines conditional branches that will be wired in
// series through Channels and Subscriptions.
type Parallel struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec defines the desired state of the Parallel.
Spec ParallelSpec `json:"spec,omitempty"`
// Status represents the current state of the Parallel. This data may be out of
// date.
// +optional
Status ParallelStatus `json:"status,omitempty"`
}
var (
// Check that Parallel can be validated and defaulted.
_ apis.Validatable = (*Parallel)(nil)
_ apis.Defaultable = (*Parallel)(nil)
// Check that Parallel can return its spec untyped.
_ apis.HasSpec = (*Parallel)(nil)
// TODO: make appropriate fields immutable.
//_ apis.Immutable = (*Parallel)(nil)
_ runtime.Object = (*Parallel)(nil)
_ webhook.GenericCRD = (*Parallel)(nil)
// Check that we can create OwnerReferences to a Parallel.
_ kmeta.OwnerRefable = (*Parallel)(nil)
)
type ParallelSpec struct {
// Branches is the list of Filter/Subscribers pairs.
Branches []ParallelBranch `json:"branches"`
// ChannelTemplate specifies which Channel CRD to use. If left unspecified, it is set to the default Channel CRD
// for the namespace (or cluster, in case there are no defaults for the namespace).
// +optional
ChannelTemplate *eventingduckv1alpha1.ChannelTemplateSpec `json:"channelTemplate"`
// Reply is a Reference to where the result of a case Subscriber gets sent to
// when the case does not have a Reply
// +optional
Reply *v1alpha1.Destination `json:"reply,omitempty"`
}
type ParallelBranch struct {
// Filter is the expression guarding the branch
Filter *v1alpha1.Destination `json:"filter,omitempty"`
// Subscriber receiving the event when the filter passes
Subscriber v1alpha1.Destination `json:"subscriber"`
// Reply is a Reference to where the result of Subscriber of this case gets sent to.
// If not specified, sent the result to the Parallel Reply
// +optional
Reply *v1alpha1.Destination `json:"reply,omitempty"`
}
// ParallelStatus represents the current state of a Parallel.
type ParallelStatus struct {
// inherits duck/v1 Status, which currently provides:
// * ObservedGeneration - the 'Generation' of the Service that was last processed by the controller.
// * Conditions - the latest available observations of a resource's current state.
duckv1.Status `json:",inline"`
// IngressChannelStatus corresponds to the ingress channel status.
IngressChannelStatus ParallelChannelStatus `json:"ingressChannelStatus"`
// BranchStatuses is an array of corresponding to branch statuses.
// Matches the Spec.Branches array in the order.
BranchStatuses []ParallelBranchStatus `json:"branchStatuses"`
// AddressStatus is the starting point to this Parallel. Sending to this
// will target the first subscriber.
// It generally has the form {channel}.{namespace}.svc.{cluster domain name}
duckv1alpha1.AddressStatus `json:",inline"`
}
// ParallelBranchStatus represents the current state of a Parallel branch
type ParallelBranchStatus struct {
// FilterSubscriptionStatus corresponds to the filter subscription status.
FilterSubscriptionStatus ParallelSubscriptionStatus `json:"filterSubscriptionStatus"`
// FilterChannelStatus corresponds to the filter channel status.
FilterChannelStatus ParallelChannelStatus `json:"filterChannelStatus"`
// SubscriptionStatus corresponds to the subscriber subscription status.
SubscriptionStatus ParallelSubscriptionStatus `json:"subscriberSubscriptionStatus"`
}
type ParallelChannelStatus struct {
// Channel is the reference to the underlying channel.
Channel corev1.ObjectReference `json:"channel"`
// ReadyCondition indicates whether the Channel is ready or not.
ReadyCondition apis.Condition `json:"ready"`
}
type ParallelSubscriptionStatus struct {
// Subscription is the reference to the underlying Subscription.
Subscription corev1.ObjectReference `json:"subscription"`
// ReadyCondition indicates whether the Subscription is ready or not.
ReadyCondition apis.Condition `json:"ready"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ParallelList is a collection of Parallels.
type ParallelList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty"`
Items []Parallel `json:"items"`
}
// GetGroupVersionKind returns GroupVersionKind for Parallel
func (p *Parallel) GetGroupVersionKind() schema.GroupVersionKind {
return SchemeGroupVersion.WithKind("Parallel")
}
// GetUntypedSpec returns the spec of the Parallel.
func (p *Parallel) GetUntypedSpec() interface{} {
return p.Spec
}

View File

@ -0,0 +1,68 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"context"
"knative.dev/pkg/apis"
)
func (p *Parallel) Validate(ctx context.Context) *apis.FieldError {
return p.Spec.Validate(ctx).ViaField("spec")
}
func (ps *ParallelSpec) Validate(ctx context.Context) *apis.FieldError {
var errs *apis.FieldError
if len(ps.Branches) == 0 {
errs = errs.Also(apis.ErrMissingField("branches"))
}
for i, s := range ps.Branches {
if err := s.Filter.ValidateDisallowDeprecated(ctx); err != nil {
errs = errs.Also(apis.ErrInvalidArrayValue(s, "branches.filter", i))
}
if e := s.Subscriber.ValidateDisallowDeprecated(ctx); e != nil {
errs = errs.Also(apis.ErrInvalidArrayValue(s, "branches.subscriber", i))
}
if e := s.Reply.Validate(ctx); e != nil {
errs = errs.Also(apis.ErrInvalidArrayValue(s, "branches.reply", i))
}
}
if ps.ChannelTemplate == nil {
errs = errs.Also(apis.ErrMissingField("channelTemplate"))
return errs
}
if len(ps.ChannelTemplate.APIVersion) == 0 {
errs = errs.Also(apis.ErrMissingField("channelTemplate.apiVersion"))
}
if len(ps.ChannelTemplate.Kind) == 0 {
errs = errs.Also(apis.ErrMissingField("channelTemplate.kind"))
}
if err := ps.Reply.Validate(ctx); err != nil {
errs = errs.Also(err.ViaField("reply"))
}
return errs
}

View File

@ -0,0 +1,61 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"knative.dev/eventing/pkg/apis/messaging"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: messaging.GroupName, Version: "v1alpha1"}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&InMemoryChannel{},
&InMemoryChannelList{},
&Sequence{},
&SequenceList{},
&Subscription{},
&SubscriptionList{},
&Channel{},
&ChannelList{},
&Parallel{},
&ParallelList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View File

@ -0,0 +1,37 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"context"
eventingduckv1alpha1 "knative.dev/eventing/pkg/apis/duck/v1alpha1"
)
func (s *Sequence) SetDefaults(ctx context.Context) {
if s != nil && s.Spec.ChannelTemplate == nil {
// The singleton may not have been set, if so ignore it and validation will reject the
// Channel.
if cd := eventingduckv1alpha1.ChannelDefaulterSingleton; cd != nil {
channelTemplate := cd.GetDefault(s.Namespace)
s.Spec.ChannelTemplate = channelTemplate
}
}
s.Spec.SetDefaults(ctx)
}
func (ss *SequenceSpec) SetDefaults(ctx context.Context) {}

View File

@ -0,0 +1,199 @@
/*
* Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"time"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
duckv1alpha1 "knative.dev/eventing/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/apis"
pkgduckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1"
)
var pCondSet = apis.NewLivingConditionSet(SequenceConditionReady, SequenceConditionChannelsReady, SequenceConditionSubscriptionsReady, SequenceConditionAddressable)
const (
// SequenceConditionReady has status True when all subconditions below have been set to True.
SequenceConditionReady = apis.ConditionReady
// SequenceChannelsReady has status True when all the channels created as part of
// this sequence are ready.
SequenceConditionChannelsReady apis.ConditionType = "ChannelsReady"
// SequenceSubscriptionsReady has status True when all the subscriptions created as part of
// this sequence are ready.
SequenceConditionSubscriptionsReady apis.ConditionType = "SubscriptionsReady"
// SequenceConditionAddressable has status true when this Sequence meets
// the Addressable contract and has a non-empty hostname.
SequenceConditionAddressable apis.ConditionType = "Addressable"
)
// GetCondition returns the condition currently associated with the given type, or nil.
func (ss *SequenceStatus) GetCondition(t apis.ConditionType) *apis.Condition {
return pCondSet.Manage(ss).GetCondition(t)
}
// IsReady returns true if the resource is ready overall.
func (ss *SequenceStatus) IsReady() bool {
return pCondSet.Manage(ss).IsHappy()
}
// InitializeConditions sets relevant unset conditions to Unknown state.
func (ss *SequenceStatus) InitializeConditions() {
pCondSet.Manage(ss).InitializeConditions()
}
// PropagateSubscriptionStatuses sets the SubscriptionStatuses and SequenceConditionSubscriptionsReady based on
// the status of the incoming subscriptions.
func (ss *SequenceStatus) PropagateSubscriptionStatuses(subscriptions []*Subscription) {
ss.SubscriptionStatuses = make([]SequenceSubscriptionStatus, len(subscriptions))
allReady := true
// If there are no subscriptions, treat that as a False case. Could go either way, but this seems right.
if len(subscriptions) == 0 {
allReady = false
}
for i, s := range subscriptions {
ss.SubscriptionStatuses[i] = SequenceSubscriptionStatus{
Subscription: corev1.ObjectReference{
APIVersion: s.APIVersion,
Kind: s.Kind,
Name: s.Name,
Namespace: s.Namespace,
},
}
readyCondition := s.Status.GetCondition(SubscriptionConditionReady)
if readyCondition != nil {
ss.SubscriptionStatuses[i].ReadyCondition = *readyCondition
if readyCondition.Status != corev1.ConditionTrue {
allReady = false
}
} else {
allReady = false
}
}
if allReady {
pCondSet.Manage(ss).MarkTrue(SequenceConditionSubscriptionsReady)
} else {
ss.MarkSubscriptionsNotReady("SubscriptionsNotReady", "Subscriptions are not ready yet, or there are none")
}
}
// PropagateChannelStatuses sets the ChannelStatuses and SequenceConditionChannelsReady based on the
// status of the incoming channels.
func (ss *SequenceStatus) PropagateChannelStatuses(channels []*duckv1alpha1.Channelable) {
ss.ChannelStatuses = make([]SequenceChannelStatus, len(channels))
allReady := true
// If there are no channels, treat that as a False case. Could go either way, but this seems right.
if len(channels) == 0 {
allReady = false
}
for i, c := range channels {
ss.ChannelStatuses[i] = SequenceChannelStatus{
Channel: corev1.ObjectReference{
APIVersion: c.APIVersion,
Kind: c.Kind,
Name: c.Name,
Namespace: c.Namespace,
},
}
// TODO: Once the addressable has a real status to dig through, use that here instead of
// addressable, because it might be addressable but not ready.
address := c.Status.AddressStatus.Address
if address != nil {
ss.ChannelStatuses[i].ReadyCondition = apis.Condition{Type: apis.ConditionReady, Status: corev1.ConditionTrue}
} else {
ss.ChannelStatuses[i].ReadyCondition = apis.Condition{Type: apis.ConditionReady, Status: corev1.ConditionFalse, Reason: "NotAddressable", Message: "Channel is not addressable"}
allReady = false
}
// Mark the Sequence address as the Address of the first channel.
if i == 0 {
ss.setAddress(address)
}
}
if allReady {
pCondSet.Manage(ss).MarkTrue(SequenceConditionChannelsReady)
} else {
ss.MarkChannelsNotReady("ChannelsNotReady", "Channels are not ready yet, or there are none")
}
}
func (ss *SequenceStatus) MarkChannelsNotReady(reason, messageFormat string, messageA ...interface{}) {
pCondSet.Manage(ss).MarkFalse(SequenceConditionChannelsReady, reason, messageFormat, messageA...)
}
func (ss *SequenceStatus) MarkSubscriptionsNotReady(reason, messageFormat string, messageA ...interface{}) {
pCondSet.Manage(ss).MarkFalse(SequenceConditionSubscriptionsReady, reason, messageFormat, messageA...)
}
func (ss *SequenceStatus) MarkAddressableNotReady(reason, messageFormat string, messageA ...interface{}) {
pCondSet.Manage(ss).MarkFalse(SequenceConditionAddressable, reason, messageFormat, messageA...)
}
func (ss *SequenceStatus) setAddress(address *pkgduckv1alpha1.Addressable) {
ss.Address = address
if address == nil {
pCondSet.Manage(ss).MarkFalse(SequenceConditionAddressable, "emptyHostname", "hostname is the empty string")
return
}
if address.URL != nil || address.Hostname != "" {
pCondSet.Manage(ss).MarkTrue(SequenceConditionAddressable)
} else {
ss.Address.Hostname = ""
ss.Address.URL = nil
pCondSet.Manage(ss).MarkFalse(SequenceConditionAddressable, "emptyHostname", "hostname is the empty string")
}
}
// MarkDeprecated adds a warning condition that this object's spec is using deprecated fields
// and will stop working in the future. Note that this does not affect the Ready condition.
func (ss *SequenceStatus) MarkDestinationDeprecatedRef(reason, msg string) {
dc := apis.Condition{
Type: StatusConditionTypeDeprecated,
Reason: reason,
Status: corev1.ConditionTrue,
Severity: apis.ConditionSeverityWarning,
Message: msg,
LastTransitionTime: apis.VolatileTime{Inner: metav1.NewTime(time.Now())},
}
for i, c := range ss.Conditions {
if c.Type == dc.Type {
ss.Conditions[i] = dc
return
}
}
ss.Conditions = append(ss.Conditions, dc)
}
// ClearDeprecated removes the StatusConditionTypeDeprecated warning condition. Note that this does not
// affect the Ready condition.
func (ss *SequenceStatus) ClearDeprecated() {
conds := make([]apis.Condition, 0, len(ss.Conditions))
for _, c := range ss.Conditions {
if c.Type != StatusConditionTypeDeprecated {
conds = append(conds, c)
}
}
ss.Conditions = conds
}

View File

@ -0,0 +1,139 @@
/*
* Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
eventingduckv1alpha1 "knative.dev/eventing/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
duckv1alpha1 "knative.dev/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/apis/v1alpha1"
"knative.dev/pkg/kmeta"
"knative.dev/pkg/webhook"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Sequence defines a sequence of Subscribers that will be wired in
// series through Channels and Subscriptions.
type Sequence struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec defines the desired state of the Sequence.
Spec SequenceSpec `json:"spec,omitempty"`
// Status represents the current state of the Sequence. This data may be out of
// date.
// +optional
Status SequenceStatus `json:"status,omitempty"`
}
var (
// Check that Sequence can be validated and defaulted.
_ apis.Validatable = (*Sequence)(nil)
_ apis.Defaultable = (*Sequence)(nil)
// Check that Sequence can return its spec untyped.
_ apis.HasSpec = (*Sequence)(nil)
// TODO: make appropriate fields immutable.
//_ apis.Immutable = (*Sequence)(nil)
_ runtime.Object = (*Sequence)(nil)
_ webhook.GenericCRD = (*Sequence)(nil)
// Check that we can create OwnerReferences to a Sequence.
_ kmeta.OwnerRefable = (*Sequence)(nil)
)
type SequenceSpec struct {
// Steps is the list of Destinations (processors / functions) that will be called in the order
// provided.
Steps []v1alpha1.Destination `json:"steps"`
// ChannelTemplate specifies which Channel CRD to use. If left unspecified, it is set to the default Channel CRD
// for the namespace (or cluster, in case there are no defaults for the namespace).
// +optional
ChannelTemplate *eventingduckv1alpha1.ChannelTemplateSpec `json:"channelTemplate,omitempty"`
// Reply is a Reference to where the result of the last Subscriber gets sent to.
// +optional
Reply *v1alpha1.Destination `json:"reply,omitempty"`
}
type SequenceChannelStatus struct {
// Channel is the reference to the underlying channel.
Channel corev1.ObjectReference `json:"channel"`
// ReadyCondition indicates whether the Channel is ready or not.
ReadyCondition apis.Condition `json:"ready"`
}
type SequenceSubscriptionStatus struct {
// Subscription is the reference to the underlying Subscription.
Subscription corev1.ObjectReference `json:"subscription"`
// ReadyCondition indicates whether the Subscription is ready or not.
ReadyCondition apis.Condition `json:"ready"`
}
// SequenceStatus represents the current state of a Sequence.
type SequenceStatus struct {
// inherits duck/v1 Status, which currently provides:
// * ObservedGeneration - the 'Generation' of the Service that was last processed by the controller.
// * Conditions - the latest available observations of a resource's current state.
duckv1.Status `json:",inline"`
// SubscriptionStatuses is an array of corresponding Subscription statuses.
// Matches the Spec.Steps array in the order.
SubscriptionStatuses []SequenceSubscriptionStatus `json:"subscriptionStatuses"`
// ChannelStatuses is an array of corresponding Channel statuses.
// Matches the Spec.Steps array in the order.
ChannelStatuses []SequenceChannelStatus `json:"channelStatuses"`
// AddressStatus is the starting point to this Sequence. Sending to this
// will target the first subscriber.
// It generally has the form {channel}.{namespace}.svc.{cluster domain name}
duckv1alpha1.AddressStatus `json:",inline"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// SequenceList is a collection of Sequences.
type SequenceList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty"`
Items []Sequence `json:"items"`
}
// GetGroupVersionKind returns GroupVersionKind for InMemoryChannels
func (p *Sequence) GetGroupVersionKind() schema.GroupVersionKind {
return SchemeGroupVersion.WithKind("Sequence")
}
// GetUntypedSpec returns the spec of the Sequence.
func (s *Sequence) GetUntypedSpec() interface{} {
return s.Spec
}

View File

@ -0,0 +1,60 @@
/*
Copyright 2019 The Knative 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.
*/
package v1alpha1
import (
"context"
"knative.dev/pkg/apis"
)
func (p *Sequence) Validate(ctx context.Context) *apis.FieldError {
return p.Spec.Validate(ctx).ViaField("spec")
}
func (ps *SequenceSpec) Validate(ctx context.Context) *apis.FieldError {
var errs *apis.FieldError
if len(ps.Steps) == 0 {
errs = errs.Also(apis.ErrMissingField("steps"))
}
for i, s := range ps.Steps {
if e := s.ValidateDisallowDeprecated(ctx); e != nil {
errs = errs.Also(apis.ErrInvalidArrayValue(s, "steps", i))
}
}
if ps.ChannelTemplate == nil {
errs = errs.Also(apis.ErrMissingField("channelTemplate"))
return errs
}
if len(ps.ChannelTemplate.APIVersion) == 0 {
errs = errs.Also(apis.ErrMissingField("channelTemplate.apiVersion"))
}
if len(ps.ChannelTemplate.Kind) == 0 {
errs = errs.Also(apis.ErrMissingField("channelTemplate.kind"))
}
if err := ps.Reply.Validate(ctx); err != nil {
errs = errs.Also(err.ViaField("reply"))
}
return errs
}

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