diff --git a/cmd/jiratarget-adapter/main.go b/cmd/jiratarget-adapter/main.go new file mode 100644 index 00000000..71ccaa19 --- /dev/null +++ b/cmd/jiratarget-adapter/main.go @@ -0,0 +1,11 @@ +package main + +import ( + pkgadapter "knative.dev/eventing/pkg/adapter/v2" + + "github.com/zeiss/typhoon/pkg/targets/adapter/jiratarget" +) + +func main() { + pkgadapter.Main("jiratarget", jiratarget.EnvAccessorCtor, jiratarget.NewTarget) +} diff --git a/cmd/servicenowtarget-adapter/main.go b/cmd/servicenowtarget-adapter/main.go new file mode 100644 index 00000000..7a2725b2 --- /dev/null +++ b/cmd/servicenowtarget-adapter/main.go @@ -0,0 +1,11 @@ +package main + +import ( + pkgadapter "knative.dev/eventing/pkg/adapter/v2" + + "github.com/zeiss/typhoon/pkg/targets/adapter/servicenowtarget" +) + +func main() { + pkgadapter.Main("servicenowtarget", servicenowtarget.EnvAccessorCtor, servicenowtarget.NewTarget) +} diff --git a/go.mod b/go.mod index b9dd0bed..da0adc1e 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.22.1 require ( github.com/Shopify/sarama v1.30.0 github.com/ZachtimusPrime/Go-Splunk-HTTP/splunk/v2 v2.0.2 + github.com/andygrunwald/go-jira v1.16.0 github.com/basgys/goxml2json v1.1.0 github.com/cloudevents/sdk-go/v2 v2.15.2 github.com/fsnotify/fsnotify v1.7.0 @@ -61,7 +62,6 @@ require ( require ( 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect 4d63.com/gochecknoglobals v0.2.1 // indirect - cloud.google.com/go/compute/metadata v0.3.0 // indirect contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d // indirect contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect contrib.go.opencensus.io/exporter/zipkin v0.1.2 // indirect @@ -120,6 +120,7 @@ require ( github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fatih/color v1.17.0 // indirect + github.com/fatih/structs v1.1.0 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/firefart/nonamedreturns v1.0.5 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect @@ -163,6 +164,7 @@ require ( github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-containerregistry v0.13.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/gordonklaus/ineffassign v0.1.0 // indirect github.com/gorilla/mux v1.8.1 // indirect @@ -301,6 +303,7 @@ require ( github.com/timonwong/loggercheck v0.9.4 // indirect github.com/tomarrell/wrapcheck/v2 v2.8.3 // indirect github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect + github.com/trivago/tgo v1.0.7 // indirect github.com/ultraware/funlen v0.1.0 // indirect github.com/ultraware/whitespace v0.1.1 // indirect github.com/uudashr/gocognit v1.1.2 // indirect diff --git a/go.sum b/go.sum index 41fbdbe7..a4f9bea6 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,6 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= @@ -133,6 +131,8 @@ github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQ github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/andygrunwald/go-jira v1.16.0 h1:PU7C7Fkk5L96JvPc6vDVIrd99vdPnYudHu4ju2c2ikQ= +github.com/andygrunwald/go-jira v1.16.0/go.mod h1:UQH4IBVxIYWbgagc0LF/k9FRs9xjIiQ8hIcC6HfLwFU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211221011931-643d94fcab96 h1:2P/dm3KbCLnRHQN/Ma50elhMx1Si9loEZe5hOrsuvuE= @@ -308,6 +308,8 @@ github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -439,6 +441,7 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -540,6 +543,8 @@ github.com/google/go-containerregistry v0.13.0 h1:y1C7Z3e149OJbOPDBxLYR8ITPz8dTK github.com/google/go-containerregistry v0.13.0/go.mod h1:J9FQ+eSS4a1aC2GNZxvNpbWhgp0487v+cgiilB4FqDo= github.com/google/go-github/v27 v27.0.6/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -1173,6 +1178,8 @@ github.com/tomarrell/wrapcheck/v2 v2.8.3 h1:5ov+Cbhlgi7s/a42BprYoxsr73CbdMUTzE3b github.com/tomarrell/wrapcheck/v2 v2.8.3/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= +github.com/trivago/tgo v1.0.7 h1:uaWH/XIy9aWYWpjm2CU3RpcqZXmX2ysQ9/Go+d9gyrM= +github.com/trivago/tgo v1.0.7/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc= github.com/tsenart/go-tsz v0.0.0-20180814232043-cdeb9e1e981e/go.mod h1:SWZznP1z5Ki7hDT2ioqiFKEse8K9tU2OUvaRI0NeGQo= github.com/tsenart/vegeta/v12 v12.8.4/go.mod h1:ZiJtwLn/9M4fTPdMY7bdbIeyNeFVE8/AHbWFqCsUuho= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -1545,6 +1552,7 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1564,6 +1572,7 @@ golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/pkg/apis/sources/v1alpha1/salesforce_types.go b/pkg/apis/sources/v1alpha1/salesforce_types.go index 341b5f88..af7116ed 100644 --- a/pkg/apis/sources/v1alpha1/salesforce_types.go +++ b/pkg/apis/sources/v1alpha1/salesforce_types.go @@ -35,6 +35,9 @@ type SalesforceSourceSpec struct { // Authentication method to interact with the Salesforce API. Auth SalesforceAuth `json:"auth"` + // InstanceURL of the Salesforce instance. + InstanceURL string `json:"instanceURL"` + // APIVersion at Salesforce. // +optional APIVersion *string `json:"apiVersion"` @@ -55,10 +58,9 @@ type SalesforceSubscription struct { // SalesforceAuth contains Salesforce credentials. type SalesforceAuth struct { - ClientID string `json:"clientID"` - Server string `json:"server"` - User string `json:"user"` - CertKey v1alpha1.ValueFromField `json:"certKey"` + ClientID string `json:"clientID"` + ClientSecret string `json:"clientSecret"` + TokenURL string `json:"tokenURL"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/sources/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/sources/v1alpha1/zz_generated.deepcopy.go index d3beb475..b01700bd 100644 --- a/pkg/apis/sources/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/sources/v1alpha1/zz_generated.deepcopy.go @@ -651,7 +651,6 @@ func (in *RateLimiter) DeepCopy() *RateLimiter { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SalesforceAuth) DeepCopyInto(out *SalesforceAuth) { *out = *in - in.CertKey.DeepCopyInto(&out.CertKey) return } @@ -730,7 +729,7 @@ func (in *SalesforceSourceList) DeepCopyObject() runtime.Object { func (in *SalesforceSourceSpec) DeepCopyInto(out *SalesforceSourceSpec) { *out = *in in.SourceSpec.DeepCopyInto(&out.SourceSpec) - in.Auth.DeepCopyInto(&out.Auth) + out.Auth = in.Auth if in.APIVersion != nil { in, out := &in.APIVersion, &out.APIVersion *out = new(string) diff --git a/pkg/apis/targets/register.go b/pkg/apis/targets/register.go index 69b22c6d..6761c841 100644 --- a/pkg/apis/targets/register.go +++ b/pkg/apis/targets/register.go @@ -46,4 +46,9 @@ var ( Group: GroupName, Resource: "salesforcetargets", } + // JiraTargetResource respresents an event target for Jira. + JiraTargetResource = schema.GroupResource{ + Group: GroupName, + Resource: "jiratargets", + } ) diff --git a/pkg/apis/targets/v1alpha1/jira_lifecycle.go b/pkg/apis/targets/v1alpha1/jira_lifecycle.go new file mode 100644 index 00000000..6723ebb7 --- /dev/null +++ b/pkg/apis/targets/v1alpha1/jira_lifecycle.go @@ -0,0 +1,83 @@ +package v1alpha1 + +import ( + "context" + "strings" + + "k8s.io/apimachinery/pkg/runtime/schema" + + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" + + "github.com/zeiss/typhoon/pkg/apis/common/v1alpha1" +) + +// Managed event types +const ( + EventTypeJiraIssueCreate = "com.zeiss.typhoon.jira.issue.create" + EventTypeJiraIssueGet = "com.zeiss.typhoon.jira.issue.get" + EventTypeJiraCustom = "com.zeiss.typhoon.jira.custom" + + EventTypeJiraIssue = "com.zeiss.typhoon.jira.issue" + EventTypeJiraCustomResponse = "com.zeiss.typhoon.jira.custom.response" +) + +// GetGroupVersionKind implements kmeta.OwnerRefable. +func (*JiraTarget) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("JiraTarget") +} + +// GetConditionSet implements duckv1.KRShaped. +func (*JiraTarget) GetConditionSet() apis.ConditionSet { + return v1alpha1.DefaultConditionSet +} + +// GetStatus implements duckv1.KRShaped. +func (t *JiraTarget) GetStatus() *duckv1.Status { + return &t.Status.Status +} + +// GetStatusManager implements Reconcilable. +func (t *JiraTarget) GetStatusManager() *v1alpha1.StatusManager { + return &v1alpha1.StatusManager{ + ConditionSet: t.GetConditionSet(), + Status: &t.Status, + } +} + +// AcceptedEventTypes implements IntegrationTarget. +func (*JiraTarget) AcceptedEventTypes() []string { + return []string{ + EventTypeJiraIssueCreate, + EventTypeJiraIssueGet, + EventTypeJiraCustom, + } +} + +// GetEventTypes implements EventSource. +func (*JiraTarget) GetEventTypes() []string { + return []string{ + EventTypeJiraIssue, + EventTypeJiraCustomResponse, + } +} + +// AsEventSource implements EventSource. +func (t *JiraTarget) AsEventSource() string { + kind := strings.ToLower(t.GetGroupVersionKind().Kind) + return "com.zeiss.typhoon." + kind + "." + t.Namespace + "." + t.Name +} + +// GetAdapterOverrides implements AdapterConfigurable. +func (t *JiraTarget) GetAdapterOverrides() *v1alpha1.AdapterOverrides { + return t.Spec.AdapterOverrides +} + +// SetDefaults implements apis.Defaultable +func (t *JiraTarget) SetDefaults(ctx context.Context) { +} + +// Validate implements apis.Validatable +func (t *JiraTarget) Validate(ctx context.Context) *apis.FieldError { + return nil +} diff --git a/pkg/apis/targets/v1alpha1/jira_types.go b/pkg/apis/targets/v1alpha1/jira_types.go new file mode 100644 index 00000000..1810e45e --- /dev/null +++ b/pkg/apis/targets/v1alpha1/jira_types.go @@ -0,0 +1,57 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/zeiss/typhoon/pkg/apis/common/v1alpha1" +) + +// +genclient +// +genreconciler +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// JiraTarget is the Schema for the Jira Target. +type JiraTarget struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec JiraTargetSpec `json:"spec"` + Status v1alpha1.Status `json:"status,omitempty"` +} + +// Check the interfaces the event target should be implementing. +var ( + _ v1alpha1.Reconcilable = (*JiraTarget)(nil) + _ v1alpha1.AdapterConfigurable = (*JiraTarget)(nil) + _ v1alpha1.EventSource = (*JiraTarget)(nil) +) + +// JiraTargetSpec defines the desired state of the event target. +type JiraTargetSpec struct { + // Authentication to interact with the JIRA REST API. + Auth JiraAuth `json:"auth"` + + // URL for Jira service. + URL string `json:"url"` + + // Adapter spec overrides parameters. + // +optional + AdapterOverrides *v1alpha1.AdapterOverrides `json:"adapterOverrides,omitempty"` +} + +// JiraAuth contains Jira credentials. +type JiraAuth struct { + // Jira username to connect to the instance as. + User string `json:"user"` + // Jira API token bound to the user. + Token SecretValueFromSource `json:"token"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// JiraTargetList is a list of event target instances. +type JiraTargetList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + Items []JiraTarget `json:"items"` +} diff --git a/pkg/apis/targets/v1alpha1/snow_lifecycle.go b/pkg/apis/targets/v1alpha1/snow_lifecycle.go new file mode 100644 index 00000000..72b924f4 --- /dev/null +++ b/pkg/apis/targets/v1alpha1/snow_lifecycle.go @@ -0,0 +1,49 @@ +package v1alpha1 + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime/schema" + + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" + + "github.com/zeiss/typhoon/pkg/apis/common/v1alpha1" +) + +// GetGroupVersionKind implements kmeta.OwnerRefable. +func (*ServiceNowTarget) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("ServiceNowTarget") +} + +// GetConditionSet implements duckv1.KRShaped. +func (*ServiceNowTarget) GetConditionSet() apis.ConditionSet { + return v1alpha1.DefaultConditionSet +} + +// GetStatus implements duckv1.KRShaped. +func (t *ServiceNowTarget) GetStatus() *duckv1.Status { + return &t.Status.Status +} + +// GetStatusManager implements Reconcilable. +func (t *ServiceNowTarget) GetStatusManager() *v1alpha1.StatusManager { + return &v1alpha1.StatusManager{ + ConditionSet: t.GetConditionSet(), + Status: &t.Status, + } +} + +// GetAdapterOverrides implements AdapterConfigurable. +func (t *ServiceNowTarget) GetAdapterOverrides() *v1alpha1.AdapterOverrides { + return t.Spec.AdapterOverrides +} + +// SetDefaults implements apis.Defaultable +func (t *ServiceNowTarget) SetDefaults(ctx context.Context) { +} + +// Validate implements apis.Validatable +func (t *ServiceNowTarget) Validate(ctx context.Context) *apis.FieldError { + return nil +} diff --git a/pkg/apis/targets/v1alpha1/snow_types.go b/pkg/apis/targets/v1alpha1/snow_types.go new file mode 100644 index 00000000..92ac53a6 --- /dev/null +++ b/pkg/apis/targets/v1alpha1/snow_types.go @@ -0,0 +1,65 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/zeiss/typhoon/pkg/apis/common/v1alpha1" +) + +// +genclient +// +genreconciler +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ServiceNowTarget ... +type ServiceNowTarget struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ServiceNowTargetSpec `json:"spec"` + Status v1alpha1.Status `json:"status,omitempty"` +} + +// Check the interfaces the event target should be implementing. +var ( + _ v1alpha1.Reconcilable = (*SalesforceTarget)(nil) + _ v1alpha1.AdapterConfigurable = (*SalesforceTarget)(nil) + _ v1alpha1.EventReceiver = (*SalesforceTarget)(nil) + _ v1alpha1.EventSource = (*SalesforceTarget)(nil) +) + +// ServiceNowTargetSpec defines the desired state of the event target. +type ServiceNowTargetSpec struct { + // Authentication information to interact with the Salesforce API. + Auth SalesforceAuth `json:"auth"` + + // Instance is the ServiceNow instance to connect to. + Instance string `json:"instance"` + + // Source is the source of the event. + Source string `json:"source"` + + // EventOptions for targets + // +optional + EventOptions *EventOptions `json:"eventOptions,omitempty"` + + // Adapter spec overrides parameters. + // +optional + AdapterOverrides *v1alpha1.AdapterOverrides `json:"adapterOverrides,omitempty"` +} + +// ServiceNowAuth contains the authentication information for the ServiceNow API. +type ServiceNowAuth struct { + // User is the username used to authenticate with the ServiceNow API. + User string `json:"user"` + // Password is the password used to authenticate with the ServiceNow API. + Password string `json:"password"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ServiceNowTargetList is a list of event target instances. +type ServiceNowTargetList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + Items []SalesforceTarget `json:"items"` +} diff --git a/pkg/apis/targets/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/targets/v1alpha1/zz_generated.deepcopy.go index d7998f66..131bd26a 100644 --- a/pkg/apis/targets/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/targets/v1alpha1/zz_generated.deepcopy.go @@ -422,6 +422,106 @@ func (in *Instrument) DeepCopy() *Instrument { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JiraAuth) DeepCopyInto(out *JiraAuth) { + *out = *in + in.Token.DeepCopyInto(&out.Token) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JiraAuth. +func (in *JiraAuth) DeepCopy() *JiraAuth { + if in == nil { + return nil + } + out := new(JiraAuth) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JiraTarget) DeepCopyInto(out *JiraTarget) { + *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 JiraTarget. +func (in *JiraTarget) DeepCopy() *JiraTarget { + if in == nil { + return nil + } + out := new(JiraTarget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *JiraTarget) 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 *JiraTargetList) DeepCopyInto(out *JiraTargetList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]JiraTarget, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JiraTargetList. +func (in *JiraTargetList) DeepCopy() *JiraTargetList { + if in == nil { + return nil + } + out := new(JiraTargetList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *JiraTargetList) 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 *JiraTargetSpec) DeepCopyInto(out *JiraTargetSpec) { + *out = *in + in.Auth.DeepCopyInto(&out.Auth) + if in.AdapterOverrides != nil { + in, out := &in.AdapterOverrides, &out.AdapterOverrides + *out = new(commonv1alpha1.AdapterOverrides) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JiraTargetSpec. +func (in *JiraTargetSpec) DeepCopy() *JiraTargetSpec { + if in == nil { + return nil + } + out := new(JiraTargetSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KafkaTarget) DeepCopyInto(out *KafkaTarget) { *out = *in @@ -1075,6 +1175,110 @@ func (in *SecretValueFromSource) DeepCopy() *SecretValueFromSource { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceNowAuth) DeepCopyInto(out *ServiceNowAuth) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceNowAuth. +func (in *ServiceNowAuth) DeepCopy() *ServiceNowAuth { + if in == nil { + return nil + } + out := new(ServiceNowAuth) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceNowTarget) DeepCopyInto(out *ServiceNowTarget) { + *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 ServiceNowTarget. +func (in *ServiceNowTarget) DeepCopy() *ServiceNowTarget { + if in == nil { + return nil + } + out := new(ServiceNowTarget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceNowTarget) 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 *ServiceNowTargetList) DeepCopyInto(out *ServiceNowTargetList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]SalesforceTarget, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceNowTargetList. +func (in *ServiceNowTargetList) DeepCopy() *ServiceNowTargetList { + if in == nil { + return nil + } + out := new(ServiceNowTargetList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceNowTargetList) 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 *ServiceNowTargetSpec) DeepCopyInto(out *ServiceNowTargetSpec) { + *out = *in + in.Auth.DeepCopyInto(&out.Auth) + if in.EventOptions != nil { + in, out := &in.EventOptions, &out.EventOptions + *out = new(EventOptions) + (*in).DeepCopyInto(*out) + } + if in.AdapterOverrides != nil { + in, out := &in.AdapterOverrides, &out.AdapterOverrides + *out = new(commonv1alpha1.AdapterOverrides) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceNowTargetSpec. +func (in *ServiceNowTargetSpec) DeepCopy() *ServiceNowTargetSpec { + if in == nil { + return nil + } + out := new(ServiceNowTargetSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SplunkTarget) DeepCopyInto(out *SplunkTarget) { *out = *in diff --git a/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/fake/fake_jiratarget.go b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/fake/fake_jiratarget.go new file mode 100644 index 00000000..7dfe9919 --- /dev/null +++ b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/fake/fake_jiratarget.go @@ -0,0 +1,125 @@ +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeJiraTargets implements JiraTargetInterface +type FakeJiraTargets struct { + Fake *FakeTargetsV1alpha1 + ns string +} + +var jiratargetsResource = v1alpha1.SchemeGroupVersion.WithResource("jiratargets") + +var jiratargetsKind = v1alpha1.SchemeGroupVersion.WithKind("JiraTarget") + +// Get takes name of the jiraTarget, and returns the corresponding jiraTarget object, and an error if there is any. +func (c *FakeJiraTargets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.JiraTarget, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(jiratargetsResource, c.ns, name), &v1alpha1.JiraTarget{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.JiraTarget), err +} + +// List takes label and field selectors, and returns the list of JiraTargets that match those selectors. +func (c *FakeJiraTargets) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.JiraTargetList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(jiratargetsResource, jiratargetsKind, c.ns, opts), &v1alpha1.JiraTargetList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.JiraTargetList{ListMeta: obj.(*v1alpha1.JiraTargetList).ListMeta} + for _, item := range obj.(*v1alpha1.JiraTargetList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested jiraTargets. +func (c *FakeJiraTargets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(jiratargetsResource, c.ns, opts)) + +} + +// Create takes the representation of a jiraTarget and creates it. Returns the server's representation of the jiraTarget, and an error, if there is any. +func (c *FakeJiraTargets) Create(ctx context.Context, jiraTarget *v1alpha1.JiraTarget, opts v1.CreateOptions) (result *v1alpha1.JiraTarget, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(jiratargetsResource, c.ns, jiraTarget), &v1alpha1.JiraTarget{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.JiraTarget), err +} + +// Update takes the representation of a jiraTarget and updates it. Returns the server's representation of the jiraTarget, and an error, if there is any. +func (c *FakeJiraTargets) Update(ctx context.Context, jiraTarget *v1alpha1.JiraTarget, opts v1.UpdateOptions) (result *v1alpha1.JiraTarget, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(jiratargetsResource, c.ns, jiraTarget), &v1alpha1.JiraTarget{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.JiraTarget), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeJiraTargets) UpdateStatus(ctx context.Context, jiraTarget *v1alpha1.JiraTarget, opts v1.UpdateOptions) (*v1alpha1.JiraTarget, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(jiratargetsResource, "status", c.ns, jiraTarget), &v1alpha1.JiraTarget{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.JiraTarget), err +} + +// Delete takes name of the jiraTarget and deletes it. Returns an error if one occurs. +func (c *FakeJiraTargets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(jiratargetsResource, c.ns, name, opts), &v1alpha1.JiraTarget{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeJiraTargets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(jiratargetsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.JiraTargetList{}) + return err +} + +// Patch applies the patch and returns the patched jiraTarget. +func (c *FakeJiraTargets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.JiraTarget, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(jiratargetsResource, c.ns, name, pt, data, subresources...), &v1alpha1.JiraTarget{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.JiraTarget), err +} diff --git a/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/fake/fake_servicenowtarget.go b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/fake/fake_servicenowtarget.go new file mode 100644 index 00000000..a5ec3e0a --- /dev/null +++ b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/fake/fake_servicenowtarget.go @@ -0,0 +1,125 @@ +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeServiceNowTargets implements ServiceNowTargetInterface +type FakeServiceNowTargets struct { + Fake *FakeTargetsV1alpha1 + ns string +} + +var servicenowtargetsResource = v1alpha1.SchemeGroupVersion.WithResource("servicenowtargets") + +var servicenowtargetsKind = v1alpha1.SchemeGroupVersion.WithKind("ServiceNowTarget") + +// Get takes name of the serviceNowTarget, and returns the corresponding serviceNowTarget object, and an error if there is any. +func (c *FakeServiceNowTargets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServiceNowTarget, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(servicenowtargetsResource, c.ns, name), &v1alpha1.ServiceNowTarget{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceNowTarget), err +} + +// List takes label and field selectors, and returns the list of ServiceNowTargets that match those selectors. +func (c *FakeServiceNowTargets) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServiceNowTargetList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(servicenowtargetsResource, servicenowtargetsKind, c.ns, opts), &v1alpha1.ServiceNowTargetList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.ServiceNowTargetList{ListMeta: obj.(*v1alpha1.ServiceNowTargetList).ListMeta} + for _, item := range obj.(*v1alpha1.ServiceNowTargetList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested serviceNowTargets. +func (c *FakeServiceNowTargets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(servicenowtargetsResource, c.ns, opts)) + +} + +// Create takes the representation of a serviceNowTarget and creates it. Returns the server's representation of the serviceNowTarget, and an error, if there is any. +func (c *FakeServiceNowTargets) Create(ctx context.Context, serviceNowTarget *v1alpha1.ServiceNowTarget, opts v1.CreateOptions) (result *v1alpha1.ServiceNowTarget, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(servicenowtargetsResource, c.ns, serviceNowTarget), &v1alpha1.ServiceNowTarget{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceNowTarget), err +} + +// Update takes the representation of a serviceNowTarget and updates it. Returns the server's representation of the serviceNowTarget, and an error, if there is any. +func (c *FakeServiceNowTargets) Update(ctx context.Context, serviceNowTarget *v1alpha1.ServiceNowTarget, opts v1.UpdateOptions) (result *v1alpha1.ServiceNowTarget, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(servicenowtargetsResource, c.ns, serviceNowTarget), &v1alpha1.ServiceNowTarget{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceNowTarget), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeServiceNowTargets) UpdateStatus(ctx context.Context, serviceNowTarget *v1alpha1.ServiceNowTarget, opts v1.UpdateOptions) (*v1alpha1.ServiceNowTarget, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(servicenowtargetsResource, "status", c.ns, serviceNowTarget), &v1alpha1.ServiceNowTarget{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceNowTarget), err +} + +// Delete takes name of the serviceNowTarget and deletes it. Returns an error if one occurs. +func (c *FakeServiceNowTargets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(servicenowtargetsResource, c.ns, name, opts), &v1alpha1.ServiceNowTarget{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeServiceNowTargets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(servicenowtargetsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.ServiceNowTargetList{}) + return err +} + +// Patch applies the patch and returns the patched serviceNowTarget. +func (c *FakeServiceNowTargets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServiceNowTarget, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(servicenowtargetsResource, c.ns, name, pt, data, subresources...), &v1alpha1.ServiceNowTarget{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ServiceNowTarget), err +} diff --git a/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/fake/fake_targets_client.go b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/fake/fake_targets_client.go index 2ac6b855..a6af975f 100644 --- a/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/fake/fake_targets_client.go +++ b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/fake/fake_targets_client.go @@ -24,6 +24,10 @@ func (c *FakeTargetsV1alpha1) HTTPTargets(namespace string) v1alpha1.HTTPTargetI return &FakeHTTPTargets{c, namespace} } +func (c *FakeTargetsV1alpha1) JiraTargets(namespace string) v1alpha1.JiraTargetInterface { + return &FakeJiraTargets{c, namespace} +} + func (c *FakeTargetsV1alpha1) KafkaTargets(namespace string) v1alpha1.KafkaTargetInterface { return &FakeKafkaTargets{c, namespace} } @@ -44,6 +48,10 @@ func (c *FakeTargetsV1alpha1) SalesforceTargets(namespace string) v1alpha1.Sales return &FakeSalesforceTargets{c, namespace} } +func (c *FakeTargetsV1alpha1) ServiceNowTargets(namespace string) v1alpha1.ServiceNowTargetInterface { + return &FakeServiceNowTargets{c, namespace} +} + func (c *FakeTargetsV1alpha1) SplunkTargets(namespace string) v1alpha1.SplunkTargetInterface { return &FakeSplunkTargets{c, namespace} } diff --git a/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/generated_expansion.go b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/generated_expansion.go index 2f61e878..6cfc127d 100644 --- a/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/generated_expansion.go +++ b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/generated_expansion.go @@ -8,6 +8,8 @@ type DatadogTargetExpansion interface{} type HTTPTargetExpansion interface{} +type JiraTargetExpansion interface{} + type KafkaTargetExpansion interface{} type LogzMetricsTargetExpansion interface{} @@ -18,4 +20,6 @@ type NatsTargetExpansion interface{} type SalesforceTargetExpansion interface{} +type ServiceNowTargetExpansion interface{} + type SplunkTargetExpansion interface{} diff --git a/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/jiratarget.go b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/jiratarget.go new file mode 100644 index 00000000..d51d92bd --- /dev/null +++ b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/jiratarget.go @@ -0,0 +1,179 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + scheme "github.com/zeiss/typhoon/pkg/client/generated/clientset/internalclientset/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// JiraTargetsGetter has a method to return a JiraTargetInterface. +// A group's client should implement this interface. +type JiraTargetsGetter interface { + JiraTargets(namespace string) JiraTargetInterface +} + +// JiraTargetInterface has methods to work with JiraTarget resources. +type JiraTargetInterface interface { + Create(ctx context.Context, jiraTarget *v1alpha1.JiraTarget, opts v1.CreateOptions) (*v1alpha1.JiraTarget, error) + Update(ctx context.Context, jiraTarget *v1alpha1.JiraTarget, opts v1.UpdateOptions) (*v1alpha1.JiraTarget, error) + UpdateStatus(ctx context.Context, jiraTarget *v1alpha1.JiraTarget, opts v1.UpdateOptions) (*v1alpha1.JiraTarget, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.JiraTarget, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.JiraTargetList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.JiraTarget, err error) + JiraTargetExpansion +} + +// jiraTargets implements JiraTargetInterface +type jiraTargets struct { + client rest.Interface + ns string +} + +// newJiraTargets returns a JiraTargets +func newJiraTargets(c *TargetsV1alpha1Client, namespace string) *jiraTargets { + return &jiraTargets{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the jiraTarget, and returns the corresponding jiraTarget object, and an error if there is any. +func (c *jiraTargets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.JiraTarget, err error) { + result = &v1alpha1.JiraTarget{} + err = c.client.Get(). + Namespace(c.ns). + Resource("jiratargets"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of JiraTargets that match those selectors. +func (c *jiraTargets) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.JiraTargetList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.JiraTargetList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("jiratargets"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested jiraTargets. +func (c *jiraTargets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("jiratargets"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a jiraTarget and creates it. Returns the server's representation of the jiraTarget, and an error, if there is any. +func (c *jiraTargets) Create(ctx context.Context, jiraTarget *v1alpha1.JiraTarget, opts v1.CreateOptions) (result *v1alpha1.JiraTarget, err error) { + result = &v1alpha1.JiraTarget{} + err = c.client.Post(). + Namespace(c.ns). + Resource("jiratargets"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(jiraTarget). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a jiraTarget and updates it. Returns the server's representation of the jiraTarget, and an error, if there is any. +func (c *jiraTargets) Update(ctx context.Context, jiraTarget *v1alpha1.JiraTarget, opts v1.UpdateOptions) (result *v1alpha1.JiraTarget, err error) { + result = &v1alpha1.JiraTarget{} + err = c.client.Put(). + Namespace(c.ns). + Resource("jiratargets"). + Name(jiraTarget.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(jiraTarget). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *jiraTargets) UpdateStatus(ctx context.Context, jiraTarget *v1alpha1.JiraTarget, opts v1.UpdateOptions) (result *v1alpha1.JiraTarget, err error) { + result = &v1alpha1.JiraTarget{} + err = c.client.Put(). + Namespace(c.ns). + Resource("jiratargets"). + Name(jiraTarget.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(jiraTarget). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the jiraTarget and deletes it. Returns an error if one occurs. +func (c *jiraTargets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("jiratargets"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *jiraTargets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("jiratargets"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched jiraTarget. +func (c *jiraTargets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.JiraTarget, err error) { + result = &v1alpha1.JiraTarget{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("jiratargets"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/servicenowtarget.go b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/servicenowtarget.go new file mode 100644 index 00000000..65236467 --- /dev/null +++ b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/servicenowtarget.go @@ -0,0 +1,179 @@ +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + scheme "github.com/zeiss/typhoon/pkg/client/generated/clientset/internalclientset/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ServiceNowTargetsGetter has a method to return a ServiceNowTargetInterface. +// A group's client should implement this interface. +type ServiceNowTargetsGetter interface { + ServiceNowTargets(namespace string) ServiceNowTargetInterface +} + +// ServiceNowTargetInterface has methods to work with ServiceNowTarget resources. +type ServiceNowTargetInterface interface { + Create(ctx context.Context, serviceNowTarget *v1alpha1.ServiceNowTarget, opts v1.CreateOptions) (*v1alpha1.ServiceNowTarget, error) + Update(ctx context.Context, serviceNowTarget *v1alpha1.ServiceNowTarget, opts v1.UpdateOptions) (*v1alpha1.ServiceNowTarget, error) + UpdateStatus(ctx context.Context, serviceNowTarget *v1alpha1.ServiceNowTarget, opts v1.UpdateOptions) (*v1alpha1.ServiceNowTarget, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ServiceNowTarget, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ServiceNowTargetList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServiceNowTarget, err error) + ServiceNowTargetExpansion +} + +// serviceNowTargets implements ServiceNowTargetInterface +type serviceNowTargets struct { + client rest.Interface + ns string +} + +// newServiceNowTargets returns a ServiceNowTargets +func newServiceNowTargets(c *TargetsV1alpha1Client, namespace string) *serviceNowTargets { + return &serviceNowTargets{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the serviceNowTarget, and returns the corresponding serviceNowTarget object, and an error if there is any. +func (c *serviceNowTargets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServiceNowTarget, err error) { + result = &v1alpha1.ServiceNowTarget{} + err = c.client.Get(). + Namespace(c.ns). + Resource("servicenowtargets"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ServiceNowTargets that match those selectors. +func (c *serviceNowTargets) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServiceNowTargetList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.ServiceNowTargetList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("servicenowtargets"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested serviceNowTargets. +func (c *serviceNowTargets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("servicenowtargets"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a serviceNowTarget and creates it. Returns the server's representation of the serviceNowTarget, and an error, if there is any. +func (c *serviceNowTargets) Create(ctx context.Context, serviceNowTarget *v1alpha1.ServiceNowTarget, opts v1.CreateOptions) (result *v1alpha1.ServiceNowTarget, err error) { + result = &v1alpha1.ServiceNowTarget{} + err = c.client.Post(). + Namespace(c.ns). + Resource("servicenowtargets"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(serviceNowTarget). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a serviceNowTarget and updates it. Returns the server's representation of the serviceNowTarget, and an error, if there is any. +func (c *serviceNowTargets) Update(ctx context.Context, serviceNowTarget *v1alpha1.ServiceNowTarget, opts v1.UpdateOptions) (result *v1alpha1.ServiceNowTarget, err error) { + result = &v1alpha1.ServiceNowTarget{} + err = c.client.Put(). + Namespace(c.ns). + Resource("servicenowtargets"). + Name(serviceNowTarget.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(serviceNowTarget). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *serviceNowTargets) UpdateStatus(ctx context.Context, serviceNowTarget *v1alpha1.ServiceNowTarget, opts v1.UpdateOptions) (result *v1alpha1.ServiceNowTarget, err error) { + result = &v1alpha1.ServiceNowTarget{} + err = c.client.Put(). + Namespace(c.ns). + Resource("servicenowtargets"). + Name(serviceNowTarget.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(serviceNowTarget). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the serviceNowTarget and deletes it. Returns an error if one occurs. +func (c *serviceNowTargets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("servicenowtargets"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *serviceNowTargets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("servicenowtargets"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched serviceNowTarget. +func (c *serviceNowTargets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServiceNowTarget, err error) { + result = &v1alpha1.ServiceNowTarget{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("servicenowtargets"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/targets_client.go b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/targets_client.go index 4075e42d..d266190e 100644 --- a/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/targets_client.go +++ b/pkg/client/generated/clientset/internalclientset/typed/targets/v1alpha1/targets_client.go @@ -15,11 +15,13 @@ type TargetsV1alpha1Interface interface { CloudEventsTargetsGetter DatadogTargetsGetter HTTPTargetsGetter + JiraTargetsGetter KafkaTargetsGetter LogzMetricsTargetsGetter LogzTargetsGetter NatsTargetsGetter SalesforceTargetsGetter + ServiceNowTargetsGetter SplunkTargetsGetter } @@ -40,6 +42,10 @@ func (c *TargetsV1alpha1Client) HTTPTargets(namespace string) HTTPTargetInterfac return newHTTPTargets(c, namespace) } +func (c *TargetsV1alpha1Client) JiraTargets(namespace string) JiraTargetInterface { + return newJiraTargets(c, namespace) +} + func (c *TargetsV1alpha1Client) KafkaTargets(namespace string) KafkaTargetInterface { return newKafkaTargets(c, namespace) } @@ -60,6 +66,10 @@ func (c *TargetsV1alpha1Client) SalesforceTargets(namespace string) SalesforceTa return newSalesforceTargets(c, namespace) } +func (c *TargetsV1alpha1Client) ServiceNowTargets(namespace string) ServiceNowTargetInterface { + return newServiceNowTargets(c, namespace) +} + func (c *TargetsV1alpha1Client) SplunkTargets(namespace string) SplunkTargetInterface { return newSplunkTargets(c, namespace) } diff --git a/pkg/client/generated/informers/externalversions/generic.go b/pkg/client/generated/informers/externalversions/generic.go index 71fe8cb1..0af37304 100644 --- a/pkg/client/generated/informers/externalversions/generic.go +++ b/pkg/client/generated/informers/externalversions/generic.go @@ -85,6 +85,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Targets().V1alpha1().DatadogTargets().Informer()}, nil case targetsv1alpha1.SchemeGroupVersion.WithResource("httptargets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Targets().V1alpha1().HTTPTargets().Informer()}, nil + case targetsv1alpha1.SchemeGroupVersion.WithResource("jiratargets"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Targets().V1alpha1().JiraTargets().Informer()}, nil case targetsv1alpha1.SchemeGroupVersion.WithResource("kafkatargets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Targets().V1alpha1().KafkaTargets().Informer()}, nil case targetsv1alpha1.SchemeGroupVersion.WithResource("logzmetricstargets"): @@ -95,6 +97,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Targets().V1alpha1().NatsTargets().Informer()}, nil case targetsv1alpha1.SchemeGroupVersion.WithResource("salesforcetargets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Targets().V1alpha1().SalesforceTargets().Informer()}, nil + case targetsv1alpha1.SchemeGroupVersion.WithResource("servicenowtargets"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Targets().V1alpha1().ServiceNowTargets().Informer()}, nil case targetsv1alpha1.SchemeGroupVersion.WithResource("splunktargets"): return &genericInformer{resource: resource.GroupResource(), informer: f.Targets().V1alpha1().SplunkTargets().Informer()}, nil diff --git a/pkg/client/generated/informers/externalversions/targets/v1alpha1/interface.go b/pkg/client/generated/informers/externalversions/targets/v1alpha1/interface.go index eae1d068..41e3f93f 100644 --- a/pkg/client/generated/informers/externalversions/targets/v1alpha1/interface.go +++ b/pkg/client/generated/informers/externalversions/targets/v1alpha1/interface.go @@ -14,6 +14,8 @@ type Interface interface { DatadogTargets() DatadogTargetInformer // HTTPTargets returns a HTTPTargetInformer. HTTPTargets() HTTPTargetInformer + // JiraTargets returns a JiraTargetInformer. + JiraTargets() JiraTargetInformer // KafkaTargets returns a KafkaTargetInformer. KafkaTargets() KafkaTargetInformer // LogzMetricsTargets returns a LogzMetricsTargetInformer. @@ -24,6 +26,8 @@ type Interface interface { NatsTargets() NatsTargetInformer // SalesforceTargets returns a SalesforceTargetInformer. SalesforceTargets() SalesforceTargetInformer + // ServiceNowTargets returns a ServiceNowTargetInformer. + ServiceNowTargets() ServiceNowTargetInformer // SplunkTargets returns a SplunkTargetInformer. SplunkTargets() SplunkTargetInformer } @@ -54,6 +58,11 @@ func (v *version) HTTPTargets() HTTPTargetInformer { return &hTTPTargetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// JiraTargets returns a JiraTargetInformer. +func (v *version) JiraTargets() JiraTargetInformer { + return &jiraTargetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // KafkaTargets returns a KafkaTargetInformer. func (v *version) KafkaTargets() KafkaTargetInformer { return &kafkaTargetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} @@ -79,6 +88,11 @@ func (v *version) SalesforceTargets() SalesforceTargetInformer { return &salesforceTargetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// ServiceNowTargets returns a ServiceNowTargetInformer. +func (v *version) ServiceNowTargets() ServiceNowTargetInformer { + return &serviceNowTargetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // SplunkTargets returns a SplunkTargetInformer. func (v *version) SplunkTargets() SplunkTargetInformer { return &splunkTargetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/generated/informers/externalversions/targets/v1alpha1/jiratarget.go b/pkg/client/generated/informers/externalversions/targets/v1alpha1/jiratarget.go new file mode 100644 index 00000000..af0e7fba --- /dev/null +++ b/pkg/client/generated/informers/externalversions/targets/v1alpha1/jiratarget.go @@ -0,0 +1,74 @@ +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + targetsv1alpha1 "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + internalclientset "github.com/zeiss/typhoon/pkg/client/generated/clientset/internalclientset" + internalinterfaces "github.com/zeiss/typhoon/pkg/client/generated/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/listers/targets/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// JiraTargetInformer provides access to a shared informer and lister for +// JiraTargets. +type JiraTargetInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.JiraTargetLister +} + +type jiraTargetInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewJiraTargetInformer constructs a new informer for JiraTarget type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewJiraTargetInformer(client internalclientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredJiraTargetInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredJiraTargetInformer constructs a new informer for JiraTarget type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredJiraTargetInformer(client internalclientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TargetsV1alpha1().JiraTargets(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TargetsV1alpha1().JiraTargets(namespace).Watch(context.TODO(), options) + }, + }, + &targetsv1alpha1.JiraTarget{}, + resyncPeriod, + indexers, + ) +} + +func (f *jiraTargetInformer) defaultInformer(client internalclientset.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredJiraTargetInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *jiraTargetInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&targetsv1alpha1.JiraTarget{}, f.defaultInformer) +} + +func (f *jiraTargetInformer) Lister() v1alpha1.JiraTargetLister { + return v1alpha1.NewJiraTargetLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/generated/informers/externalversions/targets/v1alpha1/servicenowtarget.go b/pkg/client/generated/informers/externalversions/targets/v1alpha1/servicenowtarget.go new file mode 100644 index 00000000..8e36078c --- /dev/null +++ b/pkg/client/generated/informers/externalversions/targets/v1alpha1/servicenowtarget.go @@ -0,0 +1,74 @@ +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + targetsv1alpha1 "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + internalclientset "github.com/zeiss/typhoon/pkg/client/generated/clientset/internalclientset" + internalinterfaces "github.com/zeiss/typhoon/pkg/client/generated/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/listers/targets/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ServiceNowTargetInformer provides access to a shared informer and lister for +// ServiceNowTargets. +type ServiceNowTargetInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.ServiceNowTargetLister +} + +type serviceNowTargetInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewServiceNowTargetInformer constructs a new informer for ServiceNowTarget type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewServiceNowTargetInformer(client internalclientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredServiceNowTargetInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredServiceNowTargetInformer constructs a new informer for ServiceNowTarget type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredServiceNowTargetInformer(client internalclientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TargetsV1alpha1().ServiceNowTargets(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TargetsV1alpha1().ServiceNowTargets(namespace).Watch(context.TODO(), options) + }, + }, + &targetsv1alpha1.ServiceNowTarget{}, + resyncPeriod, + indexers, + ) +} + +func (f *serviceNowTargetInformer) defaultInformer(client internalclientset.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredServiceNowTargetInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *serviceNowTargetInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&targetsv1alpha1.ServiceNowTarget{}, f.defaultInformer) +} + +func (f *serviceNowTargetInformer) Lister() v1alpha1.ServiceNowTargetLister { + return v1alpha1.NewServiceNowTargetLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget/fake/fake.go b/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget/fake/fake.go new file mode 100644 index 00000000..34f2ee5e --- /dev/null +++ b/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget/fake/fake.go @@ -0,0 +1,24 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package fake + +import ( + context "context" + + fake "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/factory/fake" + jiratarget "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" +) + +var Get = jiratarget.Get + +func init() { + injection.Fake.RegisterInformer(withInformer) +} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := fake.Get(ctx) + inf := f.Targets().V1alpha1().JiraTargets() + return context.WithValue(ctx, jiratarget.Key{}, inf), inf.Informer() +} diff --git a/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget/filtered/fake/fake.go b/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget/filtered/fake/fake.go new file mode 100644 index 00000000..11e9ec72 --- /dev/null +++ b/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget/filtered/fake/fake.go @@ -0,0 +1,36 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package fake + +import ( + context "context" + + factoryfiltered "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/factory/filtered" + filtered "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget/filtered" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +var Get = filtered.Get + +func init() { + injection.Fake.RegisterFilteredInformers(withInformer) +} + +func withInformer(ctx context.Context) (context.Context, []controller.Informer) { + untyped := ctx.Value(factoryfiltered.LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") + } + labelSelectors := untyped.([]string) + infs := []controller.Informer{} + for _, selector := range labelSelectors { + f := factoryfiltered.Get(ctx, selector) + inf := f.Targets().V1alpha1().JiraTargets() + ctx = context.WithValue(ctx, filtered.Key{Selector: selector}, inf) + infs = append(infs, inf.Informer()) + } + return ctx, infs +} diff --git a/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget/filtered/jiratarget.go b/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget/filtered/jiratarget.go new file mode 100644 index 00000000..e9dd4f3f --- /dev/null +++ b/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget/filtered/jiratarget.go @@ -0,0 +1,49 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package filtered + +import ( + context "context" + + v1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/informers/externalversions/targets/v1alpha1" + filtered "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/factory/filtered" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterFilteredInformers(withInformer) +} + +// Key is used for associating the Informer inside the context.Context. +type Key struct { + Selector string +} + +func withInformer(ctx context.Context) (context.Context, []controller.Informer) { + untyped := ctx.Value(filtered.LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") + } + labelSelectors := untyped.([]string) + infs := []controller.Informer{} + for _, selector := range labelSelectors { + f := filtered.Get(ctx, selector) + inf := f.Targets().V1alpha1().JiraTargets() + ctx = context.WithValue(ctx, Key{Selector: selector}, inf) + infs = append(infs, inf.Informer()) + } + return ctx, infs +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context, selector string) v1alpha1.JiraTargetInformer { + untyped := ctx.Value(Key{Selector: selector}) + if untyped == nil { + logging.FromContext(ctx).Panicf( + "Unable to fetch github.com/zeiss/typhoon/pkg/client/generated/informers/externalversions/targets/v1alpha1.JiraTargetInformer with selector %s from context.", selector) + } + return untyped.(v1alpha1.JiraTargetInformer) +} diff --git a/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget/jiratarget.go b/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget/jiratarget.go new file mode 100644 index 00000000..597c7bd0 --- /dev/null +++ b/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget/jiratarget.go @@ -0,0 +1,36 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package jiratarget + +import ( + context "context" + + v1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/informers/externalversions/targets/v1alpha1" + factory "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/factory" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterInformer(withInformer) +} + +// Key is used for associating the Informer inside the context.Context. +type Key struct{} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := factory.Get(ctx) + inf := f.Targets().V1alpha1().JiraTargets() + return context.WithValue(ctx, Key{}, inf), inf.Informer() +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context) v1alpha1.JiraTargetInformer { + untyped := ctx.Value(Key{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch github.com/zeiss/typhoon/pkg/client/generated/informers/externalversions/targets/v1alpha1.JiraTargetInformer from context.") + } + return untyped.(v1alpha1.JiraTargetInformer) +} diff --git a/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget/fake/fake.go b/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget/fake/fake.go new file mode 100644 index 00000000..d38f2f19 --- /dev/null +++ b/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget/fake/fake.go @@ -0,0 +1,24 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package fake + +import ( + context "context" + + fake "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/factory/fake" + servicenowtarget "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" +) + +var Get = servicenowtarget.Get + +func init() { + injection.Fake.RegisterInformer(withInformer) +} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := fake.Get(ctx) + inf := f.Targets().V1alpha1().ServiceNowTargets() + return context.WithValue(ctx, servicenowtarget.Key{}, inf), inf.Informer() +} diff --git a/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget/filtered/fake/fake.go b/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget/filtered/fake/fake.go new file mode 100644 index 00000000..b5107441 --- /dev/null +++ b/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget/filtered/fake/fake.go @@ -0,0 +1,36 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package fake + +import ( + context "context" + + factoryfiltered "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/factory/filtered" + filtered "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget/filtered" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +var Get = filtered.Get + +func init() { + injection.Fake.RegisterFilteredInformers(withInformer) +} + +func withInformer(ctx context.Context) (context.Context, []controller.Informer) { + untyped := ctx.Value(factoryfiltered.LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") + } + labelSelectors := untyped.([]string) + infs := []controller.Informer{} + for _, selector := range labelSelectors { + f := factoryfiltered.Get(ctx, selector) + inf := f.Targets().V1alpha1().ServiceNowTargets() + ctx = context.WithValue(ctx, filtered.Key{Selector: selector}, inf) + infs = append(infs, inf.Informer()) + } + return ctx, infs +} diff --git a/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget/filtered/servicenowtarget.go b/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget/filtered/servicenowtarget.go new file mode 100644 index 00000000..ec0a915d --- /dev/null +++ b/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget/filtered/servicenowtarget.go @@ -0,0 +1,49 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package filtered + +import ( + context "context" + + v1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/informers/externalversions/targets/v1alpha1" + filtered "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/factory/filtered" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterFilteredInformers(withInformer) +} + +// Key is used for associating the Informer inside the context.Context. +type Key struct { + Selector string +} + +func withInformer(ctx context.Context) (context.Context, []controller.Informer) { + untyped := ctx.Value(filtered.LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") + } + labelSelectors := untyped.([]string) + infs := []controller.Informer{} + for _, selector := range labelSelectors { + f := filtered.Get(ctx, selector) + inf := f.Targets().V1alpha1().ServiceNowTargets() + ctx = context.WithValue(ctx, Key{Selector: selector}, inf) + infs = append(infs, inf.Informer()) + } + return ctx, infs +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context, selector string) v1alpha1.ServiceNowTargetInformer { + untyped := ctx.Value(Key{Selector: selector}) + if untyped == nil { + logging.FromContext(ctx).Panicf( + "Unable to fetch github.com/zeiss/typhoon/pkg/client/generated/informers/externalversions/targets/v1alpha1.ServiceNowTargetInformer with selector %s from context.", selector) + } + return untyped.(v1alpha1.ServiceNowTargetInformer) +} diff --git a/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget/servicenowtarget.go b/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget/servicenowtarget.go new file mode 100644 index 00000000..beb42374 --- /dev/null +++ b/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget/servicenowtarget.go @@ -0,0 +1,36 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package servicenowtarget + +import ( + context "context" + + v1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/informers/externalversions/targets/v1alpha1" + factory "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/factory" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterInformer(withInformer) +} + +// Key is used for associating the Informer inside the context.Context. +type Key struct{} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := factory.Get(ctx) + inf := f.Targets().V1alpha1().ServiceNowTargets() + return context.WithValue(ctx, Key{}, inf), inf.Informer() +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context) v1alpha1.ServiceNowTargetInformer { + untyped := ctx.Value(Key{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch github.com/zeiss/typhoon/pkg/client/generated/informers/externalversions/targets/v1alpha1.ServiceNowTargetInformer from context.") + } + return untyped.(v1alpha1.ServiceNowTargetInformer) +} diff --git a/pkg/client/generated/injection/reconciler/targets/v1alpha1/jiratarget/controller.go b/pkg/client/generated/injection/reconciler/targets/v1alpha1/jiratarget/controller.go new file mode 100644 index 00000000..0e396c32 --- /dev/null +++ b/pkg/client/generated/injection/reconciler/targets/v1alpha1/jiratarget/controller.go @@ -0,0 +1,154 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package jiratarget + +import ( + context "context" + fmt "fmt" + reflect "reflect" + strings "strings" + + internalclientsetscheme "github.com/zeiss/typhoon/pkg/client/generated/clientset/internalclientset/scheme" + client "github.com/zeiss/typhoon/pkg/client/generated/injection/client" + jiratarget "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget" + zap "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + scheme "k8s.io/client-go/kubernetes/scheme" + v1 "k8s.io/client-go/kubernetes/typed/core/v1" + record "k8s.io/client-go/tools/record" + kubeclient "knative.dev/pkg/client/injection/kube/client" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" + logkey "knative.dev/pkg/logging/logkey" + reconciler "knative.dev/pkg/reconciler" +) + +const ( + defaultControllerAgentName = "jiratarget-controller" + defaultFinalizerName = "jiratargets.targets.typhoon.zeiss.com" +) + +// NewImpl returns a controller.Impl that handles queuing and feeding work from +// the queue through an implementation of controller.Reconciler, delegating to +// the provided Interface and optional Finalizer methods. OptionsFn is used to return +// controller.ControllerOptions to be used by the internal reconciler. +func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsFn) *controller.Impl { + logger := logging.FromContext(ctx) + + // Check the options function input. It should be 0 or 1. + if len(optionsFns) > 1 { + logger.Fatal("Up to one options function is supported, found: ", len(optionsFns)) + } + + jiratargetInformer := jiratarget.Get(ctx) + + lister := jiratargetInformer.Lister() + + var promoteFilterFunc func(obj interface{}) bool + var promoteFunc = func(bkt reconciler.Bucket) {} + + rec := &reconcilerImpl{ + LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + + // Signal promotion event + promoteFunc(bkt) + + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + if promoteFilterFunc != nil { + if ok := promoteFilterFunc(elt); !ok { + continue + } + } + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + Client: client.Get(ctx), + Lister: lister, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + ctrType := reflect.TypeOf(r).Elem() + ctrTypeName := fmt.Sprintf("%s.%s", ctrType.PkgPath(), ctrType.Name()) + ctrTypeName = strings.ReplaceAll(ctrTypeName, "/", ".") + + logger = logger.With( + zap.String(logkey.ControllerType, ctrTypeName), + zap.String(logkey.Kind, "targets.typhoon.zeiss.com.JiraTarget"), + ) + + impl := controller.NewContext(ctx, rec, controller.ControllerOptions{WorkQueueName: ctrTypeName, Logger: logger}) + agentName := defaultControllerAgentName + + // Pass impl to the options. Save any optional results. + for _, fn := range optionsFns { + opts := fn(impl) + if opts.ConfigStore != nil { + rec.configStore = opts.ConfigStore + } + if opts.FinalizerName != "" { + rec.finalizerName = opts.FinalizerName + } + if opts.AgentName != "" { + agentName = opts.AgentName + } + if opts.SkipStatusUpdates { + rec.skipStatusUpdates = true + } + if opts.DemoteFunc != nil { + rec.DemoteFunc = opts.DemoteFunc + } + if opts.PromoteFilterFunc != nil { + promoteFilterFunc = opts.PromoteFilterFunc + } + if opts.PromoteFunc != nil { + promoteFunc = opts.PromoteFunc + } + } + + rec.Recorder = createRecorder(ctx, agentName) + + return impl +} + +func createRecorder(ctx context.Context, agentName string) record.EventRecorder { + logger := logging.FromContext(ctx) + + recorder := controller.GetEventRecorder(ctx) + if recorder == nil { + // Create event broadcaster + logger.Debug("Creating event broadcaster") + eventBroadcaster := record.NewBroadcaster() + watches := []watch.Interface{ + eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof), + eventBroadcaster.StartRecordingToSink( + &v1.EventSinkImpl{Interface: kubeclient.Get(ctx).CoreV1().Events("")}), + } + recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: agentName}) + go func() { + <-ctx.Done() + for _, w := range watches { + w.Stop() + } + }() + } + + return recorder +} + +func init() { + internalclientsetscheme.AddToScheme(scheme.Scheme) +} diff --git a/pkg/client/generated/injection/reconciler/targets/v1alpha1/jiratarget/reconciler.go b/pkg/client/generated/injection/reconciler/targets/v1alpha1/jiratarget/reconciler.go new file mode 100644 index 00000000..db4c6d43 --- /dev/null +++ b/pkg/client/generated/injection/reconciler/targets/v1alpha1/jiratarget/reconciler.go @@ -0,0 +1,424 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package jiratarget + +import ( + context "context" + json "encoding/json" + fmt "fmt" + + v1alpha1 "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + internalclientset "github.com/zeiss/typhoon/pkg/client/generated/clientset/internalclientset" + targetsv1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/listers/targets/v1alpha1" + zap "go.uber.org/zap" + "go.uber.org/zap/zapcore" + v1 "k8s.io/api/core/v1" + equality "k8s.io/apimachinery/pkg/api/equality" + errors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + sets "k8s.io/apimachinery/pkg/util/sets" + record "k8s.io/client-go/tools/record" + controller "knative.dev/pkg/controller" + kmp "knative.dev/pkg/kmp" + logging "knative.dev/pkg/logging" + reconciler "knative.dev/pkg/reconciler" +) + +// Interface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1alpha1.JiraTarget. +type Interface interface { + // ReconcileKind implements custom logic to reconcile v1alpha1.JiraTarget. Any changes + // to the objects .Status or .Finalizers will be propagated to the stored + // object. It is recommended that implementors do not call any update calls + // for the Kind inside of ReconcileKind, it is the responsibility of the calling + // controller to propagate those properties. The resource passed to ReconcileKind + // will always have an empty deletion timestamp. + ReconcileKind(ctx context.Context, o *v1alpha1.JiraTarget) reconciler.Event +} + +// Finalizer defines the strongly typed interfaces to be implemented by a +// controller finalizing v1alpha1.JiraTarget. +type Finalizer interface { + // FinalizeKind implements custom logic to finalize v1alpha1.JiraTarget. Any changes + // to the objects .Status or .Finalizers will be ignored. Returning a nil or + // Normal type reconciler.Event will allow the finalizer to be deleted on + // the resource. The resource passed to FinalizeKind will always have a set + // deletion timestamp. + FinalizeKind(ctx context.Context, o *v1alpha1.JiraTarget) reconciler.Event +} + +// ReadOnlyInterface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1alpha1.JiraTarget if they want to process resources for which +// they are not the leader. +type ReadOnlyInterface interface { + // ObserveKind implements logic to observe v1alpha1.JiraTarget. + // This method should not write to the API. + ObserveKind(ctx context.Context, o *v1alpha1.JiraTarget) reconciler.Event +} + +type doReconcile func(ctx context.Context, o *v1alpha1.JiraTarget) reconciler.Event + +// reconcilerImpl implements controller.Reconciler for v1alpha1.JiraTarget resources. +type reconcilerImpl struct { + // LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware. + reconciler.LeaderAwareFuncs + + // Client is used to write back status updates. + Client internalclientset.Interface + + // Listers index properties about resources. + Lister targetsv1alpha1.JiraTargetLister + + // Recorder is an event recorder for recording Event resources to the + // Kubernetes API. + Recorder record.EventRecorder + + // configStore allows for decorating a context with config maps. + // +optional + configStore reconciler.ConfigStore + + // reconciler is the implementation of the business logic of the resource. + reconciler Interface + + // finalizerName is the name of the finalizer to reconcile. + finalizerName string + + // skipStatusUpdates configures whether or not this reconciler automatically updates + // the status of the reconciled resource. + skipStatusUpdates bool +} + +// Check that our Reconciler implements controller.Reconciler. +var _ controller.Reconciler = (*reconcilerImpl)(nil) + +// Check that our generated Reconciler is always LeaderAware. +var _ reconciler.LeaderAware = (*reconcilerImpl)(nil) + +func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client internalclientset.Interface, lister targetsv1alpha1.JiraTargetLister, recorder record.EventRecorder, r Interface, options ...controller.Options) controller.Reconciler { + // Check the options function input. It should be 0 or 1. + if len(options) > 1 { + logger.Fatal("Up to one options struct is supported, found: ", len(options)) + } + + // Fail fast when users inadvertently implement the other LeaderAware interface. + // For the typed reconcilers, Promote shouldn't take any arguments. + if _, ok := r.(reconciler.LeaderAware); ok { + logger.Fatalf("%T implements the incorrect LeaderAware interface. Promote() should not take an argument as genreconciler handles the enqueuing automatically.", r) + } + + rec := &reconcilerImpl{ + LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + // TODO: Consider letting users specify a filter in options. + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + Client: client, + Lister: lister, + Recorder: recorder, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + for _, opts := range options { + if opts.ConfigStore != nil { + rec.configStore = opts.ConfigStore + } + if opts.FinalizerName != "" { + rec.finalizerName = opts.FinalizerName + } + if opts.SkipStatusUpdates { + rec.skipStatusUpdates = true + } + if opts.DemoteFunc != nil { + rec.DemoteFunc = opts.DemoteFunc + } + } + + return rec +} + +// Reconcile implements controller.Reconciler +func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error { + logger := logging.FromContext(ctx) + + // Initialize the reconciler state. This will convert the namespace/name + // string into a distinct namespace and name, determine if this instance of + // the reconciler is the leader, and any additional interfaces implemented + // by the reconciler. Returns an error is the resource key is invalid. + s, err := newState(key, r) + if err != nil { + logger.Error("Invalid resource key: ", key) + return nil + } + + // If we are not the leader, and we don't implement either ReadOnly + // observer interfaces, then take a fast-path out. + if s.isNotLeaderNorObserver() { + return controller.NewSkipKey(key) + } + + // If configStore is set, attach the frozen configuration to the context. + if r.configStore != nil { + ctx = r.configStore.ToContext(ctx) + } + + // Add the recorder to context. + ctx = controller.WithEventRecorder(ctx, r.Recorder) + + // Get the resource with this namespace/name. + + getter := r.Lister.JiraTargets(s.namespace) + + original, err := getter.Get(s.name) + + if errors.IsNotFound(err) { + // The resource may no longer exist, in which case we stop processing and call + // the ObserveDeletion handler if appropriate. + logger.Debugf("Resource %q no longer exists", key) + if del, ok := r.reconciler.(reconciler.OnDeletionInterface); ok { + return del.ObserveDeletion(ctx, types.NamespacedName{ + Namespace: s.namespace, + Name: s.name, + }) + } + return nil + } else if err != nil { + return err + } + + // Don't modify the informers copy. + resource := original.DeepCopy() + + var reconcileEvent reconciler.Event + + name, do := s.reconcileMethodFor(resource) + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", name)) + switch name { + case reconciler.DoReconcileKind: + // Set and update the finalizer on resource if r.reconciler + // implements Finalizer. + if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil { + return fmt.Errorf("failed to set finalizers: %w", err) + } + + if !r.skipStatusUpdates { + reconciler.PreProcessReconcile(ctx, resource) + } + + // Reconcile this copy of the resource and then write back any status + // updates regardless of whether the reconciliation errored out. + reconcileEvent = do(ctx, resource) + + if !r.skipStatusUpdates { + reconciler.PostProcessReconcile(ctx, resource, original) + } + + case reconciler.DoFinalizeKind: + // For finalizing reconcilers, if this resource being marked for deletion + // and reconciled cleanly (nil or normal event), remove the finalizer. + reconcileEvent = do(ctx, resource) + + if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil { + return fmt.Errorf("failed to clear finalizers: %w", err) + } + + case reconciler.DoObserveKind: + // Observe any changes to this resource, since we are not the leader. + reconcileEvent = do(ctx, resource) + + } + + // Synchronize the status. + switch { + case r.skipStatusUpdates: + // This reconciler implementation is configured to skip resource updates. + // This may mean this reconciler does not observe spec, but reconciles external changes. + case equality.Semantic.DeepEqual(original.Status, resource.Status): + // If we didn't change anything then don't call updateStatus. + // This is important because the copy we loaded from the injectionInformer's + // cache may be stale and we don't want to overwrite a prior update + // to status with this stale state. + case !s.isLeader: + // High-availability reconcilers may have many replicas watching the resource, but only + // the elected leader is expected to write modifications. + logger.Warn("Saw status changes when we aren't the leader!") + default: + if err = r.updateStatus(ctx, logger, original, resource); err != nil { + logger.Warnw("Failed to update resource status", zap.Error(err)) + r.Recorder.Eventf(resource, v1.EventTypeWarning, "UpdateFailed", + "Failed to update status for %q: %v", resource.Name, err) + return err + } + } + + // Report the reconciler event, if any. + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + if reconciler.EventAs(reconcileEvent, &event) { + logger.Infow("Returned an event", zap.Any("event", reconcileEvent)) + r.Recorder.Event(resource, event.EventType, event.Reason, event.Error()) + + // the event was wrapped inside an error, consider the reconciliation as failed + if _, isEvent := reconcileEvent.(*reconciler.ReconcilerEvent); !isEvent { + return reconcileEvent + } + return nil + } + + if controller.IsSkipKey(reconcileEvent) { + // This is a wrapped error, don't emit an event. + } else if ok, _ := controller.IsRequeueKey(reconcileEvent); ok { + // This is a wrapped error, don't emit an event. + } else { + logger.Errorw("Returned an error", zap.Error(reconcileEvent)) + r.Recorder.Event(resource, v1.EventTypeWarning, "InternalError", reconcileEvent.Error()) + } + return reconcileEvent + } + + return nil +} + +func (r *reconcilerImpl) updateStatus(ctx context.Context, logger *zap.SugaredLogger, existing *v1alpha1.JiraTarget, desired *v1alpha1.JiraTarget) error { + existing = existing.DeepCopy() + return reconciler.RetryUpdateConflicts(func(attempts int) (err error) { + // The first iteration tries to use the injectionInformer's state, subsequent attempts fetch the latest state via API. + if attempts > 0 { + + getter := r.Client.TargetsV1alpha1().JiraTargets(desired.Namespace) + + existing, err = getter.Get(ctx, desired.Name, metav1.GetOptions{}) + if err != nil { + return err + } + } + + // If there's nothing to update, just return. + if equality.Semantic.DeepEqual(existing.Status, desired.Status) { + return nil + } + + if logger.Desugar().Core().Enabled(zapcore.DebugLevel) { + if diff, err := kmp.SafeDiff(existing.Status, desired.Status); err == nil && diff != "" { + logger.Debug("Updating status with: ", diff) + } + } + + existing.Status = desired.Status + + updater := r.Client.TargetsV1alpha1().JiraTargets(existing.Namespace) + + _, err = updater.UpdateStatus(ctx, existing, metav1.UpdateOptions{}) + return err + }) +} + +// updateFinalizersFiltered will update the Finalizers of the resource. +// TODO: this method could be generic and sync all finalizers. For now it only +// updates defaultFinalizerName or its override. +func (r *reconcilerImpl) updateFinalizersFiltered(ctx context.Context, resource *v1alpha1.JiraTarget, desiredFinalizers sets.Set[string]) (*v1alpha1.JiraTarget, error) { + // Don't modify the informers copy. + existing := resource.DeepCopy() + + var finalizers []string + + // If there's nothing to update, just return. + existingFinalizers := sets.New[string](existing.Finalizers...) + + if desiredFinalizers.Has(r.finalizerName) { + if existingFinalizers.Has(r.finalizerName) { + // Nothing to do. + return resource, nil + } + // Add the finalizer. + finalizers = append(existing.Finalizers, r.finalizerName) + } else { + if !existingFinalizers.Has(r.finalizerName) { + // Nothing to do. + return resource, nil + } + // Remove the finalizer. + existingFinalizers.Delete(r.finalizerName) + finalizers = sets.List(existingFinalizers) + } + + mergePatch := map[string]interface{}{ + "metadata": map[string]interface{}{ + "finalizers": finalizers, + "resourceVersion": existing.ResourceVersion, + }, + } + + patch, err := json.Marshal(mergePatch) + if err != nil { + return resource, err + } + + patcher := r.Client.TargetsV1alpha1().JiraTargets(resource.Namespace) + + resourceName := resource.Name + updated, err := patcher.Patch(ctx, resourceName, types.MergePatchType, patch, metav1.PatchOptions{}) + if err != nil { + r.Recorder.Eventf(existing, v1.EventTypeWarning, "FinalizerUpdateFailed", + "Failed to update finalizers for %q: %v", resourceName, err) + } else { + r.Recorder.Eventf(updated, v1.EventTypeNormal, "FinalizerUpdate", + "Updated %q finalizers", resource.GetName()) + } + return updated, err +} + +func (r *reconcilerImpl) setFinalizerIfFinalizer(ctx context.Context, resource *v1alpha1.JiraTarget) (*v1alpha1.JiraTarget, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + + finalizers := sets.New[string](resource.Finalizers...) + + // If this resource is not being deleted, mark the finalizer. + if resource.GetDeletionTimestamp().IsZero() { + finalizers.Insert(r.finalizerName) + } + + // Synchronize the finalizers filtered by r.finalizerName. + return r.updateFinalizersFiltered(ctx, resource, finalizers) +} + +func (r *reconcilerImpl) clearFinalizer(ctx context.Context, resource *v1alpha1.JiraTarget, reconcileEvent reconciler.Event) (*v1alpha1.JiraTarget, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + if resource.GetDeletionTimestamp().IsZero() { + return resource, nil + } + + finalizers := sets.New[string](resource.Finalizers...) + + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + if reconciler.EventAs(reconcileEvent, &event) { + if event.EventType == v1.EventTypeNormal { + finalizers.Delete(r.finalizerName) + } + } + } else { + finalizers.Delete(r.finalizerName) + } + + // Synchronize the finalizers filtered by r.finalizerName. + return r.updateFinalizersFiltered(ctx, resource, finalizers) +} diff --git a/pkg/client/generated/injection/reconciler/targets/v1alpha1/jiratarget/state.go b/pkg/client/generated/injection/reconciler/targets/v1alpha1/jiratarget/state.go new file mode 100644 index 00000000..767b2e1f --- /dev/null +++ b/pkg/client/generated/injection/reconciler/targets/v1alpha1/jiratarget/state.go @@ -0,0 +1,81 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package jiratarget + +import ( + fmt "fmt" + + v1alpha1 "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + types "k8s.io/apimachinery/pkg/types" + cache "k8s.io/client-go/tools/cache" + reconciler "knative.dev/pkg/reconciler" +) + +// state is used to track the state of a reconciler in a single run. +type state struct { + // key is the original reconciliation key from the queue. + key string + // namespace is the namespace split from the reconciliation key. + namespace string + // name is the name split from the reconciliation key. + name string + // reconciler is the reconciler. + reconciler Interface + // roi is the read only interface cast of the reconciler. + roi ReadOnlyInterface + // isROI (Read Only Interface) the reconciler only observes reconciliation. + isROI bool + // isLeader the instance of the reconciler is the elected leader. + isLeader bool +} + +func newState(key string, r *reconcilerImpl) (*state, error) { + // Convert the namespace/name string into a distinct namespace and name. + namespace, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + return nil, fmt.Errorf("invalid resource key: %s", key) + } + + roi, isROI := r.reconciler.(ReadOnlyInterface) + + isLeader := r.IsLeaderFor(types.NamespacedName{ + Namespace: namespace, + Name: name, + }) + + return &state{ + key: key, + namespace: namespace, + name: name, + reconciler: r.reconciler, + roi: roi, + isROI: isROI, + isLeader: isLeader, + }, nil +} + +// isNotLeaderNorObserver checks to see if this reconciler with the current +// state is enabled to do any work or not. +// isNotLeaderNorObserver returns true when there is no work possible for the +// reconciler. +func (s *state) isNotLeaderNorObserver() bool { + if !s.isLeader && !s.isROI { + // If we are not the leader, and we don't implement the ReadOnly + // interface, then take a fast-path out. + return true + } + return false +} + +func (s *state) reconcileMethodFor(o *v1alpha1.JiraTarget) (string, doReconcile) { + if o.GetDeletionTimestamp().IsZero() { + if s.isLeader { + return reconciler.DoReconcileKind, s.reconciler.ReconcileKind + } else if s.isROI { + return reconciler.DoObserveKind, s.roi.ObserveKind + } + } else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok { + return reconciler.DoFinalizeKind, fin.FinalizeKind + } + return "unknown", nil +} diff --git a/pkg/client/generated/injection/reconciler/targets/v1alpha1/servicenowtarget/controller.go b/pkg/client/generated/injection/reconciler/targets/v1alpha1/servicenowtarget/controller.go new file mode 100644 index 00000000..d79cf596 --- /dev/null +++ b/pkg/client/generated/injection/reconciler/targets/v1alpha1/servicenowtarget/controller.go @@ -0,0 +1,154 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package servicenowtarget + +import ( + context "context" + fmt "fmt" + reflect "reflect" + strings "strings" + + internalclientsetscheme "github.com/zeiss/typhoon/pkg/client/generated/clientset/internalclientset/scheme" + client "github.com/zeiss/typhoon/pkg/client/generated/injection/client" + servicenowtarget "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget" + zap "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + scheme "k8s.io/client-go/kubernetes/scheme" + v1 "k8s.io/client-go/kubernetes/typed/core/v1" + record "k8s.io/client-go/tools/record" + kubeclient "knative.dev/pkg/client/injection/kube/client" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" + logkey "knative.dev/pkg/logging/logkey" + reconciler "knative.dev/pkg/reconciler" +) + +const ( + defaultControllerAgentName = "servicenowtarget-controller" + defaultFinalizerName = "servicenowtargets.targets.typhoon.zeiss.com" +) + +// NewImpl returns a controller.Impl that handles queuing and feeding work from +// the queue through an implementation of controller.Reconciler, delegating to +// the provided Interface and optional Finalizer methods. OptionsFn is used to return +// controller.ControllerOptions to be used by the internal reconciler. +func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsFn) *controller.Impl { + logger := logging.FromContext(ctx) + + // Check the options function input. It should be 0 or 1. + if len(optionsFns) > 1 { + logger.Fatal("Up to one options function is supported, found: ", len(optionsFns)) + } + + servicenowtargetInformer := servicenowtarget.Get(ctx) + + lister := servicenowtargetInformer.Lister() + + var promoteFilterFunc func(obj interface{}) bool + var promoteFunc = func(bkt reconciler.Bucket) {} + + rec := &reconcilerImpl{ + LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + + // Signal promotion event + promoteFunc(bkt) + + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + if promoteFilterFunc != nil { + if ok := promoteFilterFunc(elt); !ok { + continue + } + } + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + Client: client.Get(ctx), + Lister: lister, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + ctrType := reflect.TypeOf(r).Elem() + ctrTypeName := fmt.Sprintf("%s.%s", ctrType.PkgPath(), ctrType.Name()) + ctrTypeName = strings.ReplaceAll(ctrTypeName, "/", ".") + + logger = logger.With( + zap.String(logkey.ControllerType, ctrTypeName), + zap.String(logkey.Kind, "targets.typhoon.zeiss.com.ServiceNowTarget"), + ) + + impl := controller.NewContext(ctx, rec, controller.ControllerOptions{WorkQueueName: ctrTypeName, Logger: logger}) + agentName := defaultControllerAgentName + + // Pass impl to the options. Save any optional results. + for _, fn := range optionsFns { + opts := fn(impl) + if opts.ConfigStore != nil { + rec.configStore = opts.ConfigStore + } + if opts.FinalizerName != "" { + rec.finalizerName = opts.FinalizerName + } + if opts.AgentName != "" { + agentName = opts.AgentName + } + if opts.SkipStatusUpdates { + rec.skipStatusUpdates = true + } + if opts.DemoteFunc != nil { + rec.DemoteFunc = opts.DemoteFunc + } + if opts.PromoteFilterFunc != nil { + promoteFilterFunc = opts.PromoteFilterFunc + } + if opts.PromoteFunc != nil { + promoteFunc = opts.PromoteFunc + } + } + + rec.Recorder = createRecorder(ctx, agentName) + + return impl +} + +func createRecorder(ctx context.Context, agentName string) record.EventRecorder { + logger := logging.FromContext(ctx) + + recorder := controller.GetEventRecorder(ctx) + if recorder == nil { + // Create event broadcaster + logger.Debug("Creating event broadcaster") + eventBroadcaster := record.NewBroadcaster() + watches := []watch.Interface{ + eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof), + eventBroadcaster.StartRecordingToSink( + &v1.EventSinkImpl{Interface: kubeclient.Get(ctx).CoreV1().Events("")}), + } + recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: agentName}) + go func() { + <-ctx.Done() + for _, w := range watches { + w.Stop() + } + }() + } + + return recorder +} + +func init() { + internalclientsetscheme.AddToScheme(scheme.Scheme) +} diff --git a/pkg/client/generated/injection/reconciler/targets/v1alpha1/servicenowtarget/reconciler.go b/pkg/client/generated/injection/reconciler/targets/v1alpha1/servicenowtarget/reconciler.go new file mode 100644 index 00000000..b1fdaaba --- /dev/null +++ b/pkg/client/generated/injection/reconciler/targets/v1alpha1/servicenowtarget/reconciler.go @@ -0,0 +1,424 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package servicenowtarget + +import ( + context "context" + json "encoding/json" + fmt "fmt" + + v1alpha1 "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + internalclientset "github.com/zeiss/typhoon/pkg/client/generated/clientset/internalclientset" + targetsv1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/listers/targets/v1alpha1" + zap "go.uber.org/zap" + "go.uber.org/zap/zapcore" + v1 "k8s.io/api/core/v1" + equality "k8s.io/apimachinery/pkg/api/equality" + errors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + sets "k8s.io/apimachinery/pkg/util/sets" + record "k8s.io/client-go/tools/record" + controller "knative.dev/pkg/controller" + kmp "knative.dev/pkg/kmp" + logging "knative.dev/pkg/logging" + reconciler "knative.dev/pkg/reconciler" +) + +// Interface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1alpha1.ServiceNowTarget. +type Interface interface { + // ReconcileKind implements custom logic to reconcile v1alpha1.ServiceNowTarget. Any changes + // to the objects .Status or .Finalizers will be propagated to the stored + // object. It is recommended that implementors do not call any update calls + // for the Kind inside of ReconcileKind, it is the responsibility of the calling + // controller to propagate those properties. The resource passed to ReconcileKind + // will always have an empty deletion timestamp. + ReconcileKind(ctx context.Context, o *v1alpha1.ServiceNowTarget) reconciler.Event +} + +// Finalizer defines the strongly typed interfaces to be implemented by a +// controller finalizing v1alpha1.ServiceNowTarget. +type Finalizer interface { + // FinalizeKind implements custom logic to finalize v1alpha1.ServiceNowTarget. Any changes + // to the objects .Status or .Finalizers will be ignored. Returning a nil or + // Normal type reconciler.Event will allow the finalizer to be deleted on + // the resource. The resource passed to FinalizeKind will always have a set + // deletion timestamp. + FinalizeKind(ctx context.Context, o *v1alpha1.ServiceNowTarget) reconciler.Event +} + +// ReadOnlyInterface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1alpha1.ServiceNowTarget if they want to process resources for which +// they are not the leader. +type ReadOnlyInterface interface { + // ObserveKind implements logic to observe v1alpha1.ServiceNowTarget. + // This method should not write to the API. + ObserveKind(ctx context.Context, o *v1alpha1.ServiceNowTarget) reconciler.Event +} + +type doReconcile func(ctx context.Context, o *v1alpha1.ServiceNowTarget) reconciler.Event + +// reconcilerImpl implements controller.Reconciler for v1alpha1.ServiceNowTarget resources. +type reconcilerImpl struct { + // LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware. + reconciler.LeaderAwareFuncs + + // Client is used to write back status updates. + Client internalclientset.Interface + + // Listers index properties about resources. + Lister targetsv1alpha1.ServiceNowTargetLister + + // Recorder is an event recorder for recording Event resources to the + // Kubernetes API. + Recorder record.EventRecorder + + // configStore allows for decorating a context with config maps. + // +optional + configStore reconciler.ConfigStore + + // reconciler is the implementation of the business logic of the resource. + reconciler Interface + + // finalizerName is the name of the finalizer to reconcile. + finalizerName string + + // skipStatusUpdates configures whether or not this reconciler automatically updates + // the status of the reconciled resource. + skipStatusUpdates bool +} + +// Check that our Reconciler implements controller.Reconciler. +var _ controller.Reconciler = (*reconcilerImpl)(nil) + +// Check that our generated Reconciler is always LeaderAware. +var _ reconciler.LeaderAware = (*reconcilerImpl)(nil) + +func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client internalclientset.Interface, lister targetsv1alpha1.ServiceNowTargetLister, recorder record.EventRecorder, r Interface, options ...controller.Options) controller.Reconciler { + // Check the options function input. It should be 0 or 1. + if len(options) > 1 { + logger.Fatal("Up to one options struct is supported, found: ", len(options)) + } + + // Fail fast when users inadvertently implement the other LeaderAware interface. + // For the typed reconcilers, Promote shouldn't take any arguments. + if _, ok := r.(reconciler.LeaderAware); ok { + logger.Fatalf("%T implements the incorrect LeaderAware interface. Promote() should not take an argument as genreconciler handles the enqueuing automatically.", r) + } + + rec := &reconcilerImpl{ + LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + // TODO: Consider letting users specify a filter in options. + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + Client: client, + Lister: lister, + Recorder: recorder, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + for _, opts := range options { + if opts.ConfigStore != nil { + rec.configStore = opts.ConfigStore + } + if opts.FinalizerName != "" { + rec.finalizerName = opts.FinalizerName + } + if opts.SkipStatusUpdates { + rec.skipStatusUpdates = true + } + if opts.DemoteFunc != nil { + rec.DemoteFunc = opts.DemoteFunc + } + } + + return rec +} + +// Reconcile implements controller.Reconciler +func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error { + logger := logging.FromContext(ctx) + + // Initialize the reconciler state. This will convert the namespace/name + // string into a distinct namespace and name, determine if this instance of + // the reconciler is the leader, and any additional interfaces implemented + // by the reconciler. Returns an error is the resource key is invalid. + s, err := newState(key, r) + if err != nil { + logger.Error("Invalid resource key: ", key) + return nil + } + + // If we are not the leader, and we don't implement either ReadOnly + // observer interfaces, then take a fast-path out. + if s.isNotLeaderNorObserver() { + return controller.NewSkipKey(key) + } + + // If configStore is set, attach the frozen configuration to the context. + if r.configStore != nil { + ctx = r.configStore.ToContext(ctx) + } + + // Add the recorder to context. + ctx = controller.WithEventRecorder(ctx, r.Recorder) + + // Get the resource with this namespace/name. + + getter := r.Lister.ServiceNowTargets(s.namespace) + + original, err := getter.Get(s.name) + + if errors.IsNotFound(err) { + // The resource may no longer exist, in which case we stop processing and call + // the ObserveDeletion handler if appropriate. + logger.Debugf("Resource %q no longer exists", key) + if del, ok := r.reconciler.(reconciler.OnDeletionInterface); ok { + return del.ObserveDeletion(ctx, types.NamespacedName{ + Namespace: s.namespace, + Name: s.name, + }) + } + return nil + } else if err != nil { + return err + } + + // Don't modify the informers copy. + resource := original.DeepCopy() + + var reconcileEvent reconciler.Event + + name, do := s.reconcileMethodFor(resource) + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", name)) + switch name { + case reconciler.DoReconcileKind: + // Set and update the finalizer on resource if r.reconciler + // implements Finalizer. + if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil { + return fmt.Errorf("failed to set finalizers: %w", err) + } + + if !r.skipStatusUpdates { + reconciler.PreProcessReconcile(ctx, resource) + } + + // Reconcile this copy of the resource and then write back any status + // updates regardless of whether the reconciliation errored out. + reconcileEvent = do(ctx, resource) + + if !r.skipStatusUpdates { + reconciler.PostProcessReconcile(ctx, resource, original) + } + + case reconciler.DoFinalizeKind: + // For finalizing reconcilers, if this resource being marked for deletion + // and reconciled cleanly (nil or normal event), remove the finalizer. + reconcileEvent = do(ctx, resource) + + if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil { + return fmt.Errorf("failed to clear finalizers: %w", err) + } + + case reconciler.DoObserveKind: + // Observe any changes to this resource, since we are not the leader. + reconcileEvent = do(ctx, resource) + + } + + // Synchronize the status. + switch { + case r.skipStatusUpdates: + // This reconciler implementation is configured to skip resource updates. + // This may mean this reconciler does not observe spec, but reconciles external changes. + case equality.Semantic.DeepEqual(original.Status, resource.Status): + // If we didn't change anything then don't call updateStatus. + // This is important because the copy we loaded from the injectionInformer's + // cache may be stale and we don't want to overwrite a prior update + // to status with this stale state. + case !s.isLeader: + // High-availability reconcilers may have many replicas watching the resource, but only + // the elected leader is expected to write modifications. + logger.Warn("Saw status changes when we aren't the leader!") + default: + if err = r.updateStatus(ctx, logger, original, resource); err != nil { + logger.Warnw("Failed to update resource status", zap.Error(err)) + r.Recorder.Eventf(resource, v1.EventTypeWarning, "UpdateFailed", + "Failed to update status for %q: %v", resource.Name, err) + return err + } + } + + // Report the reconciler event, if any. + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + if reconciler.EventAs(reconcileEvent, &event) { + logger.Infow("Returned an event", zap.Any("event", reconcileEvent)) + r.Recorder.Event(resource, event.EventType, event.Reason, event.Error()) + + // the event was wrapped inside an error, consider the reconciliation as failed + if _, isEvent := reconcileEvent.(*reconciler.ReconcilerEvent); !isEvent { + return reconcileEvent + } + return nil + } + + if controller.IsSkipKey(reconcileEvent) { + // This is a wrapped error, don't emit an event. + } else if ok, _ := controller.IsRequeueKey(reconcileEvent); ok { + // This is a wrapped error, don't emit an event. + } else { + logger.Errorw("Returned an error", zap.Error(reconcileEvent)) + r.Recorder.Event(resource, v1.EventTypeWarning, "InternalError", reconcileEvent.Error()) + } + return reconcileEvent + } + + return nil +} + +func (r *reconcilerImpl) updateStatus(ctx context.Context, logger *zap.SugaredLogger, existing *v1alpha1.ServiceNowTarget, desired *v1alpha1.ServiceNowTarget) error { + existing = existing.DeepCopy() + return reconciler.RetryUpdateConflicts(func(attempts int) (err error) { + // The first iteration tries to use the injectionInformer's state, subsequent attempts fetch the latest state via API. + if attempts > 0 { + + getter := r.Client.TargetsV1alpha1().ServiceNowTargets(desired.Namespace) + + existing, err = getter.Get(ctx, desired.Name, metav1.GetOptions{}) + if err != nil { + return err + } + } + + // If there's nothing to update, just return. + if equality.Semantic.DeepEqual(existing.Status, desired.Status) { + return nil + } + + if logger.Desugar().Core().Enabled(zapcore.DebugLevel) { + if diff, err := kmp.SafeDiff(existing.Status, desired.Status); err == nil && diff != "" { + logger.Debug("Updating status with: ", diff) + } + } + + existing.Status = desired.Status + + updater := r.Client.TargetsV1alpha1().ServiceNowTargets(existing.Namespace) + + _, err = updater.UpdateStatus(ctx, existing, metav1.UpdateOptions{}) + return err + }) +} + +// updateFinalizersFiltered will update the Finalizers of the resource. +// TODO: this method could be generic and sync all finalizers. For now it only +// updates defaultFinalizerName or its override. +func (r *reconcilerImpl) updateFinalizersFiltered(ctx context.Context, resource *v1alpha1.ServiceNowTarget, desiredFinalizers sets.Set[string]) (*v1alpha1.ServiceNowTarget, error) { + // Don't modify the informers copy. + existing := resource.DeepCopy() + + var finalizers []string + + // If there's nothing to update, just return. + existingFinalizers := sets.New[string](existing.Finalizers...) + + if desiredFinalizers.Has(r.finalizerName) { + if existingFinalizers.Has(r.finalizerName) { + // Nothing to do. + return resource, nil + } + // Add the finalizer. + finalizers = append(existing.Finalizers, r.finalizerName) + } else { + if !existingFinalizers.Has(r.finalizerName) { + // Nothing to do. + return resource, nil + } + // Remove the finalizer. + existingFinalizers.Delete(r.finalizerName) + finalizers = sets.List(existingFinalizers) + } + + mergePatch := map[string]interface{}{ + "metadata": map[string]interface{}{ + "finalizers": finalizers, + "resourceVersion": existing.ResourceVersion, + }, + } + + patch, err := json.Marshal(mergePatch) + if err != nil { + return resource, err + } + + patcher := r.Client.TargetsV1alpha1().ServiceNowTargets(resource.Namespace) + + resourceName := resource.Name + updated, err := patcher.Patch(ctx, resourceName, types.MergePatchType, patch, metav1.PatchOptions{}) + if err != nil { + r.Recorder.Eventf(existing, v1.EventTypeWarning, "FinalizerUpdateFailed", + "Failed to update finalizers for %q: %v", resourceName, err) + } else { + r.Recorder.Eventf(updated, v1.EventTypeNormal, "FinalizerUpdate", + "Updated %q finalizers", resource.GetName()) + } + return updated, err +} + +func (r *reconcilerImpl) setFinalizerIfFinalizer(ctx context.Context, resource *v1alpha1.ServiceNowTarget) (*v1alpha1.ServiceNowTarget, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + + finalizers := sets.New[string](resource.Finalizers...) + + // If this resource is not being deleted, mark the finalizer. + if resource.GetDeletionTimestamp().IsZero() { + finalizers.Insert(r.finalizerName) + } + + // Synchronize the finalizers filtered by r.finalizerName. + return r.updateFinalizersFiltered(ctx, resource, finalizers) +} + +func (r *reconcilerImpl) clearFinalizer(ctx context.Context, resource *v1alpha1.ServiceNowTarget, reconcileEvent reconciler.Event) (*v1alpha1.ServiceNowTarget, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + if resource.GetDeletionTimestamp().IsZero() { + return resource, nil + } + + finalizers := sets.New[string](resource.Finalizers...) + + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + if reconciler.EventAs(reconcileEvent, &event) { + if event.EventType == v1.EventTypeNormal { + finalizers.Delete(r.finalizerName) + } + } + } else { + finalizers.Delete(r.finalizerName) + } + + // Synchronize the finalizers filtered by r.finalizerName. + return r.updateFinalizersFiltered(ctx, resource, finalizers) +} diff --git a/pkg/client/generated/injection/reconciler/targets/v1alpha1/servicenowtarget/state.go b/pkg/client/generated/injection/reconciler/targets/v1alpha1/servicenowtarget/state.go new file mode 100644 index 00000000..590a2a6b --- /dev/null +++ b/pkg/client/generated/injection/reconciler/targets/v1alpha1/servicenowtarget/state.go @@ -0,0 +1,81 @@ +// Code generated by injection-gen. DO NOT EDIT. + +package servicenowtarget + +import ( + fmt "fmt" + + v1alpha1 "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + types "k8s.io/apimachinery/pkg/types" + cache "k8s.io/client-go/tools/cache" + reconciler "knative.dev/pkg/reconciler" +) + +// state is used to track the state of a reconciler in a single run. +type state struct { + // key is the original reconciliation key from the queue. + key string + // namespace is the namespace split from the reconciliation key. + namespace string + // name is the name split from the reconciliation key. + name string + // reconciler is the reconciler. + reconciler Interface + // roi is the read only interface cast of the reconciler. + roi ReadOnlyInterface + // isROI (Read Only Interface) the reconciler only observes reconciliation. + isROI bool + // isLeader the instance of the reconciler is the elected leader. + isLeader bool +} + +func newState(key string, r *reconcilerImpl) (*state, error) { + // Convert the namespace/name string into a distinct namespace and name. + namespace, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + return nil, fmt.Errorf("invalid resource key: %s", key) + } + + roi, isROI := r.reconciler.(ReadOnlyInterface) + + isLeader := r.IsLeaderFor(types.NamespacedName{ + Namespace: namespace, + Name: name, + }) + + return &state{ + key: key, + namespace: namespace, + name: name, + reconciler: r.reconciler, + roi: roi, + isROI: isROI, + isLeader: isLeader, + }, nil +} + +// isNotLeaderNorObserver checks to see if this reconciler with the current +// state is enabled to do any work or not. +// isNotLeaderNorObserver returns true when there is no work possible for the +// reconciler. +func (s *state) isNotLeaderNorObserver() bool { + if !s.isLeader && !s.isROI { + // If we are not the leader, and we don't implement the ReadOnly + // interface, then take a fast-path out. + return true + } + return false +} + +func (s *state) reconcileMethodFor(o *v1alpha1.ServiceNowTarget) (string, doReconcile) { + if o.GetDeletionTimestamp().IsZero() { + if s.isLeader { + return reconciler.DoReconcileKind, s.reconciler.ReconcileKind + } else if s.isROI { + return reconciler.DoObserveKind, s.roi.ObserveKind + } + } else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok { + return reconciler.DoFinalizeKind, fin.FinalizeKind + } + return "unknown", nil +} diff --git a/pkg/client/generated/listers/targets/v1alpha1/expansion_generated.go b/pkg/client/generated/listers/targets/v1alpha1/expansion_generated.go index c80d31e6..002c77c6 100644 --- a/pkg/client/generated/listers/targets/v1alpha1/expansion_generated.go +++ b/pkg/client/generated/listers/targets/v1alpha1/expansion_generated.go @@ -26,6 +26,14 @@ type HTTPTargetListerExpansion interface{} // HTTPTargetNamespaceLister. type HTTPTargetNamespaceListerExpansion interface{} +// JiraTargetListerExpansion allows custom methods to be added to +// JiraTargetLister. +type JiraTargetListerExpansion interface{} + +// JiraTargetNamespaceListerExpansion allows custom methods to be added to +// JiraTargetNamespaceLister. +type JiraTargetNamespaceListerExpansion interface{} + // KafkaTargetListerExpansion allows custom methods to be added to // KafkaTargetLister. type KafkaTargetListerExpansion interface{} @@ -66,6 +74,14 @@ type SalesforceTargetListerExpansion interface{} // SalesforceTargetNamespaceLister. type SalesforceTargetNamespaceListerExpansion interface{} +// ServiceNowTargetListerExpansion allows custom methods to be added to +// ServiceNowTargetLister. +type ServiceNowTargetListerExpansion interface{} + +// ServiceNowTargetNamespaceListerExpansion allows custom methods to be added to +// ServiceNowTargetNamespaceLister. +type ServiceNowTargetNamespaceListerExpansion interface{} + // SplunkTargetListerExpansion allows custom methods to be added to // SplunkTargetLister. type SplunkTargetListerExpansion interface{} diff --git a/pkg/client/generated/listers/targets/v1alpha1/jiratarget.go b/pkg/client/generated/listers/targets/v1alpha1/jiratarget.go new file mode 100644 index 00000000..ca85ac77 --- /dev/null +++ b/pkg/client/generated/listers/targets/v1alpha1/jiratarget.go @@ -0,0 +1,83 @@ +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// JiraTargetLister helps list JiraTargets. +// All objects returned here must be treated as read-only. +type JiraTargetLister interface { + // List lists all JiraTargets in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.JiraTarget, err error) + // JiraTargets returns an object that can list and get JiraTargets. + JiraTargets(namespace string) JiraTargetNamespaceLister + JiraTargetListerExpansion +} + +// jiraTargetLister implements the JiraTargetLister interface. +type jiraTargetLister struct { + indexer cache.Indexer +} + +// NewJiraTargetLister returns a new JiraTargetLister. +func NewJiraTargetLister(indexer cache.Indexer) JiraTargetLister { + return &jiraTargetLister{indexer: indexer} +} + +// List lists all JiraTargets in the indexer. +func (s *jiraTargetLister) List(selector labels.Selector) (ret []*v1alpha1.JiraTarget, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.JiraTarget)) + }) + return ret, err +} + +// JiraTargets returns an object that can list and get JiraTargets. +func (s *jiraTargetLister) JiraTargets(namespace string) JiraTargetNamespaceLister { + return jiraTargetNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// JiraTargetNamespaceLister helps list and get JiraTargets. +// All objects returned here must be treated as read-only. +type JiraTargetNamespaceLister interface { + // List lists all JiraTargets in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.JiraTarget, err error) + // Get retrieves the JiraTarget from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.JiraTarget, error) + JiraTargetNamespaceListerExpansion +} + +// jiraTargetNamespaceLister implements the JiraTargetNamespaceLister +// interface. +type jiraTargetNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all JiraTargets in the indexer for a given namespace. +func (s jiraTargetNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.JiraTarget, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.JiraTarget)) + }) + return ret, err +} + +// Get retrieves the JiraTarget from the indexer for a given namespace and name. +func (s jiraTargetNamespaceLister) Get(name string) (*v1alpha1.JiraTarget, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("jiratarget"), name) + } + return obj.(*v1alpha1.JiraTarget), nil +} diff --git a/pkg/client/generated/listers/targets/v1alpha1/servicenowtarget.go b/pkg/client/generated/listers/targets/v1alpha1/servicenowtarget.go new file mode 100644 index 00000000..8353d4c4 --- /dev/null +++ b/pkg/client/generated/listers/targets/v1alpha1/servicenowtarget.go @@ -0,0 +1,83 @@ +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ServiceNowTargetLister helps list ServiceNowTargets. +// All objects returned here must be treated as read-only. +type ServiceNowTargetLister interface { + // List lists all ServiceNowTargets in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.ServiceNowTarget, err error) + // ServiceNowTargets returns an object that can list and get ServiceNowTargets. + ServiceNowTargets(namespace string) ServiceNowTargetNamespaceLister + ServiceNowTargetListerExpansion +} + +// serviceNowTargetLister implements the ServiceNowTargetLister interface. +type serviceNowTargetLister struct { + indexer cache.Indexer +} + +// NewServiceNowTargetLister returns a new ServiceNowTargetLister. +func NewServiceNowTargetLister(indexer cache.Indexer) ServiceNowTargetLister { + return &serviceNowTargetLister{indexer: indexer} +} + +// List lists all ServiceNowTargets in the indexer. +func (s *serviceNowTargetLister) List(selector labels.Selector) (ret []*v1alpha1.ServiceNowTarget, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ServiceNowTarget)) + }) + return ret, err +} + +// ServiceNowTargets returns an object that can list and get ServiceNowTargets. +func (s *serviceNowTargetLister) ServiceNowTargets(namespace string) ServiceNowTargetNamespaceLister { + return serviceNowTargetNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// ServiceNowTargetNamespaceLister helps list and get ServiceNowTargets. +// All objects returned here must be treated as read-only. +type ServiceNowTargetNamespaceLister interface { + // List lists all ServiceNowTargets in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.ServiceNowTarget, err error) + // Get retrieves the ServiceNowTarget from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.ServiceNowTarget, error) + ServiceNowTargetNamespaceListerExpansion +} + +// serviceNowTargetNamespaceLister implements the ServiceNowTargetNamespaceLister +// interface. +type serviceNowTargetNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all ServiceNowTargets in the indexer for a given namespace. +func (s serviceNowTargetNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ServiceNowTarget, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ServiceNowTarget)) + }) + return ret, err +} + +// Get retrieves the ServiceNowTarget from the indexer for a given namespace and name. +func (s serviceNowTargetNamespaceLister) Get(name string) (*v1alpha1.ServiceNowTarget, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("servicenowtarget"), name) + } + return obj.(*v1alpha1.ServiceNowTarget), nil +} diff --git a/pkg/sources/reconciler/salesforcesource/adapter.go b/pkg/sources/reconciler/salesforcesource/adapter.go index 8b539f95..ed8a0b4f 100644 --- a/pkg/sources/reconciler/salesforcesource/adapter.go +++ b/pkg/sources/reconciler/salesforcesource/adapter.go @@ -16,10 +16,10 @@ import ( ) const ( - envSalesforceAuthClientID = "SALESFORCE_AUTH_CLIENT_ID" - envSalesforceAuthServer = "SALESFORCE_AUTH_SERVER" - envSalesforceAuthUser = "SALESFORCE_AUTH_USER" - envSalesforceAuthCertKey = "SALESFORCE_AUTH_CERT_KEY" + envSalesforceTokenURL = "SALESFORCE_TOKEN_URL" + envSalesforceClientID = "SALESFORCE_CLIENT_ID" + envSalesforceClientSecret = "SALESFORCE_CLIENT_SECRET" + envSalesforceInstanceURL = "SALESFORCE_URL" envSalesforceAPIVersion = "SALESFORCE_API_VERSION" envSalesforceChannel = "SALESFORCE_SUBCRIPTION_CHANNEL" envSalesforceReplayID = "SALESFORCE_SUBCRIPTION_REPLAY_ID" @@ -29,7 +29,7 @@ const ( // These are automatically populated by envconfig. type adapterConfig struct { // Container image - Image string `default:"gcr.io/triggermesh/salesforcesource-adapter"` + Image string `default:"ghcr.io/zeiss/typhoon/salesforcesource-adapter"` // Configuration accessor for logging/metrics/tracing configs source.ConfigAccessor } @@ -54,27 +54,27 @@ func (r *Reconciler) BuildAdapter(src commonv1alpha1.Reconcilable, sinkURI *apis func MakeAppEnv(o *v1alpha1.SalesforceSource) []corev1.EnvVar { appEnv := []corev1.EnvVar{ { - Name: envSalesforceAuthClientID, + Name: envSalesforceClientID, Value: o.Spec.Auth.ClientID, }, { - Name: envSalesforceAuthServer, - Value: o.Spec.Auth.Server, + Name: envSalesforceClientSecret, + Value: o.Spec.Auth.ClientSecret, }, { - Name: envSalesforceAuthUser, - Value: o.Spec.Auth.User, + Name: envSalesforceTokenURL, + Value: o.Spec.Auth.TokenURL, }, { Name: envSalesforceChannel, Value: o.Spec.Subscription.Channel, }, + { + Name: envSalesforceInstanceURL, + Value: o.Spec.InstanceURL, + }, } - appEnv = common.MaybeAppendValueFromEnvVar(appEnv, - envSalesforceAuthCertKey, o.Spec.Auth.CertKey, - ) - if o.Spec.Subscription.ReplayID != nil { appEnv = append(appEnv, corev1.EnvVar{ Name: envSalesforceReplayID, diff --git a/pkg/targets/adapter/jiratarget/adapter.go b/pkg/targets/adapter/jiratarget/adapter.go new file mode 100644 index 00000000..4891686d --- /dev/null +++ b/pkg/targets/adapter/jiratarget/adapter.go @@ -0,0 +1,233 @@ +package jiratarget + +import ( + "context" + "encoding/json" + "io" + "net/http" + "net/url" + "path" + + "github.com/google/uuid" + "go.uber.org/zap" + + cloudevents "github.com/cloudevents/sdk-go/v2" + + pkgadapter "knative.dev/eventing/pkg/adapter/v2" + "knative.dev/pkg/logging" + + "github.com/andygrunwald/go-jira" + + "github.com/zeiss/typhoon/pkg/apis/targets" + "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + "github.com/zeiss/typhoon/pkg/metrics" +) + +// Method performed on the API URI +type Method string + +// Available actions at the JIRA API +const ( + MethodCreate Method = http.MethodPost + MethodPut Method = http.MethodPut + MethodPatch Method = http.MethodPatch + MethodGet Method = http.MethodGet + MethodDelete Method = http.MethodDelete +) + +// JiraAPIRequest contains common parameters used for +// interacting with Jira using the API. +type JiraAPIRequest struct { + Method Method `json:"method"` + Path string `json:"path"` + Query map[string]string `json:"query"` + Payload json.RawMessage `json:"payload"` +} + +// IssueGetRequest contains parameters for issue retrieval +type IssueGetRequest struct { + ID string `json:"id"` + Options jira.GetQueryOptions `json:"options"` +} + +// NewTarget creates a Jira target adapter +func NewTarget(ctx context.Context, envAcc pkgadapter.EnvConfigAccessor, ceClient cloudevents.Client) pkgadapter.Adapter { + logger := logging.FromContext(ctx) + + mt := &pkgadapter.MetricTag{ + ResourceGroup: targets.JiraTargetResource.String(), + Namespace: envAcc.GetNamespace(), + Name: envAcc.GetName(), + } + + metrics.MustRegisterEventProcessingStatsView() + + env := envAcc.(*envAccessor) + + basicAuth := jira.BasicAuthTransport{ + Username: env.JiraBasicAuthUser, + Password: env.JiraBasicAuthToken, + } + + jiraClient, err := jira.NewClient(basicAuth.Client(), env.JiraURL) + if err != nil { + logger.Panicw("Could not create the Jira client", zap.Error(err)) + } + + return &jiraAdapter{ + ceClient: ceClient, + logger: logger, + + jiraClient: jiraClient, + baseURL: env.JiraURL, + resSource: env.Namespace + "/" + env.Name + ": " + env.JiraURL, + + sr: metrics.MustNewEventProcessingStatsReporter(mt), + } +} + +type jiraAdapter struct { + ceClient cloudevents.Client + logger *zap.SugaredLogger + + baseURL string + jiraClient *jira.Client + resSource string + + sr *metrics.EventProcessingStatsReporter +} + +var _ pkgadapter.Adapter = (*jiraAdapter)(nil) + +func (a *jiraAdapter) Start(ctx context.Context) error { + a.logger.Info("Starting Jira adapter") + + if err := a.ceClient.StartReceiver(ctx, a.dispatch); err != nil { + return err + } + return nil +} + +func (a *jiraAdapter) dispatch(ctx context.Context, event cloudevents.Event) (*cloudevents.Event, cloudevents.Result) { + + switch event.Type() { + case v1alpha1.EventTypeJiraIssueCreate: + return a.jiraIssueCreate(ctx, event) + case v1alpha1.EventTypeJiraIssueGet: + return a.jiraIssueGet(ctx, event) + case v1alpha1.EventTypeJiraCustom: + return a.jiraCustomRequest(ctx, event) + } + + a.logger.Errorf("Event type %q is not supported", event.Type()) + return nil, cloudevents.ResultNACK +} + +func (a *jiraAdapter) jiraIssueCreate(ctx context.Context, event cloudevents.Event) (*cloudevents.Event, cloudevents.Result) { + j := &jira.Issue{} + if err := event.DataAs(j); err != nil { + a.logger.Errorw("Error processing incoming event data as Jira Issue", zap.Error(err)) + return nil, cloudevents.ResultACK + } + + issue, res, err := a.jiraClient.Issue.CreateWithContext(ctx, j) + if err != nil { + respErr := jira.NewJiraError(res, err) + a.logger.Errorw("Error requesting Jira API", zap.Error(respErr)) + return nil, cloudevents.ResultACK + } + + out := cloudevents.NewEvent() + if err := out.SetData(cloudevents.ApplicationJSON, issue); err != nil { + a.logger.Errorw("Error generating response event", zap.Error(err)) + return nil, cloudevents.ResultACK + } + + out.SetID(uuid.New().String()) + out.SetType(v1alpha1.EventTypeJiraIssue) + out.SetSource(a.resSource) + + return &out, cloudevents.ResultACK +} + +func (a *jiraAdapter) jiraIssueGet(ctx context.Context, event cloudevents.Event) (*cloudevents.Event, cloudevents.Result) { + j := &IssueGetRequest{} + if err := event.DataAs(j); err != nil { + a.logger.Errorw("Error processing incoming event data as IssueGetRequest", zap.Error(err)) + return nil, cloudevents.ResultACK + } + + issue, res, err := a.jiraClient.Issue.GetWithContext(ctx, j.ID, &j.Options) + if err != nil { + respErr := jira.NewJiraError(res, err) + a.logger.Errorw("Error requesting Jira API", zap.Error(respErr)) + return nil, cloudevents.ResultACK + } + + out := cloudevents.NewEvent() + if err := out.SetData(cloudevents.ApplicationJSON, issue); err != nil { + a.logger.Errorw("Error generating response event", zap.Error(err)) + return nil, cloudevents.ResultACK + } + + out.SetID(uuid.New().String()) + out.SetType(v1alpha1.EventTypeJiraIssue) + out.SetSource(a.resSource) + + return &out, cloudevents.ResultACK +} + +func (a *jiraAdapter) jiraCustomRequest(ctx context.Context, event cloudevents.Event) (*cloudevents.Event, cloudevents.Result) { + j := &JiraAPIRequest{} + if err := event.DataAs(j); err != nil { + a.logger.Errorw("Error processing incoming event data as generic Jira API request", zap.Error(err)) + return nil, cloudevents.ResultACK + } + + u, err := url.Parse(a.baseURL) + if err != nil { + a.logger.Errorw("Error parsing base URL", zap.Error(err)) + return nil, cloudevents.ResultACK + } + u.Path = path.Join(u.Path, j.Path) + + if len(j.Query) > 0 { + q := url.Values{} + for k, v := range j.Query { + q.Add(k, v) + } + u.RawQuery = q.Encode() + } + + req, err := a.jiraClient.NewRequestWithContext(ctx, string(j.Method), u.String(), j.Payload) + if err != nil { + a.logger.Errorw("Error creating request", zap.Error(err)) + return nil, cloudevents.ResultACK + } + + res, err := a.jiraClient.Do(req, nil) + if err != nil { + respErr := jira.NewJiraError(res, err) + a.logger.Errorw("Error requesting Jira API", zap.Error(respErr)) + return nil, cloudevents.ResultACK + } + + defer res.Body.Close() + resBody, err := io.ReadAll(res.Body) + if err != nil { + a.logger.Errorw("Error reading response from Jira API", zap.Error(err)) + return nil, cloudevents.ResultACK + } + + out := cloudevents.NewEvent() + if err := out.SetData(cloudevents.ApplicationJSON, resBody); err != nil { + a.logger.Errorw("Error generating response event", zap.Error(err)) + return nil, cloudevents.ResultACK + } + + out.SetID(uuid.New().String()) + out.SetType(event.Type() + ".response") + out.SetSource(a.resSource) + + return &out, cloudevents.ResultACK +} diff --git a/pkg/targets/adapter/jiratarget/adapter_test.go b/pkg/targets/adapter/jiratarget/adapter_test.go new file mode 100644 index 00000000..fe723077 --- /dev/null +++ b/pkg/targets/adapter/jiratarget/adapter_test.go @@ -0,0 +1,366 @@ +package jiratarget + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/andygrunwald/go-jira" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + cloudevents "github.com/cloudevents/sdk-go/v2" + cetest "github.com/cloudevents/sdk-go/v2/client/test" + logtesting "knative.dev/pkg/logging/testing" + + "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" +) + +const ( + tID = "abc123" + tContentType = "application/json" + tSource = "test.source" + + tResSource = "source.response" + + tIssueID = "10002" + tIssue = ` + { + "expand":"renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations", + "id":"10002", + "self":"http://www.example.com/jira/rest/api/2/issue/10002", + "key":"EX-1", + "fields":{ + "watcher":{ + "self":"http://www.example.com/jira/rest/api/2/issue/EX-1/watchers", + "isWatching":false, + "watchCount":1, + "watchers":[ + { + "self":"http://www.example.com/jira/rest/api/2/user?username=fred", + "name":"fred", + "displayName":"Fred F. User", + "active":false + } + ] + }, + "sub-tasks":[ + { + "id":"10000", + "type":{ + "id":"10000", + "name":"", + "inward":"Parent", + "outward":"Sub-task" + }, + "outwardIssue":{ + "id":"10003", + "key":"EX-2", + "self":"http://www.example.com/jira/rest/api/2/issue/EX-2", + "fields":{ + "status":{ + "iconUrl":"http://www.example.com/jira//images/icons/statuses/open.png", + "name":"Open" + } + } + } + } + ], + "description":"example bug report", + "project":{ + "self":"http://www.example.com/jira/rest/api/2/project/EX", + "id":"10000", + "key":"EX", + "name":"Example", + "avatarUrls":{ + "48x48":"http://www.example.com/jira/secure/projectavatar?size=large&pid=10000", + "24x24":"http://www.example.com/jira/secure/projectavatar?size=small&pid=10000", + "16x16":"http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000", + "32x32":"http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000" + }, + "projectCategory":{ + "self":"http://www.example.com/jira/rest/api/2/projectCategory/10000", + "id":"10000", + "name":"FIRST", + "description":"First Project Category" + } + }, + "comment":{ + "comments":[ + { + "self":"http://www.example.com/jira/rest/api/2/issue/10010/comment/10000", + "id":"10000", + "author":{ + "self":"http://www.example.com/jira/rest/api/2/user?username=fred", + "name":"fred", + "displayName":"Fred F. User", + "active":false + }, + "body":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.", + "updateAuthor":{ + "self":"http://www.example.com/jira/rest/api/2/user?username=fred", + "name":"fred", + "displayName":"Fred F. User", + "active":false + }, + "created":"2016-03-16T04:22:37.356+0000", + "updated":"2016-03-16T04:22:37.356+0000", + "visibility":{ + "type":"role", + "value":"Administrators" + } + } + ] + }, + "worklog":{ + "worklogs":[ + { + "self":"http://www.example.com/jira/rest/api/2/issue/10010/worklog/10000", + "author":{ + "self":"http://www.example.com/jira/rest/api/2/user?username=fred", + "name":"fred", + "displayName":"Fred F. User", + "active":false + }, + "updateAuthor":{ + "self":"http://www.example.com/jira/rest/api/2/user?username=fred", + "name":"fred", + "displayName":"Fred F. User", + "active":false + }, + "comment":"I did some work here.", + "updated":"2016-03-16T04:22:37.471+0000", + "visibility":{ + "type":"group", + "value":"jira-developers" + }, + "started":"2016-03-16T04:22:37.471+0000", + "timeSpent":"3h 20m", + "timeSpentSeconds":12000, + "id":"100028", + "issueId":"10002" + } + ] + }, + "updated":"2016-04-06T02:36:53.594-0700", + "duedate":"2018-01-19", + "timetracking":{ + "originalEstimate":"10m", + "remainingEstimate":"3m", + "timeSpent":"6m", + "originalEstimateSeconds":600, + "remainingEstimateSeconds":200, + "timeSpentSeconds":400 + } + }, + "names":{ + "watcher":"watcher", + "attachment":"attachment", + "sub-tasks":"sub-tasks", + "description":"description", + "project":"project", + "comment":"comment", + "issuelinks":"issuelinks", + "worklog":"worklog", + "updated":"updated", + "timetracking":"timetracking" + }, + "schema":{} + }` + tProjects = ` + [ + { + "expand":"description,lead,issueTypes,url,projectKeys,permissions,insight", + "self":"https://tmtest.atlassian.net/rest/api/3/project/10000", + "id":"10000", + "key":"IP", + "name":"ITSM project", + "avatarUrls":{ + "48x48":"https://tmtest.atlassian.net/secure/projectavatar?pid=10000&avatarId=10424", + "24x24":"https://tmtest.atlassian.net/secure/projectavatar?size=small&s=small&pid=10000&avatarId=10424", + "16x16":"https://tmtest.atlassian.net/secure/projectavatar?size=xsmall&s=xsmall&pid=10000&avatarId=10424", + "32x32":"https://tmtest.atlassian.net/secure/projectavatar?size=medium&s=medium&pid=10000&avatarId=10424" + }, + "projectTypeKey":"service_desk", + "simplified":false, + "style":"classic", + "isPrivate":false, + "properties":{ + + } + } + ]` +) + +func TestJiraEvents(t *testing.T) { + server := newJiraMockedServer() + defer server.Close() + + testCases := map[string]struct { + inType string + inData string + + noPayload bool + outType string + }{ + "create issue": { + inType: v1alpha1.EventTypeJiraIssueCreate, + inData: `{ + "fields": { + "project": + { + "key": "EX-1" + }, + "labels": ["alpha","beta"], + "description": "example bug report", + "issuetype": { + "name": "Task" + }, + "assignee": { + "accountId": "5fe0704c9edf280075f188f0" + } + } + }`, + outType: v1alpha1.EventTypeJiraIssue, + }, + + "create issue - wrong payload": { + inType: v1alpha1.EventTypeJiraIssueCreate, + inData: `{"a":"b"}`, + + noPayload: true, + }, + + "get issue": { + inType: v1alpha1.EventTypeJiraIssueGet, + inData: `{"id":"` + tIssueID + `"}`, + outType: v1alpha1.EventTypeJiraIssue, + }, + + "list projects": { + inType: v1alpha1.EventTypeJiraCustom, + inData: `{"method":"GET", "path":"/rest/api/3/project"}`, + outType: v1alpha1.EventTypeJiraCustomResponse, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + + // jira client configured to use test server + jiraClient, err := jira.NewClient(nil, server.URL) + if err != nil { + assert.FailNow(t, "Could not build mocked Jira client: %v", err) + } + + ceClient, send, responses := cetest.NewMockResponderClient(t, 1) + + ja := jiraAdapter{ + logger: logtesting.TestLogger(t), + ceClient: ceClient, + + jiraClient: jiraClient, + baseURL: server.URL, + resSource: tResSource, + } + + go func() { + if err := ja.Start(context.Background()); err != nil { + assert.FailNow(t, "could not start test adapter") + } + }() + + in, err := createInEvent(tc.inType, tc.inData) + require.Nil(t, err, "Could not create incoming event") + + send <- *in + + select { + case event := <-responses: + if tc.noPayload { + assert.Nil(t, event.Event.Data(), "Unexpected event received") + return + } + require.NotNil(t, event.Event.Data(), "Expected event was not received") + + assert.Equal(t, tc.outType, event.Event.Context.GetType()) + assert.Equal(t, tResSource, event.Event.Context.GetSource()) + + case <-time.After(13 * time.Second): + assert.Fail(t, "expected cloud event response was not received") + } + + }) + } +} + +func createInEvent(evType, evData string) (*cloudevents.Event, error) { + data := make(map[string]interface{}) + + if err := json.Unmarshal([]byte(evData), &data); err != nil { + return nil, err + } + + event := cloudevents.NewEvent() + if err := event.SetData(tContentType, data); err != nil { + return nil, err + } + + event.SetID(tID) + event.SetType(evType) + event.SetSource(tSource) + + return &event, nil +} + +func newJiraMockedServer() *httptest.Server { + testMux := http.NewServeMux() + testServer := httptest.NewServer(testMux) + + testMux.HandleFunc("/rest/api/2/issue", func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodPost: + j := &jira.Issue{} + err := json.NewDecoder(r.Body).Decode(j) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + if j.Fields == nil { + w.WriteHeader(http.StatusBadRequest) + return + } + + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, tIssue) + default: + w.WriteHeader(http.StatusBadRequest) + } + + }) + + testMux.HandleFunc("/rest/api/2/issue/"+tIssueID, func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + fmt.Fprint(w, tIssue) + default: + w.WriteHeader(http.StatusBadRequest) + } + }) + + testMux.HandleFunc("/rest/api/3/project", func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + fmt.Fprint(w, tProjects) + default: + w.WriteHeader(http.StatusBadRequest) + } + }) + + return testServer +} diff --git a/pkg/targets/adapter/jiratarget/config.go b/pkg/targets/adapter/jiratarget/config.go new file mode 100644 index 00000000..98f09a20 --- /dev/null +++ b/pkg/targets/adapter/jiratarget/config.go @@ -0,0 +1,18 @@ +package jiratarget + +import ( + pkgadapter "knative.dev/eventing/pkg/adapter/v2" +) + +// EnvAccessorCtor for configuration parameters +func EnvAccessorCtor() pkgadapter.EnvConfigAccessor { + return &envAccessor{} +} + +type envAccessor struct { + pkgadapter.EnvConfig + + JiraBasicAuthUser string `envconfig:"JIRA_BASIC_AUTH_USERNAME" required:"true"` + JiraBasicAuthToken string `envconfig:"JIRA_BASIC_AUTH_PASSWORD" required:"true"` + JiraURL string `envconfig:"JIRA_URL" required:"true"` +} diff --git a/pkg/targets/adapter/snowtarget/adapter.go b/pkg/targets/adapter/servicenowtarget/adapter.go similarity index 83% rename from pkg/targets/adapter/snowtarget/adapter.go rename to pkg/targets/adapter/servicenowtarget/adapter.go index 24f9c2d0..2d4a183b 100644 --- a/pkg/targets/adapter/snowtarget/adapter.go +++ b/pkg/targets/adapter/servicenowtarget/adapter.go @@ -1,7 +1,8 @@ -package snowtarget +package servicenowtarget import ( "context" + "net/http" "github.com/zeiss/snow-go/push" "github.com/zeiss/typhoon/pkg/apis/targets" @@ -21,10 +22,10 @@ func EnvAccessorCtor() pkgadapter.EnvConfigAccessor { type envAccessor struct { pkgadapter.EnvConfig - instance string `envconfig:"SNOW_INSTANCE"` - user string `envconfig:"SNOW_BASICAUTH_USER"` - password string `envconfig:"SNOW_BASICAUTH_PASSWORD"` - source string `envconfig:"SNOW_SOURCE"` + instance string `envconfig:"SERVICENOW_INSTANCE"` + user string `envconfig:"SERVICENOW_BASICAUTH_USER"` + password string `envconfig:"SERVICENOW_BASICAUTH_PASSWORD"` + source string `envconfig:"SERVICENOW_SOURCE"` } var _ pkgadapter.Adapter = (*snowAdapter)(nil) @@ -65,7 +66,7 @@ func NewTarget(ctx context.Context, envAcc pkgadapter.EnvConfigAccessor, client // Start is the main entrypoint for the adapter func (a *snowAdapter) Start(ctx context.Context) error { - a.logger.Info("starting SNOW adapter") + a.logger.Info("starting ServiceNow adapter") return a.client.StartReceiver(ctx, a.dispatch) } @@ -78,7 +79,7 @@ func (a *snowAdapter) dispatch(event cloudevents.Event) cloudevents.Result { err := a.sc.Do(context.Background(), req, res) if err != nil { - return err + return a.errorHTTPResult(http.StatusBadRequest, "failed to push event to ServiceNow: %v", err) } return cloudevents.ResultACK diff --git a/pkg/targets/reconciler/jiratarget/adapter.go b/pkg/targets/reconciler/jiratarget/adapter.go new file mode 100644 index 00000000..f7007354 --- /dev/null +++ b/pkg/targets/reconciler/jiratarget/adapter.go @@ -0,0 +1,68 @@ +package jiratarget + +import ( + corev1 "k8s.io/api/core/v1" + + "knative.dev/eventing/pkg/reconciler/source" + "knative.dev/pkg/apis" + servingv1 "knative.dev/serving/pkg/apis/serving/v1" + + commonv1alpha1 "github.com/zeiss/typhoon/pkg/apis/common/v1alpha1" + "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + common "github.com/zeiss/typhoon/pkg/reconciler" + "github.com/zeiss/typhoon/pkg/reconciler/resource" +) + +const ( + envJiraBasicAuthUser = "JIRA_BASIC_AUTH_USER" + envJiraBasicAuthToken = "JIRA_BASIC_AUTH_TOKEN" + envJiraURL = "JIRA_URL" +) + +// adapterConfig contains properties used to configure the target's adapter. +// Public fields are automatically populated by envconfig. +type adapterConfig struct { + // Configuration accessor for logging/metrics/tracing + obsConfig source.ConfigAccessor + // Container image + Image string `default:"ghcr.io/zeiss/typhoon/jiratarget-adapter"` +} + +// Verify that Reconciler implements common.AdapterBuilder. +var _ common.AdapterBuilder[*servingv1.Service] = (*Reconciler)(nil) + +// BuildAdapter implements common.AdapterBuilder. +func (r *Reconciler) BuildAdapter(trg commonv1alpha1.Reconcilable, _ *apis.URL) (*servingv1.Service, error) { + typedTrg := trg.(*v1alpha1.JiraTarget) + + return common.NewAdapterKnService(trg, nil, + resource.Image(r.adapterCfg.Image), + resource.EnvVars(MakeAppEnv(typedTrg)...), + resource.EnvVars(r.adapterCfg.obsConfig.ToEnvVars()...), + ), nil +} + +// MakeAppEnv extracts environment variables from the object. +// Exported to be used in external tools for local test environments. +func MakeAppEnv(o *v1alpha1.JiraTarget) []corev1.EnvVar { + return []corev1.EnvVar{ + { + Name: envJiraBasicAuthUser, + Value: o.Spec.Auth.User, + }, + { + Name: envJiraBasicAuthToken, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: o.Spec.Auth.Token.SecretKeyRef, + }, + }, + { + Name: envJiraURL, + Value: o.Spec.URL, + }, + { + Name: common.EnvBridgeID, + Value: common.GetStatefulBridgeID(o), + }, + } +} diff --git a/pkg/targets/reconciler/jiratarget/controller.go b/pkg/targets/reconciler/jiratarget/controller.go new file mode 100644 index 00000000..d80d15ef --- /dev/null +++ b/pkg/targets/reconciler/jiratarget/controller.go @@ -0,0 +1,53 @@ +package jiratarget + +import ( + "context" + + "github.com/kelseyhightower/envconfig" + + "knative.dev/eventing/pkg/reconciler/source" + "knative.dev/pkg/configmap" + "knative.dev/pkg/controller" + + "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + informerv1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/targets/v1alpha1/jiratarget" + reconcilerv1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/injection/reconciler/targets/v1alpha1/jiratarget" + common "github.com/zeiss/typhoon/pkg/reconciler" +) + +// NewController initializes the controller and is called by the generated code +// Registers event handlers to enqueue events +func NewController( + ctx context.Context, + cmw configmap.Watcher, +) *controller.Impl { + + typ := (*v1alpha1.JiraTarget)(nil) + app := common.ComponentName(typ) + + // Calling envconfig.Process() with a prefix appends that prefix + // (uppercased) to the Go field name, e.g. MYTARGET_IMAGE. + adapterCfg := &adapterConfig{ + obsConfig: source.WatchConfigurations(ctx, app, cmw), + } + envconfig.MustProcess(app, adapterCfg) + + informer := informerv1alpha1.Get(ctx) + + r := &Reconciler{ + adapterCfg: adapterCfg, + } + impl := reconcilerv1alpha1.NewImpl(ctx, r) + + r.base = common.NewGenericServiceReconciler[*v1alpha1.JiraTarget]( + ctx, + typ.GetGroupVersionKind(), + impl.Tracker, + impl.EnqueueControllerOf, + informer.Lister().JiraTargets, + ) + + informer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) + + return impl +} diff --git a/pkg/targets/reconciler/jiratarget/reconciler.go b/pkg/targets/reconciler/jiratarget/reconciler.go new file mode 100644 index 00000000..505681dc --- /dev/null +++ b/pkg/targets/reconciler/jiratarget/reconciler.go @@ -0,0 +1,30 @@ +package jiratarget + +import ( + "context" + + "knative.dev/pkg/reconciler" + + commonv1alpha1 "github.com/zeiss/typhoon/pkg/apis/common/v1alpha1" + "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + reconcilerv1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/injection/reconciler/targets/v1alpha1/jiratarget" + listersv1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/listers/targets/v1alpha1" + common "github.com/zeiss/typhoon/pkg/reconciler" +) + +// Reconciler implements controller.Reconciler for the event target type. +type Reconciler struct { + base common.GenericServiceReconciler[*v1alpha1.JiraTarget, listersv1alpha1.JiraTargetNamespaceLister] + adapterCfg *adapterConfig +} + +// Check that our Reconciler implements Interface +var _ reconcilerv1alpha1.Interface = (*Reconciler)(nil) + +// ReconcileKind implements Interface.ReconcileKind. +func (r *Reconciler) ReconcileKind(ctx context.Context, trg *v1alpha1.JiraTarget) reconciler.Event { + // inject target into context for usage in reconciliation logic + ctx = commonv1alpha1.WithReconcilable(ctx, trg) + + return r.base.ReconcileAdapter(ctx, r) +} diff --git a/pkg/targets/reconciler/servicenowtarget/adapter.go b/pkg/targets/reconciler/servicenowtarget/adapter.go new file mode 100644 index 00000000..fc199a89 --- /dev/null +++ b/pkg/targets/reconciler/servicenowtarget/adapter.go @@ -0,0 +1,69 @@ +package servicenowtarget + +import ( + corev1 "k8s.io/api/core/v1" + + "knative.dev/eventing/pkg/reconciler/source" + "knative.dev/pkg/apis" + servingv1 "knative.dev/serving/pkg/apis/serving/v1" + + commonv1alpha1 "github.com/zeiss/typhoon/pkg/apis/common/v1alpha1" + "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + common "github.com/zeiss/typhoon/pkg/reconciler" + "github.com/zeiss/typhoon/pkg/reconciler/resource" +) + +const ( + envServiceNowInstance = "SERVICENOW_INSTANCE" + envServiceNowUser = "SERVICENOW_BASICAUTH_USER" + envServiceNowPassword = "SERVICENOW_BASICAUTH_PASSWORD" + envServiceNowSource = "SERVICENOW_SOURCE" +) + +// adapterConfig contains properties used to configure the target's adapter. +// Public fields are automatically populated by envconfig. +type adapterConfig struct { + // Configuration accessor for logging/metrics/tracing + obsConfig source.ConfigAccessor + // Container image + Image string `default:"ghcr.io/zeiss/typhoon/servicenowtarget-adapter"` +} + +// Verify that Reconciler implements common.AdapterBuilder. +var _ common.AdapterBuilder[*servingv1.Service] = (*Reconciler)(nil) + +// BuildAdapter implements common.AdapterBuilder. +func (r *Reconciler) BuildAdapter(trg commonv1alpha1.Reconcilable, _ *apis.URL) (*servingv1.Service, error) { + typedTrg := trg.(*v1alpha1.JiraTarget) + + return common.NewAdapterKnService(trg, nil, + resource.Image(r.adapterCfg.Image), + resource.EnvVars(MakeAppEnv(typedTrg)...), + resource.EnvVars(r.adapterCfg.obsConfig.ToEnvVars()...), + ), nil +} + +// MakeAppEnv extracts environment variables from the object. +// Exported to be used in external tools for local test environments. +func MakeAppEnv(o *v1alpha1.JiraTarget) []corev1.EnvVar { + return []corev1.EnvVar{ + { + Name: envServiceNowUser, + Value: o.Spec.Auth.User, + }, + { + Name: envServiceNowPassword, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: o.Spec.Auth.Token.SecretKeyRef, + }, + }, + { + Name: envServiceNowInstance, + Value: o.Spec.URL, + }, + { + Name: common.EnvBridgeID, + Value: common.GetStatefulBridgeID(o), + }, + } +} diff --git a/pkg/targets/reconciler/servicenowtarget/controller.go b/pkg/targets/reconciler/servicenowtarget/controller.go new file mode 100644 index 00000000..50fa9b0d --- /dev/null +++ b/pkg/targets/reconciler/servicenowtarget/controller.go @@ -0,0 +1,52 @@ +package servicenowtarget + +import ( + "context" + + "github.com/kelseyhightower/envconfig" + + "knative.dev/eventing/pkg/reconciler/source" + "knative.dev/pkg/configmap" + "knative.dev/pkg/controller" + + "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + informerv1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/injection/informers/targets/v1alpha1/servicenowtarget" + reconcilerv1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/injection/reconciler/targets/v1alpha1/servicenowtarget" + common "github.com/zeiss/typhoon/pkg/reconciler" +) + +// NewController initializes the controller and is called by the generated code +// Registers event handlers to enqueue events +func NewController( + ctx context.Context, + cmw configmap.Watcher, +) *controller.Impl { + typ := (*v1alpha1.ServiceNowTarget)(nil) + app := common.ComponentName(typ) + + // Calling envconfig.Process() with a prefix appends that prefix + // (uppercased) to the Go field name, e.g. MYTARGET_IMAGE. + adapterCfg := &adapterConfig{ + obsConfig: source.WatchConfigurations(ctx, app, cmw), + } + envconfig.MustProcess(app, adapterCfg) + + informer := informerv1alpha1.Get(ctx) + + r := &Reconciler{ + adapterCfg: adapterCfg, + } + impl := reconcilerv1alpha1.NewImpl(ctx, r) + + r.base = common.NewGenericServiceReconciler[*v1alpha1.ServiceNowTarget]( + ctx, + typ.GetGroupVersionKind(), + impl.Tracker, + impl.EnqueueControllerOf, + informer.Lister().ServiceNowTargets, + ) + + informer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) + + return impl +} diff --git a/pkg/targets/reconciler/servicenowtarget/reconciler.go b/pkg/targets/reconciler/servicenowtarget/reconciler.go new file mode 100644 index 00000000..1d42483a --- /dev/null +++ b/pkg/targets/reconciler/servicenowtarget/reconciler.go @@ -0,0 +1,30 @@ +package servicenowtarget + +import ( + "context" + + "knative.dev/pkg/reconciler" + + commonv1alpha1 "github.com/zeiss/typhoon/pkg/apis/common/v1alpha1" + "github.com/zeiss/typhoon/pkg/apis/targets/v1alpha1" + reconcilerv1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/injection/reconciler/targets/v1alpha1/servicenowtarget" + listersv1alpha1 "github.com/zeiss/typhoon/pkg/client/generated/listers/targets/v1alpha1" + common "github.com/zeiss/typhoon/pkg/reconciler" +) + +// Reconciler implements controller.Reconciler for the event target type. +type Reconciler struct { + base common.GenericServiceReconciler[*v1alpha1.ServiceNowTarget, listersv1alpha1.ServiceNowTargetNamespaceLister] + adapterCfg *adapterConfig +} + +// Check that our Reconciler implements Interface +var _ reconcilerv1alpha1.Interface = (*Reconciler)(nil) + +// ReconcileKind implements Interface.ReconcileKind. +func (r *Reconciler) ReconcileKind(ctx context.Context, trg *v1alpha1.ServiceNowTarget) reconciler.Event { + // inject target into context for usage in reconciliation logic + ctx = commonv1alpha1.WithReconcilable(ctx, trg) + + return r.base.ReconcileAdapter(ctx, r) +}