├── .gitignore ├── docs ├── result-slack.png ├── result-telegram.png └── result-workchat.png ├── util ├── const.go └── notify.go ├── deployment ├── rbac │ ├── service-account.yml │ ├── clusterrolebinding.yml │ └── clusterrole.yml ├── app-deploy.yaml └── config.yaml ├── model ├── std.go ├── helper.go ├── slack.go └── workplace.go ├── go.mod ├── event-handler └── handler.go ├── Dockerfile ├── liveness-check └── checker.go ├── README.md ├── config └── config.go ├── main.go ├── config.example.yaml ├── go.sum └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | vendor/ 3 | 4 | # Binary File 5 | siera-kube-watch 6 | 7 | /**/config.yaml -------------------------------------------------------------------------------- /docs/result-slack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/warungpintar/siera-kube-watch/HEAD/docs/result-slack.png -------------------------------------------------------------------------------- /docs/result-telegram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/warungpintar/siera-kube-watch/HEAD/docs/result-telegram.png -------------------------------------------------------------------------------- /docs/result-workchat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/warungpintar/siera-kube-watch/HEAD/docs/result-workchat.png -------------------------------------------------------------------------------- /util/const.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | const ( 4 | // Event type 5 | WARNING = "Warning" 6 | NORMAL = "Normal" 7 | ) 8 | -------------------------------------------------------------------------------- /deployment/rbac/service-account.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | app: siera-watch 6 | name: siera-watch 7 | namespace: hack-tribe -------------------------------------------------------------------------------- /deployment/rbac/clusterrolebinding.yml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1beta1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app: siera-watch 6 | name: siera-watch 7 | namespace: hack-tribe 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: ClusterRole 11 | name: siera-watch 12 | subjects: 13 | - kind: ServiceAccount 14 | name: siera-watch 15 | namespace: hack-tribe -------------------------------------------------------------------------------- /model/std.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "encoding/json" 4 | 5 | type StdModel struct { 6 | Text string `json:"text"` 7 | } 8 | 9 | func (model *StdModel) New(text string) { 10 | model.Text = text 11 | } 12 | 13 | func (model *StdModel) Send(url string) (err error) { 14 | buffer, err := json.Marshal(model) 15 | if err != nil { 16 | return 17 | } 18 | 19 | err = postRequest(buffer, url) 20 | return 21 | } 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/warungpintar/siera-kube-watch 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/imdario/mergo v0.3.9 // indirect 7 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect 8 | golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect 9 | gopkg.in/yaml.v2 v2.2.4 10 | k8s.io/api v0.15.9 11 | k8s.io/apimachinery v0.15.9 12 | k8s.io/client-go v0.15.9 13 | k8s.io/utils v0.0.0-20200414100711-2df71ebbae66 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /event-handler/handler.go: -------------------------------------------------------------------------------- 1 | package event_handler 2 | 3 | import ( 4 | "github.com/warungpintar/siera-kube-watch/util" 5 | corev1 "k8s.io/api/core/v1" 6 | ) 7 | 8 | func OnAddEvent(obj interface{}) { 9 | event := obj.(*corev1.Event) 10 | util.NotifyEvent(event) 11 | } 12 | 13 | func OnDeleteEvent(obj interface{}) { 14 | event := obj.(*corev1.Event) 15 | util.NotifyEvent(event) 16 | } 17 | 18 | func OnUpdateEvent(oldObj, newObj interface{}) { 19 | event := newObj.(*corev1.Event) 20 | util.NotifyEvent(event) 21 | } 22 | -------------------------------------------------------------------------------- /model/helper.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "net/http" 8 | ) 9 | 10 | func postRequest(buffer []byte, url string) (err error) { 11 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(buffer)) 12 | req.Header.Set("Content-Type", "application/json") 13 | 14 | client := &http.Client{} 15 | resp, err := client.Do(req) 16 | if err != nil { 17 | return 18 | } 19 | 20 | defer resp.Body.Close() 21 | if resp.StatusCode != http.StatusOK { 22 | return errors.New(fmt.Sprintf("Failed to post event to %s with status code %d", url, resp.StatusCode)) 23 | } 24 | 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /model/slack.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/warungpintar/siera-kube-watch/config" 8 | ) 9 | 10 | type SlackModel struct { 11 | Text string `json:"text"` 12 | Channel string `json:"channel"` 13 | Username string `json:"username"` 14 | } 15 | 16 | func (model *SlackModel) New(text string) { 17 | model.Text = text 18 | model.Channel = config.GlobalConfig.Slack.Channel 19 | model.Username = config.GlobalConfig.Slack.Username 20 | } 21 | 22 | func (model *SlackModel) Send(url string) (err error) { 23 | buffer, err := json.Marshal(model) 24 | fmt.Println(string(buffer)) 25 | if err != nil { 26 | return 27 | } 28 | 29 | err = postRequest(buffer, url) 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.14-alpine AS builder 2 | WORKDIR /usr/src/app 3 | 4 | ARG SSH_PRIVATE_KEY 5 | ENV GO111MODULE=on 6 | 7 | COPY . . 8 | 9 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o /usr/src/app/bin/main /usr/src/app/main.go 10 | 11 | # Main Image 12 | FROM alpine:latest 13 | 14 | RUN apk update \ 15 | && apk --no-cache add \ 16 | ca-certificates openssl && update-ca-certificates 17 | 18 | # Setting timezone 19 | ENV TZ=Asia/Jakarta 20 | RUN apk add -U tzdata 21 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 22 | 23 | # Setting folder workdir 24 | WORKDIR /usr/src/app 25 | RUN mkdir -p etc/siera-kube-watch 26 | 27 | COPY --from=builder /usr/src/app/bin/main . 28 | 29 | CMD ["./main"] 30 | -------------------------------------------------------------------------------- /liveness-check/checker.go: -------------------------------------------------------------------------------- 1 | package liveness_check 2 | 3 | import ( 4 | "log" 5 | "time" 6 | 7 | "github.com/warungpintar/siera-kube-watch/config" 8 | "github.com/warungpintar/siera-kube-watch/util" 9 | ) 10 | 11 | func Ping() { 12 | if config.GlobalConfig.Livenesscheck.Enabled { 13 | 14 | interval, err := time.ParseDuration(config.GlobalConfig.Livenesscheck.Interval) 15 | 16 | if err != nil { 17 | log.Fatalf("Error parsing livness check interval value: %v", err) 18 | } 19 | 20 | log.Printf("Liveness check enabled with interval: %s", interval) 21 | 22 | intervalInSeconds := interval.Seconds() 23 | for { 24 | util.PostEvent("This is a dead man's switch mechanism to ensure alert pipeline is working.") 25 | time.Sleep(time.Duration(intervalInSeconds) * time.Second) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /model/workplace.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | type WorkplaceModel struct { 8 | Recipient *RecipientModel `json:"recipient"` 9 | Message *MessageModel `json:"message"` 10 | } 11 | 12 | type RecipientModel struct { 13 | ThreadKey string `json:"thread_key"` 14 | } 15 | 16 | type MessageModel struct { 17 | Text string `json:"text"` 18 | } 19 | 20 | func (model *WorkplaceModel) New(text string, threadKey string) { 21 | model.Recipient = &RecipientModel{ 22 | ThreadKey: threadKey, 23 | } 24 | 25 | model.Message = &MessageModel{ 26 | Text: text, 27 | } 28 | } 29 | 30 | func (model *WorkplaceModel) Send(url string) (err error) { 31 | buffer, err := json.Marshal(model) 32 | if err != nil { 33 | return 34 | } 35 | 36 | err = postRequest(buffer, url) 37 | return 38 | } 39 | -------------------------------------------------------------------------------- /deployment/app-deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: siera-watch 5 | namespace: hack-tribe 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: siera-watch 10 | replicas: 1 11 | template: 12 | metadata: 13 | labels: 14 | app: siera-watch 15 | spec: 16 | serviceAccountName: siera-watch 17 | containers: 18 | - image: warungpintar/siera-kube-watch:v1.0.0 19 | name: siera-watch 20 | imagePullPolicy: Always 21 | volumeMounts: 22 | - name: siera-watch-config 23 | mountPath: /usr/src/app/etc/siera-kube-watch 24 | 25 | imagePullSecrets: 26 | - name: gcr-json-key 27 | volumes: 28 | - name: siera-watch-config 29 | secret: 30 | secretName: siera-watch-config 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # siera-kube-watch 2 | **siera kubewatch** is a Kubernetes events watcher that aims to publish incident (unexpected event) as a notification through webhooks. 3 | 4 | Supported webhooks: 5 | - slack 6 | - telegram 7 | - workplace chat 8 | - webhook 9 | 10 | It also supports liveness check (dead man's switch) functionality for alerting on availability of alerting pipeline. 11 | ``` 12 | livenesscheck: 13 | enabled: true 14 | interval: "1m" 15 | ``` 16 | 17 | # Build and Run 18 | 19 | ## Install Dependencies 20 | ``` 21 | $ go mod download 22 | ``` 23 | 24 | ## Build 25 | ``` 26 | $ go build 27 | ``` 28 | 29 | ## Run 30 | ``` 31 | $ ./siera-kube-watch 32 | ``` 33 | 34 | # Configuration 35 | Copy the `config.example.yaml` as `config.yaml` and setup your own configuration as needed. 36 | 37 | # Helm 38 | 39 | We provide a helm chart for easy installation https://github.com/warungpintar/charts/tree/master/warpin/siera-kube-watch 40 | 41 | ## Result workplace chat 42 | ![GitHub Logo](docs/result-workchat.png) 43 | 44 | ## Result slack 45 | ![GitHub Logo](docs/result-slack.png) 46 | 47 | ## Result telegram 48 | ![GitHub Logo](docs/result-telegram.png) 49 | 50 | docker image size 35.2MB -------------------------------------------------------------------------------- /deployment/rbac/clusterrole.yml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | labels: 5 | app: siera-watch 6 | name: siera-watch 7 | namespace: hack-tribe 8 | rules: 9 | - apiGroups: 10 | - "" 11 | resources: 12 | - pods 13 | - namespaces 14 | - services 15 | - deployments 16 | - replicationcontrollers 17 | - replicasets 18 | - daemonsets 19 | - persistentvolumes 20 | - events 21 | verbs: 22 | - list 23 | - watch 24 | - get 25 | - apiGroups: 26 | - apps 27 | resources: 28 | - daemonsets 29 | - deployments 30 | - deployments/scale 31 | - replicasets 32 | - replicasets/scale 33 | - statefulsets 34 | verbs: 35 | - get 36 | - list 37 | - watch 38 | - apiGroups: 39 | - extensions 40 | resources: 41 | - daemonsets 42 | - deployments 43 | - deployments/scale 44 | - replicasets 45 | - replicasets/scale 46 | - replicationcontrollers/scale 47 | verbs: 48 | - get 49 | - list 50 | - watch 51 | - apiGroups: 52 | - batch 53 | resources: 54 | - cronjobs 55 | - jobs 56 | verbs: 57 | - get 58 | - list 59 | - watch -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | 7 | "gopkg.in/yaml.v2" 8 | ) 9 | 10 | type Config struct { 11 | Webhook struct { 12 | Enabled bool `yaml:"enabled"` 13 | Url string `yaml:"url"` 14 | } 15 | 16 | Livenesscheck struct { 17 | Enabled bool `yaml:"enabled"` 18 | Interval string `yaml:"interval"` 19 | } 20 | 21 | Slack struct { 22 | Enabled bool `yaml:"enabled"` 23 | Url string `yaml:"url"` 24 | Username string `yaml:"username"` 25 | Channel string `yaml:"channel"` 26 | } 27 | 28 | Telegram struct { 29 | Enabled bool `yaml:"enabled"` 30 | Token string `yaml:"token"` 31 | ChatID string `yaml:"chatID"` 32 | } 33 | 34 | Workplace struct { 35 | Enabled bool `yaml:"enabled"` 36 | ThreadKey string `yaml:"thread.key"` 37 | Token string `yaml:"token"` 38 | } 39 | 40 | ExcludedReasons []string `yaml:"excluded.reasons,flow"` 41 | IncludedReasons []string `yaml:"included.reasons,flow"` 42 | 43 | IncludedNamespace []string `yaml:"included.namespaces,flow"` 44 | } 45 | 46 | var GlobalConfig = &Config{} 47 | 48 | func (config *Config) Load() (err error) { 49 | yamlFile, err := ioutil.ReadFile("/usr/src/app/etc/siera-kube-watch/config.yaml") 50 | 51 | // uncomment this to test on your local 52 | // yamlFile, err := ioutil.ReadFile("./config.yaml") 53 | if err != nil { 54 | log.Fatalf("Error read config file: %v", err) 55 | return 56 | } 57 | 58 | err = yaml.Unmarshal(yamlFile, config) 59 | if err != nil { 60 | log.Fatalf("Error unmarshal: %v", err) 61 | } 62 | 63 | return 64 | } 65 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/warungpintar/siera-kube-watch/config" 8 | liveness_check "github.com/warungpintar/siera-kube-watch/liveness-check" 9 | 10 | eventHandler "github.com/warungpintar/siera-kube-watch/event-handler" 11 | "k8s.io/apimachinery/pkg/util/runtime" 12 | kubeinformers "k8s.io/client-go/informers" 13 | "k8s.io/client-go/kubernetes" 14 | _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" 15 | "k8s.io/client-go/tools/cache" 16 | "k8s.io/client-go/tools/clientcmd" 17 | ) 18 | 19 | func main() { 20 | //setup kubernetesConfig so the client can connect to kube cluster 21 | kubernetesConfig, err := clientcmd.BuildConfigFromFlags("", "") 22 | 23 | // uncomment this to test on on your local 24 | // kubernetesConfig, err := clientcmd.BuildConfigFromFlags("", filepath.Join(os.Getenv("HOME"), ".kube", "config")) 25 | if err != nil { 26 | log.Fatalf("Error parsing kubernetes config: %v", err) 27 | } 28 | 29 | err = config.GlobalConfig.Load() 30 | if err != nil { 31 | log.Fatalf("Error parsing config file: %v", err) 32 | } 33 | 34 | // create the clientset 35 | clientset, err := kubernetes.NewForConfig(kubernetesConfig) 36 | if err != nil { 37 | log.Fatalf("Error create kubernetes client set: %v", err) 38 | } 39 | 40 | log.Println("Client set created") 41 | 42 | kubeInformerFactory := kubeinformers.NewSharedInformerFactory(clientset, 0) 43 | eventInformer := kubeInformerFactory.Core().V1().Events().Informer() 44 | 45 | eventStopper := make(chan struct{}) 46 | defer runtime.HandleCrash() 47 | 48 | eventInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 49 | AddFunc: eventHandler.OnAddEvent, 50 | DeleteFunc: eventHandler.OnDeleteEvent, 51 | UpdateFunc: eventHandler.OnUpdateEvent, 52 | }) 53 | 54 | go liveness_check.Ping() 55 | 56 | go eventInformer.Run(eventStopper) 57 | 58 | if !cache.WaitForCacheSync(eventStopper, eventInformer.HasSynced) { 59 | runtime.HandleError(fmt.Errorf("timed out waiting for event caches to sync")) 60 | return 61 | } 62 | <-eventStopper 63 | } 64 | -------------------------------------------------------------------------------- /util/notify.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/warungpintar/siera-kube-watch/config" 8 | "github.com/warungpintar/siera-kube-watch/model" 9 | corev1 "k8s.io/api/core/v1" 10 | ) 11 | 12 | func NotifyEvent(event *corev1.Event) { 13 | if isNamespaceIncluded(event.Namespace) { 14 | if !isExist(config.GlobalConfig.ExcludedReasons, event.Reason) { 15 | if event.Type != NORMAL { 16 | message := parseEventToMessage(event) 17 | PostEvent(message) 18 | } else { 19 | if isExist(config.GlobalConfig.IncludedReasons, event.Reason) { 20 | message := parseEventToMessage(event) 21 | PostEvent(message) 22 | } 23 | } 24 | } 25 | } 26 | } 27 | 28 | func isNamespaceIncluded(namespace string) bool { 29 | if config.GlobalConfig.IncludedNamespace == nil || len(config.GlobalConfig.IncludedNamespace) == 0 || 30 | isExist(config.GlobalConfig.IncludedNamespace, namespace) { 31 | return true 32 | } 33 | 34 | return false 35 | } 36 | 37 | func parseEventToMessage(event *corev1.Event) string { 38 | return fmt.Sprintf("[%s: %s] %s/%s %s %v", event.Type, event.Reason, event.Namespace, event.Name, event.Message, event.LastTimestamp) 39 | } 40 | 41 | func isExist(arr []string, element string) bool { 42 | for _, el := range arr { 43 | if el == element { 44 | return true 45 | } 46 | } 47 | 48 | return false 49 | } 50 | 51 | func PostEvent(message string) { 52 | if config.GlobalConfig.Webhook.Enabled { 53 | model := model.StdModel{} 54 | model.New(message) 55 | err := model.Send(config.GlobalConfig.Webhook.Url) 56 | if err != nil { 57 | log.Println(err) 58 | } 59 | } 60 | 61 | if config.GlobalConfig.Slack.Enabled { 62 | model := model.SlackModel{} 63 | model.New(message) 64 | err := model.Send(config.GlobalConfig.Slack.Url) 65 | if err != nil { 66 | log.Println(err) 67 | } 68 | } 69 | 70 | if config.GlobalConfig.Telegram.Enabled { 71 | model := model.StdModel{} 72 | model.New(message) 73 | url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", config.GlobalConfig.Telegram.Token, config.GlobalConfig.Telegram.ChatID) 74 | err := model.Send(url) 75 | if err != nil { 76 | log.Println(err) 77 | } 78 | } 79 | 80 | if config.GlobalConfig.Workplace.Enabled { 81 | model := model.WorkplaceModel{} 82 | model.New(message, config.GlobalConfig.Workplace.ThreadKey) 83 | url := fmt.Sprintf("https://graph.facebook.com/v3.2/me/messages?access_token=%s&formatting=MARKDOWN", config.GlobalConfig.Workplace.Token) 84 | err := model.Send(url) 85 | if err != nil { 86 | log.Println(err) 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /config.example.yaml: -------------------------------------------------------------------------------- 1 | ### An example of configuration file for siera-kube-watch 2 | webhook: 3 | enabled: true 4 | url: "http://127.0.0.1/webhook" 5 | 6 | ### Enable dead man's switch functionality, similar to https://docs.openshift.com/container-platform/3.11/install_config/prometheus_cluster_monitoring.html#dead-mans-switch_prometheus-cluster-monitoring 7 | livenesscheck: 8 | enabled: true 9 | interval: "2s" 10 | 11 | ### For more information, refer to https://api.slack.com/messaging/webhooks 12 | slack: 13 | enabled: true 14 | ### slack url contain webhook url for your workspace. 15 | ### Example: 16 | ### url: "https://hooks.slack.com/services/TOKEN" 17 | ### username: "Siera Kube Watch" [OPTIONAL] 18 | ### channel: "#siera" [OPTIONAL] 19 | url: "https://hooks.slack.com/services/TOKEN" 20 | 21 | ### For more information, refer to https://core.telegram.org/bots/api 22 | telegram: 23 | enabled: true 24 | ### token is a unique authentication token when a bot is created. 25 | ### Example: 26 | ### token: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" 27 | token: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" 28 | ### chatID is a string contain unique identifier for the target chat or username of the target channel (in the format 29 | ### @channelusername). 30 | ### Example: 31 | ### chatID: "-1234567890123" 32 | chatID: "-1234567890123" 33 | 34 | ### For more information, refer to https://developers.facebook.com/docs/workplace/bots/ 35 | workplace: 36 | enabled: false 37 | ### thread.key is unique identifier of an existing group chat thread. 38 | ### Example: 39 | ### thread.key: "t_123456789" 40 | thread.key: "t_123456789" 41 | ### token is a generated access token when you create a new app for Workplace. 42 | ### For more information, refer to https://developers.facebook.com/docs/workplace/reference/permissions/ 43 | ### Example: 44 | ### token: "ABCDEF123456" 45 | token: "ABCDEF123456" 46 | 47 | 48 | ### In case you want to filter your event stream from specific reason, you can add that reason to excluded.reasons. 49 | ### excluded.reasons is an array of reasons that will be excluded so any event that have one of these reasons won't be 50 | ### published. 51 | ### Example: 52 | ### excluded.reasons: ["FailedGetResourceMetric", "ScalingReplicaSet", "Started", "Killing", "Unhealthy"] 53 | excluded.reasons: [] 54 | 55 | ### For filtering purpose, by default we excluded normal events (to avoid noisy channel) but if needed you can include 56 | ### several reasons from normal event for investigation purpose. 57 | ### included.reasons is an array of reasons that will be included from normal events. 58 | ### Example: 59 | ### included.reasons: ["ScalingReplicaSet", "SuccessfulCreate", "SuccessfulDelete"] 60 | included.reasons: ["ScalingReplicaSet", "Started", "Killing"] 61 | 62 | ### By default we include event from all namespaces, but you can filter namespace that you want to watch for. 63 | ### included.namespaces is an array of specific namespaces that will be included instead of publishing all namespaces. 64 | ### Example: 65 | ### included.namespaces: ["my-namespace", "your-namespace"] 66 | ### If you want to add this configuration, you can uncomment this field (by removing `#` in front of this 67 | ### configuration) and set whatever value you wish. 68 | # included.namespaces: [] 69 | -------------------------------------------------------------------------------- /deployment/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: siera-watch-config 5 | namespace: hack-tribe 6 | labels: 7 | app: siera-watch 8 | type: Opaque 9 | stringData: 10 | config.yaml: |- 11 | ### An example of configuration file for siera-kube-watch 12 | webhook: 13 | enabled: true 14 | url: "http://127.0.0.1/webhook" 15 | 16 | ### For more information, refer to https://api.slack.com/messaging/webhooks 17 | slack: 18 | enabled: true 19 | ### slack url contain webhook url for your workspace. 20 | ### Example: 21 | ### url: "https://hooks.slack.com/services/TOKEN" 22 | url: "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX" 23 | 24 | ### For more information, refer to https://core.telegram.org/bots/api 25 | telegram: 26 | enabled: true 27 | ### token is a unique authentication token when a bot is created. 28 | ### Example: 29 | ### token: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" 30 | token: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" 31 | ### chatID is a string contain unique identifier for the target chat or username of the target channel (in the format 32 | ### @channelusername). 33 | ### Example: 34 | ### chatID: "-1234567890123" 35 | chatID: "-1234567890123" 36 | 37 | ### For more information, refer to https://developers.facebook.com/docs/workplace/bots/ 38 | workplace: 39 | enabled: false 40 | ### thread.key is unique identifier of an existing group chat thread. 41 | ### Example: 42 | ### thread.key: "t_123456789" 43 | thread.key: "t_123456789" 44 | ### token is a generated access token when you create a new app for Workplace. 45 | ### For more information, refer to https://developers.facebook.com/docs/workplace/reference/permissions/ 46 | ### Example: 47 | ### token: "ABCDEF123456" 48 | token: "ABCDEF123456" 49 | 50 | ### In case you want to filter your event stream from specific reason, you can add that reason to excluded.reasons. 51 | ### excluded.reasons is an array of reasons that will be excluded so any event that have one of these reasons won't be 52 | ### published. 53 | ### Example: 54 | ### excluded.reasons: ["FailedGetResourceMetric", "ScalingReplicaSet", "Started", "Killing", "Unhealthy"] 55 | excluded.reasons: [] 56 | 57 | ### For filtering purpose, by default we excluded normal events (to avoid noisy channel) but if needed you can include 58 | ### several reasons from normal event for investigation purpose. 59 | ### included.reasons is an array of reasons that will be included from normal events. 60 | ### Example: 61 | ### included.reasons: ["ScalingReplicaSet", "SuccessfulCreate", "SuccessfulDelete"] 62 | included.reasons: ["ScalingReplicaSet", "Started", "Killing"] 63 | 64 | ### By default we include event from all namespaces, but you can filter namespace that you want to watch for. 65 | ### included.namespaces is an array of specific namespaces that will be included instead of publishing all namespaces. 66 | ### Example: 67 | ### included.namespaces: ["my-namespace", "your-namespace"] 68 | ### If you want to add this configuration, you can uncomment this field (by removing `#` in front of this 69 | ### configuration) and set whatever value you wish. 70 | # included.namespaces: [] 71 | 72 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 8 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= 9 | github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= 10 | github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 11 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 12 | github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= 13 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 14 | github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415 h1:WSBJMqJbLxsn+bTCPyPYZfqHdJmc8MK4wrBjMft6BAM= 15 | github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 16 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8= 17 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 18 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 19 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 20 | github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 21 | github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= 22 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 23 | github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= 24 | github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= 25 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 26 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= 27 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= 28 | github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= 29 | github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 30 | github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= 31 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 32 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 33 | github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 34 | github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= 35 | github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 36 | github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE= 37 | github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 38 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 39 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 40 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 41 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 42 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= 43 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 44 | github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 45 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= 46 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 47 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 48 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 49 | github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= 50 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 51 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 52 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 53 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 54 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 55 | github.com/warungpintar/siera-kube-watch v0.0.0-20200531032330-105e8a13c123 h1:cqHO8hou9Gwz58pkLSyqJghdc1TnrgECtBNbwqfxWfg= 56 | golang.org/x/crypto v0.0.0-20181025213731-e84da0312774 h1:a4tQYYYuK9QdeO/+kEvNYyuR21S+7ve5EANok6hABhI= 57 | golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 58 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 59 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 60 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 61 | golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68= 62 | golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 63 | golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 64 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= 65 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 66 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 67 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= 68 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 69 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 70 | golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc= 71 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 72 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 73 | golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= 74 | golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 75 | golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 76 | golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= 77 | golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 78 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 79 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 80 | google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= 81 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 82 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 83 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 84 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 85 | gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o= 86 | gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 87 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 88 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 89 | gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= 90 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 91 | k8s.io/api v0.15.9 h1:cCJD4WRNDrUWhputmpfvCnvpFFXJ68x8ycVqOBN7lHw= 92 | k8s.io/api v0.15.9/go.mod h1:k1xN2kcg4y3Yn8leWHxx5KW/tfWls8cd7L/0H/bzLdM= 93 | k8s.io/apimachinery v0.15.9 h1:vdgC+8MiWwgFVsUkmlkTpp4Dkpk9GY+aidLK31kXjeg= 94 | k8s.io/apimachinery v0.15.9/go.mod h1:Xc10RHc1U+F/e9GCloJ8QAeCGevSVP5xhOhqlE+e1kM= 95 | k8s.io/client-go v0.15.9 h1:PzeaA1blWxqTSJSzC9O9awEn60BqlYSVuBo1OdbHvZY= 96 | k8s.io/client-go v0.15.9/go.mod h1:5EsswhUDX/8AtuZlqgcnwC/QY++960gbBM2IyQ5t4nA= 97 | k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68= 98 | k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 99 | k8s.io/klog/v2 v2.0.0 h1:Foj74zO6RbjjP4hBEKjnYtjjAhGg4jNynUdYF6fJrok= 100 | k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= 101 | k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= 102 | k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= 103 | k8s.io/utils v0.0.0-20200414100711-2df71ebbae66 h1:Ly1Oxdu5p5ZFmiVT71LFgeZETvMfZ1iBIGeOenT2JeM= 104 | k8s.io/utils v0.0.0-20200414100711-2df71ebbae66/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= 105 | sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 106 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 107 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------