未验证 提交 c5a63500 编写于 作者: Z zryfish 提交者: GitHub

add application controller (#2652)

Signed-off-by: NJeff <zw0948@gmail.com>
上级 d4272ef4
......@@ -28,6 +28,7 @@ import (
"k8s.io/client-go/tools/record"
cliflag "k8s.io/component-base/cli/flag"
"k8s.io/klog"
"k8s.io/klog/klogr"
"kubesphere.io/kubesphere/cmd/controller-manager/app/options"
"kubesphere.io/kubesphere/pkg/apis"
controllerconfig "kubesphere.io/kubesphere/pkg/apiserver/config"
......@@ -48,6 +49,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
"sigs.k8s.io/controller-runtime/pkg/webhook"
application "sigs.k8s.io/application/controllers"
)
func NewControllerManagerCommand() *cobra.Command {
......@@ -181,6 +184,16 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
klog.Fatal("Unable to create namespace controller")
}
err = (&application.ApplicationReconciler{
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
Mapper: mgr.GetRESTMapper(),
Log: klogr.New(),
}).SetupWithManager(mgr)
if err != nil {
klog.Fatal("Unable to create application controller")
}
// TODO(jeff): refactor config with CRD
servicemeshEnabled := s.ServiceMeshOptions != nil && len(s.ServiceMeshOptions.IstioPilotHost) != 0
if err = addControllers(mgr,
......
......@@ -10,6 +10,7 @@ require (
code.cloudfoundry.org/bytefmt v0.0.0-20190710193110-1eb035ffe2b6
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/PuerkitoBio/goquery v1.5.0
github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30 // indirect
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
github.com/aws/aws-sdk-go v1.25.21
github.com/beevik/etree v1.1.0
......@@ -26,11 +27,13 @@ require (
github.com/emirpasic/gods v1.12.0 // indirect
github.com/fatih/structs v1.1.0
github.com/go-ldap/ldap v3.0.3+incompatible
github.com/go-logfmt/logfmt v0.4.0 // indirect
github.com/go-logr/zapr v0.1.1 // indirect
github.com/go-openapi/loads v0.19.2
github.com/go-openapi/spec v0.19.4
github.com/go-openapi/strfmt v0.19.3
github.com/go-openapi/validate v0.19.5
github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/go-redis/redis v6.15.2+incompatible
github.com/go-sql-driver/mysql v1.4.1
github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6
......@@ -40,6 +43,7 @@ require (
github.com/google/go-cmp v0.3.1
github.com/google/go-querystring v1.0.0 // indirect
github.com/google/uuid v1.1.1
github.com/gophercloud/gophercloud v0.3.0 // indirect
github.com/gorilla/mux v1.7.1 // indirect
github.com/gorilla/websocket v1.4.1
github.com/hashicorp/go-version v1.2.0 // indirect
......@@ -47,8 +51,8 @@ require (
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/kiali/kiali v0.15.1-0.20200520152915-769a61d75460
github.com/kubernetes-csi/external-snapshotter/v2 v2.1.0
github.com/kubernetes-sigs/application v0.0.0-20191210100950-18cc93526ab4
github.com/kubesphere/sonargo v0.0.2
github.com/leodido/go-urn v1.1.0 // indirect
github.com/lib/pq v1.2.0 // indirect
github.com/mattn/go-sqlite3 v1.11.0 // indirect
github.com/onsi/ginkgo v1.12.0
......@@ -77,6 +81,7 @@ require (
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
google.golang.org/grpc v1.26.0
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/go-playground/validator.v9 v9.29.1 // indirect
gopkg.in/src-d/go-billy.v4 v4.3.0 // indirect
gopkg.in/src-d/go-git.v4 v4.11.0
gopkg.in/yaml.v2 v2.2.8
......@@ -96,6 +101,7 @@ require (
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
k8s.io/kubectl v0.17.3
openpitrix.io/openpitrix v0.4.9-0.20200611125425-ae07f141e797
sigs.k8s.io/application v0.8.3
sigs.k8s.io/controller-runtime v0.5.0
sigs.k8s.io/controller-tools v0.2.4
sigs.k8s.io/kubefed v0.2.0-alpha.1
......@@ -134,6 +140,7 @@ replace (
github.com/alecthomas/units => github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf
github.com/andybalholm/cascadia => github.com/andybalholm/cascadia v1.0.0
github.com/anmitsu/go-shlex => github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239
github.com/appscode/jsonpatch => github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30
github.com/armon/consul-api => github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6
github.com/asaskevich/govalidator => github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
github.com/aws/aws-sdk-go => github.com/aws/aws-sdk-go v1.22.2
......@@ -291,7 +298,6 @@ replace (
github.com/kubernetes-csi/csi-lib-utils => github.com/kubernetes-csi/csi-lib-utils v0.7.0
github.com/kubernetes-csi/csi-test => github.com/kubernetes-csi/csi-test v2.0.0+incompatible
github.com/kubernetes-csi/external-snapshotter/v2 => github.com/kubernetes-csi/external-snapshotter/v2 v2.1.0
github.com/kubernetes-sigs/application => github.com/kubesphere/application v0.0.0-20191210100950-18cc93526ab4
github.com/kubesphere/sonargo => github.com/kubesphere/sonargo v0.0.2
github.com/leodido/go-urn => github.com/leodido/go-urn v1.1.0
github.com/lib/pq => github.com/lib/pq v1.2.0
......@@ -466,7 +472,6 @@ replace (
k8s.io/kubernetes => k8s.io/kubernetes v1.14.0
k8s.io/metrics => k8s.io/metrics v0.17.3
k8s.io/utils => k8s.io/utils v0.0.0-20191114184206-e782cd3c129f
kubesphere.io/application => kubesphere.io/application v0.0.0-20190404151855-67ae7f915d4e
kubesphere.io/im => kubesphere.io/im v0.1.0
modernc.org/cc => modernc.org/cc v1.0.0
modernc.org/golex => modernc.org/golex v1.0.0
......@@ -480,6 +485,7 @@ replace (
openpitrix.io/openpitrix => openpitrix.io/openpitrix v0.4.9-0.20200611125425-ae07f141e797
rsc.io/goversion => rsc.io/goversion v1.0.0
rsc.io/letsencrypt => rsc.io/letsencrypt v0.0.1
sigs.k8s.io/application => kubesphere.io/application v1.0.0
sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.4.0
sigs.k8s.io/controller-tools => sigs.k8s.io/controller-tools v0.2.4
sigs.k8s.io/kubefed => sigs.k8s.io/kubefed v0.2.0-alpha.1
......
......@@ -44,6 +44,7 @@ github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRy
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30/go.mod h1:4AJxUpXUhv4N+ziTvIcWWXgeorXpxPZOfk9HdEVr96M=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
......@@ -293,8 +294,11 @@ github.com/kubernetes-csi/csi-lib-utils v0.7.0/go.mod h1:bze+2G9+cmoHxN6+WyG1qT4
github.com/kubernetes-csi/csi-test v2.0.0+incompatible/go.mod h1:YxJ4UiuPWIhMBkxUKY5c267DyA0uDZ/MtAimhx/2TA0=
github.com/kubernetes-csi/external-snapshotter/v2 v2.1.0 h1:a1cpbNAdOTHO7Lk5UO5tjcbYPEEamIxmzATt+pKoDhc=
github.com/kubernetes-csi/external-snapshotter/v2 v2.1.0/go.mod h1:dV5oB3U62KBdlf9ADWkMmjGd3USauqQtwIm2OZb5mqI=
github.com/kubernetes-sigs/application v0.8.3 h1:+yuwN6P0xpLKzFXH/oxv1OxiVhyKgtL9PJVUQTfM/+I=
github.com/kubesphere/application v0.0.0-20191210100950-18cc93526ab4 h1:pugSGmj80MTp+XA4OHiQJM/GxtrII9tf173GwTZLtYE=
github.com/kubesphere/application v0.0.0-20191210100950-18cc93526ab4/go.mod h1:sILRE7W0CquRyC51JNRj4U7OP7CJl3o62TcX5E6IcWs=
github.com/kubesphere/application v0.0.0-20200221140547-8beafe2fa7ef h1:0s/3VfJ9xP9cqLB7dKj1eXCfC+Nr8fy/5xUJhD2lojU=
github.com/kubesphere/application v0.0.0-20200221140547-8beafe2fa7ef/go.mod h1:Sn/bPGEhZxJeByRvkBo3I+n343KJ+5PBbhdmCdoJZX8=
github.com/kubesphere/kiali v0.15.1-0.20200520152915-769a61d75460 h1:EcC/7blefyiDDDq3xfBlQj/vHL2ytz/JEpgHkeXKbpc=
github.com/kubesphere/kiali v0.15.1-0.20200520152915-769a61d75460/go.mod h1:Y1EqeixoXkKkU8I+yvOfhdh21+8+etFE6wYOVT2XFdI=
github.com/kubesphere/sonargo v0.0.2 h1:hsSRE3sv3mkPcUAeSABdp7rtfcNW2zzeHXzFa01CTkU=
......@@ -579,6 +583,10 @@ k8s.io/metrics v0.17.3/go.mod h1:HEJGy1fhHOjHggW9rMDBJBD3YuGroH3Y1pnIRw9FFaI=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
kubesphere.io/application v0.0.0-20190404151855-67ae7f915d4e/go.mod h1:NhUQ0ZUdFz8NTQ+SvQG0JUKAn+q71v3TPExjsjRPIZI=
kubesphere.io/application v0.0.0-20200714170321-46533bef8155 h1:XfL/qmbzyF9NEFtg//6k3cQOdZVhNBvjRmU0mak/LmM=
kubesphere.io/application v0.0.0-20200714170321-46533bef8155/go.mod h1:Mv+ht9RE/QNtITYCzRbt3XTIN6t6so6cInmiyg6wOIg=
kubesphere.io/application v1.0.0 h1:1H9HOb2OryNdrlUqrrhqtKC+IWmeE1rUsjrtcgKczEk=
kubesphere.io/application v1.0.0/go.mod h1:Mv+ht9RE/QNtITYCzRbt3XTIN6t6so6cInmiyg6wOIg=
kubesphere.io/im v0.1.0 h1:Isu/WBOawUb4fzSlQeD1f6Vbq9pqFS0PmDg8v8iFYaY=
kubesphere.io/im v0.1.0/go.mod h1:DHJj/JngMUFyaXecLjBPXj/zk5Oi7ifIixLRp0qJkyA=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
......
......@@ -20,7 +20,7 @@ import (
"istio.io/client-go/pkg/apis/networking/v1alpha3"
"kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
"github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
appv1beta1 "sigs.k8s.io/application/pkg/apis/app/v1beta1"
)
func init() {
......@@ -31,5 +31,5 @@ func init() {
AddToSchemes = append(AddToSchemes, v1alpha3.SchemeBuilder.AddToScheme)
// Register application scheme
AddToSchemes = append(AddToSchemes, v1beta1.SchemeBuilder.AddToScheme)
AddToSchemes = append(AddToSchemes, appv1beta1.SchemeBuilder.AddToScheme)
}
......@@ -4,9 +4,6 @@ import (
"fmt"
"time"
applicationclient "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned"
applicationinformers "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions/app/v1beta1"
applicationlister "github.com/kubernetes-sigs/application/pkg/client/listers/app/v1beta1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
......@@ -26,6 +23,9 @@ import (
servicemeshinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/servicemesh/v1alpha2"
servicemeshlisters "kubesphere.io/kubesphere/pkg/client/listers/servicemesh/v1alpha2"
"kubesphere.io/kubesphere/pkg/controller/virtualservice/util"
applicationclient "sigs.k8s.io/application/pkg/client/clientset/versioned"
applicationinformers "sigs.k8s.io/application/pkg/client/informers/externalversions/app/v1beta1"
applicationlister "sigs.k8s.io/application/pkg/client/listers/app/v1beta1"
)
const (
......
......@@ -19,8 +19,6 @@ package informers
import (
snapshotclient "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/clientset/versioned"
snapshotinformer "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/informers/externalversions"
applicationclient "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned"
applicationinformers "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions"
istioclient "istio.io/client-go/pkg/clientset/versioned"
istioinformers "istio.io/client-go/pkg/informers/externalversions"
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
......@@ -29,6 +27,8 @@ import (
"k8s.io/client-go/kubernetes"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned"
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
applicationclient "sigs.k8s.io/application/pkg/client/clientset/versioned"
applicationinformers "sigs.k8s.io/application/pkg/client/informers/externalversions"
"time"
)
......
......@@ -2,11 +2,11 @@ package informers
import (
snapshotinformer "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/informers/externalversions"
appinformers "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions"
istioinformers "istio.io/client-go/pkg/informers/externalversions"
apiextensionsinformers "k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions"
"k8s.io/client-go/informers"
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
appinformers "sigs.k8s.io/application/pkg/client/informers/externalversions"
)
type nullInformerFactory struct {
......
......@@ -290,7 +290,6 @@ func (h *handler) passwordGrant(username string, password string, req *restful.R
return
default:
response.WriteError(http.StatusInternalServerError, apierrors.NewInternalError(err))
return
}
}
......
......@@ -3,7 +3,6 @@ package v1alpha3
import (
"github.com/google/go-cmp/cmp"
fakesnapshot "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/clientset/versioned/fake"
fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake"
fakeistio "istio.io/client-go/pkg/clientset/versioned/fake"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
......@@ -15,6 +14,7 @@ import (
fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource"
fakeapp "sigs.k8s.io/application/pkg/client/clientset/versioned/fake"
"testing"
)
......
......@@ -17,11 +17,11 @@ limitations under the License.
package application
import (
"github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
"github.com/kubernetes-sigs/application/pkg/client/informers/externalversions"
"k8s.io/apimachinery/pkg/labels"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/server/params"
"sigs.k8s.io/application/pkg/apis/app/v1beta1"
"sigs.k8s.io/application/pkg/client/informers/externalversions"
"sort"
)
......
......@@ -19,7 +19,6 @@ package resource
import (
"github.com/google/go-cmp/cmp"
fakesnapshot "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/clientset/versioned/fake"
fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake"
fakeistio "istio.io/client-go/pkg/clientset/versioned/fake"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
......@@ -30,6 +29,7 @@ import (
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/server/params"
fakeapp "sigs.k8s.io/application/pkg/client/clientset/versioned/fake"
"testing"
)
......
......@@ -17,12 +17,12 @@ limitations under the License.
package application
import (
appv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
"github.com/kubernetes-sigs/application/pkg/client/informers/externalversions"
"k8s.io/apimachinery/pkg/runtime"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
appv1beta1 "sigs.k8s.io/application/pkg/apis/app/v1beta1"
"sigs.k8s.io/application/pkg/client/informers/externalversions"
"time"
)
......
......@@ -18,13 +18,13 @@ package application
import (
"github.com/google/go-cmp/cmp"
appv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
"github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake"
"github.com/kubernetes-sigs/application/pkg/client/informers/externalversions"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query"
appv1beta1 "sigs.k8s.io/application/pkg/apis/app/v1beta1"
"sigs.k8s.io/application/pkg/client/clientset/versioned/fake"
"sigs.k8s.io/application/pkg/client/informers/externalversions"
"testing"
)
......
......@@ -19,7 +19,6 @@ package resource
import (
"github.com/google/go-cmp/cmp"
fakesnapshot "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/clientset/versioned/fake"
fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake"
fakeistio "istio.io/client-go/pkg/clientset/versioned/fake"
corev1 "k8s.io/api/core/v1"
fakeapiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake"
......@@ -29,6 +28,7 @@ import (
"kubesphere.io/kubesphere/pkg/apiserver/query"
fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
"kubesphere.io/kubesphere/pkg/informers"
fakeapp "sigs.k8s.io/application/pkg/client/clientset/versioned/fake"
"testing"
)
......
......@@ -18,7 +18,6 @@ package tenant
import (
"github.com/google/go-cmp/cmp"
fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake"
fakeistio "istio.io/client-go/pkg/clientset/versioned/fake"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
......@@ -35,6 +34,7 @@ import (
fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
"kubesphere.io/kubesphere/pkg/informers"
"reflect"
fakeapp "sigs.k8s.io/application/pkg/client/clientset/versioned/fake"
"testing"
)
......
......@@ -2,14 +2,14 @@ package k8s
import (
snapshotclient "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/clientset/versioned"
application "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned"
applicationclientset "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned"
istioclient "istio.io/client-go/pkg/clientset/versioned"
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
application "sigs.k8s.io/application/pkg/client/clientset/versioned"
applicationclientset "sigs.k8s.io/application/pkg/client/clientset/versioned"
)
type FakeClient struct {
......
......@@ -2,7 +2,6 @@ package k8s
import (
snapshotclient "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/clientset/versioned"
applicationclientset "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned"
istioclient "istio.io/client-go/pkg/clientset/versioned"
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/client-go/discovery"
......@@ -10,6 +9,7 @@ import (
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
applicationclientset "sigs.k8s.io/application/pkg/client/clientset/versioned"
"strings"
)
......
......@@ -2,13 +2,13 @@ package k8s
import (
snapshotclient "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/clientset/versioned"
application "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned"
istio "istio.io/client-go/pkg/clientset/versioned"
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
application "sigs.k8s.io/application/pkg/client/clientset/versioned"
)
type nullClient struct {
......
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta1
import (
"github.com/kubernetes-sigs/application/pkg/component"
cr "github.com/kubernetes-sigs/application/pkg/customresource"
"github.com/kubernetes-sigs/application/pkg/finalizer"
"github.com/kubernetes-sigs/application/pkg/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"strings"
)
// Mutate - mutate expected
func (a *Application) Mutate(rsrc interface{}, labels map[string]string, status interface{}, expected, dependent, observed *resource.ObjectBag) (*resource.ObjectBag, error) {
exp := resource.ObjectBag{}
for _, o := range observed.Items() {
o.Lifecycle = resource.LifecycleManaged
exp.Add(o)
}
return &exp, nil
}
// Finalize - execute finalizers
func (a *Application) Finalize(rsrc, sts interface{}, observed *resource.ObjectBag) error {
finalizer.Remove(a, finalizer.Cleanup)
return nil
}
//DependentResources - returns dependent rsrc
func (a *Application) DependentResources(rsrc interface{}) *resource.ObjectBag {
return &resource.ObjectBag{}
}
// ExpectedResources returns the list of resource/name for those resources created by
// the operator for this spec and those resources referenced by this operator.
// Mark resources as owned, referred
func (a *Application) ExpectedResources(rsrc interface{}, rsrclabels map[string]string, dependent, aggregated *resource.ObjectBag) (*resource.ObjectBag, error) {
return &resource.ObjectBag{}, nil
}
// GKVersions returns the gvks for the given gk
func GKVersions(s *runtime.Scheme, mgk metav1.GroupKind) []schema.GroupVersionKind {
gvks := []schema.GroupVersionKind{}
gk := schema.GroupKind{Group: mgk.Group, Kind: mgk.Kind}
for gvk := range s.AllKnownTypes() {
if gk != gvk.GroupKind() {
continue
}
gvks = append(gvks, gvk)
}
return gvks
}
// Observables - return selectors
func (a *Application) Observables(scheme *runtime.Scheme, rsrc interface{}, rsrclabels map[string]string, expected *resource.ObjectBag) []resource.Observable {
var observables []resource.Observable
if a.Spec.Selector == nil || a.Spec.Selector.MatchLabels == nil {
return observables
}
for _, gk := range a.Spec.ComponentGroupKinds {
listGK := gk
if !strings.HasSuffix(listGK.Kind, "List") {
listGK.Kind = listGK.Kind + "List"
}
for _, gvk := range GKVersions(scheme, listGK) {
ol, err := scheme.New(gvk)
if err == nil {
observable := resource.Observable{
ObjList: ol.(metav1.ListInterface),
Labels: a.Spec.Selector.MatchLabels,
Namespace: rsrc.(metav1.Object).GetNamespace(),
}
observables = append(observables, observable)
}
}
}
return observables
}
// Differs returns true if the resource needs to be updated
func (a *Application) Differs(expected metav1.Object, observed metav1.Object) bool {
return false
}
// UpdateComponentStatus use reconciled objects to update component status
func (a *Application) UpdateComponentStatus(rsrci, statusi interface{}, reconciled *resource.ObjectBag, err error) {
if a != nil {
stts := statusi.(*ApplicationStatus)
stts.UpdateStatus(reconciled.Objs(), err)
}
}
// ApplyDefaults default app crd
func (a *Application) ApplyDefaults() {
return
}
// UpdateRsrcStatus records status or error in status
func (a *Application) UpdateRsrcStatus(status interface{}, err error) bool {
appstatus := status.(*ApplicationStatus)
if status != nil {
a.Status = *appstatus
}
if err != nil {
a.Status.SetError("ErrorSeen", err.Error())
} else {
a.Status.ClearError()
}
return true
}
// Validate the Application
func (a *Application) Validate() error {
return nil
}
// Components returns components for this resource
func (a *Application) Components() []component.Component {
c := []component.Component{}
c = append(c, component.Component{
Handle: a,
Name: "app",
CR: a,
OwnerRef: a.OwnerRef(),
})
return c
}
// OwnerRef returns owner ref object with the component's resource as owner
func (a *Application) OwnerRef() *metav1.OwnerReference {
if !a.Spec.AddOwnerRef {
return nil
}
isController := false
gvk := schema.GroupVersionKind{
Group: SchemeGroupVersion.Group,
Version: SchemeGroupVersion.Version,
Kind: "Application",
}
ref := metav1.NewControllerRef(a, gvk)
ref.Controller = &isController
return ref
}
// NewRsrc - return a new resource object
func (a *Application) NewRsrc() cr.Handle {
return &Application{}
}
// NewStatus - return a resource status object
func (a *Application) NewStatus() interface{} {
s := a.Status.DeepCopy()
s.ComponentList = ComponentList{}
return s
}
// StatusDiffers returns True if there is a change in status
func (a *Application) StatusDiffers(new ApplicationStatus) bool {
return true
}
/*
Copyright 2018 The Kubernetes Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func (m *ApplicationStatus) addCondition(ctype ConditionType, status corev1.ConditionStatus, reason, message string) {
now := metav1.Now()
c := &Condition{
Type: ctype,
LastUpdateTime: now,
LastTransitionTime: now,
Status: status,
Reason: reason,
Message: message,
}
m.Conditions = append(m.Conditions, *c)
}
// setConditionValue updates or creates a new condition
func (m *ApplicationStatus) setConditionValue(ctype ConditionType, status corev1.ConditionStatus, reason, message string) {
var c *Condition
for i := range m.Conditions {
if m.Conditions[i].Type == ctype {
c = &m.Conditions[i]
}
}
if c == nil {
m.addCondition(ctype, status, reason, message)
} else {
// check message ?
if c.Status == status && c.Reason == reason && c.Message == message {
return
}
now := metav1.Now()
c.LastUpdateTime = now
if c.Status != status {
c.LastTransitionTime = now
}
c.Status = status
c.Reason = reason
c.Message = message
}
}
// RemoveCondition removes the condition with the provided type.
func (m *ApplicationStatus) RemoveCondition(ctype ConditionType) {
for i, c := range m.Conditions {
if c.Type == ctype {
m.Conditions[i] = m.Conditions[len(m.Conditions)-1]
m.Conditions = m.Conditions[:len(m.Conditions)-1]
break
}
}
}
// GetCondition get existing condition
func (m *ApplicationStatus) GetCondition(ctype ConditionType) *Condition {
for i := range m.Conditions {
if m.Conditions[i].Type == ctype {
return &m.Conditions[i]
}
}
return nil
}
// IsConditionTrue - if condition is true
func (m *ApplicationStatus) IsConditionTrue(ctype ConditionType) bool {
if c := m.GetCondition(ctype); c != nil {
return c.Status == corev1.ConditionTrue
}
return false
}
// IsReady returns true if ready condition is set
func (m *ApplicationStatus) IsReady() bool { return m.IsConditionTrue(Ready) }
// IsNotReady returns true if ready condition is set
func (m *ApplicationStatus) IsNotReady() bool { return !m.IsConditionTrue(Ready) }
// ConditionReason - return condition reason
func (m *ApplicationStatus) ConditionReason(ctype ConditionType) string {
if c := m.GetCondition(ctype); c != nil {
return c.Reason
}
return ""
}
// Ready - shortcut to set ready contition to true
func (m *ApplicationStatus) Ready(reason, message string) {
m.SetCondition(Ready, reason, message)
}
// NotReady - shortcut to set ready contition to false
func (m *ApplicationStatus) NotReady(reason, message string) {
m.ClearCondition(Ready, reason, message)
}
// SetError - shortcut to set error condition
func (m *ApplicationStatus) SetError(reason, message string) {
m.SetCondition(Error, reason, message)
}
// ClearError - shortcut to set error condition
func (m *ApplicationStatus) ClearError() {
m.ClearCondition(Error, "NoError", "No error seen")
}
// Settled - shortcut to set Settled contition to true
func (m *ApplicationStatus) Settled(reason, message string) {
m.SetCondition(Settled, reason, message)
}
// NotSettled - shortcut to set Settled contition to false
func (m *ApplicationStatus) NotSettled(reason, message string) {
m.ClearCondition(Settled, reason, message)
}
// EnsureCondition useful for adding default conditions
func (m *ApplicationStatus) EnsureCondition(ctype ConditionType) {
if c := m.GetCondition(ctype); c != nil {
return
}
m.addCondition(ctype, corev1.ConditionUnknown, ReasonInit, "Not Observed")
}
// EnsureStandardConditions - helper to inject standard conditions
func (m *ApplicationStatus) EnsureStandardConditions() {
m.EnsureCondition(Ready)
m.EnsureCondition(Settled)
m.EnsureCondition(Error)
}
// ClearCondition updates or creates a new condition
func (m *ApplicationStatus) ClearCondition(ctype ConditionType, reason, message string) {
m.setConditionValue(ctype, corev1.ConditionFalse, reason, message)
}
// SetCondition updates or creates a new condition
func (m *ApplicationStatus) SetCondition(ctype ConditionType, reason, message string) {
m.setConditionValue(ctype, corev1.ConditionTrue, reason, message)
}
// RemoveAllConditions updates or creates a new condition
func (m *ApplicationStatus) RemoveAllConditions() {
m.Conditions = []Condition{}
}
// ClearAllConditions updates or creates a new condition
func (m *ApplicationStatus) ClearAllConditions() {
for i := range m.Conditions {
m.Conditions[i].Status = corev1.ConditionFalse
}
}
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package v1beta1 contains API Schema definitions for the app v1beta1 API group
// +k8s:openapi-gen=true
// +k8s:deepcopy-gen=package,register
// +k8s:conversion-gen=github.com/kubernetes-sigs/application/pkg/apis/app
// +k8s:defaulter-gen=TypeMeta
// +groupName=app.k8s.io
package v1beta1
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// NOTE: Boilerplate only. Ignore this file.
// Package v1beta1 contains API Schema definitions for the app v1beta1 API group
// +k8s:openapi-gen=true
// +k8s:deepcopy-gen=package,register
// +k8s:conversion-gen=github.com/kubernetes-sigs/application/pkg/apis/app
// +k8s:defaulter-gen=TypeMeta
// +groupName=app.k8s.io
package v1beta1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/runtime/scheme"
)
var (
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: "app.k8s.io", Version: "v1beta1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
// AddToScheme is required by pkg/client/...
AddToScheme = SchemeBuilder.AddToScheme
)
// Resource is required by pkg/client/listers/...
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
/*
Copyright 2018 The Kubernetes Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta1
import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
policyv1 "k8s.io/api/policy/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// Constants defining labels
const (
StatusReady = "Ready"
StatusInProgress = "InProgress"
StatusDisabled = "Disabled"
)
func (s *ObjectStatus) update(rsrc metav1.Object) {
ro := rsrc.(runtime.Object)
gvk := ro.GetObjectKind().GroupVersionKind()
s.Link = rsrc.GetSelfLink()
s.Name = rsrc.GetName()
s.Group = gvk.GroupVersion().String()
s.Kind = gvk.GroupKind().Kind
s.Status = StatusReady
}
// ResetComponentList - reset component list objects
func (m *ApplicationStatus) ResetComponentList() {
m.ComponentList.Objects = []ObjectStatus{}
}
// UpdateStatus the component status
func (m *ApplicationStatus) UpdateStatus(rsrcs []metav1.Object, err error) {
var ready = true
for _, r := range rsrcs {
os := ObjectStatus{}
os.update(r)
switch r.(type) {
case *appsv1.StatefulSet:
os.Status = stsStatus(r.(*appsv1.StatefulSet))
case *policyv1.PodDisruptionBudget:
os.Status = pdbStatus(r.(*policyv1.PodDisruptionBudget))
case *appsv1.Deployment:
os.Status = deploymentStatus(r.(*appsv1.Deployment))
case *appsv1.ReplicaSet:
os.Status = replicasetStatus(r.(*appsv1.ReplicaSet))
case *appsv1.DaemonSet:
os.Status = daemonsetStatus(r.(*appsv1.DaemonSet))
case *corev1.Pod:
os.Status = podStatus(r.(*corev1.Pod))
case *corev1.Service:
os.Status = serviceStatus(r.(*corev1.Service))
case *corev1.PersistentVolumeClaim:
os.Status = pvcStatus(r.(*corev1.PersistentVolumeClaim))
//case *corev1.ReplicationController:
// Ingress
default:
os.Status = StatusReady
}
m.ComponentList.Objects = append(m.ComponentList.Objects, os)
}
for _, os := range m.ComponentList.Objects {
if os.Status != StatusReady {
ready = false
}
}
if ready {
m.Ready("ComponentsReady", "all components ready")
} else {
m.NotReady("ComponentsNotReady", "some components not ready")
}
if err != nil {
m.SetCondition(Error, "ErrorSeen", err.Error())
}
}
// Resource specific logic -----------------------------------
// Statefulset
func stsStatus(rsrc *appsv1.StatefulSet) string {
if rsrc.Status.ReadyReplicas == *rsrc.Spec.Replicas && rsrc.Status.CurrentReplicas == *rsrc.Spec.Replicas {
return StatusReady
}
return StatusInProgress
}
// Deployment
func deploymentStatus(rsrc *appsv1.Deployment) string {
status := StatusInProgress
progress := true
available := true
for _, c := range rsrc.Status.Conditions {
switch c.Type {
case appsv1.DeploymentProgressing:
// https://github.com/kubernetes/kubernetes/blob/a3ccea9d8743f2ff82e41b6c2af6dc2c41dc7b10/pkg/controller/deployment/progress.go#L52
if c.Status != corev1.ConditionTrue || c.Reason != "NewReplicaSetAvailable" {
progress = false
}
case appsv1.DeploymentAvailable:
if c.Status == corev1.ConditionFalse {
available = false
}
}
}
if progress && available {
status = StatusReady
}
return status
}
// Replicaset
func replicasetStatus(rsrc *appsv1.ReplicaSet) string {
status := StatusInProgress
failure := false
for _, c := range rsrc.Status.Conditions {
switch c.Type {
// https://github.com/kubernetes/kubernetes/blob/a3ccea9d8743f2ff82e41b6c2af6dc2c41dc7b10/pkg/controller/replicaset/replica_set_utils.go
case appsv1.ReplicaSetReplicaFailure:
if c.Status == corev1.ConditionTrue {
failure = true
break
}
}
}
if !failure && rsrc.Status.ReadyReplicas == rsrc.Status.Replicas && rsrc.Status.Replicas == rsrc.Status.AvailableReplicas {
status = StatusReady
}
return status
}
// Daemonset
func daemonsetStatus(rsrc *appsv1.DaemonSet) string {
status := StatusInProgress
if rsrc.Status.DesiredNumberScheduled == rsrc.Status.NumberAvailable && rsrc.Status.DesiredNumberScheduled == rsrc.Status.NumberReady {
status = StatusReady
}
return status
}
// PVC
func pvcStatus(rsrc *corev1.PersistentVolumeClaim) string {
status := StatusInProgress
if rsrc.Status.Phase == corev1.ClaimBound {
status = StatusReady
}
return status
}
// Service
func serviceStatus(rsrc *corev1.Service) string {
status := StatusReady
return status
}
// Pod
func podStatus(rsrc *corev1.Pod) string {
status := StatusInProgress
for i := range rsrc.Status.Conditions {
if rsrc.Status.Conditions[i].Type == corev1.PodReady &&
rsrc.Status.Conditions[i].Status == corev1.ConditionTrue {
status = StatusReady
break
}
}
return status
}
// PodDisruptionBudget
func pdbStatus(rsrc *policyv1.PodDisruptionBudget) string {
if rsrc.Status.CurrentHealthy >= rsrc.Status.DesiredHealthy {
return StatusReady
}
return StatusInProgress
}
/*
Copyright 2018 The Kubernetes Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package component
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"reflect"
"strings"
)
// Constants defining labels
const (
LabelCR = "custom-resource"
LabelCRName = "custom-resource-name"
LabelCRNamespace = "custom-resource-namespace"
LabelComponent = "component"
)
// Labels return
func Labels(cr metav1.Object, component string) map[string]string {
return map[string]string{
LabelCR: strings.Trim(reflect.TypeOf(cr).String(), "*"),
LabelCRName: cr.GetName(),
LabelCRNamespace: cr.GetNamespace(),
LabelComponent: component,
}
}
// Labels return the common labels for a resource
func (c *Component) Labels() map[string]string {
return Labels(c.CR, c.Name)
}
// Merge is used to merge multiple maps into the target map
func (out KVMap) Merge(kvmaps ...KVMap) {
for _, kvmap := range kvmaps {
for k, v := range kvmap {
out[k] = v
}
}
}
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package component contains component definition
package component
/*
Copyright 2018 The Kubernetes Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package component
import (
"github.com/kubernetes-sigs/application/pkg/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// Handle is an interface for operating on logical Components of a CR
type Handle interface {
DependentResources(rsrc interface{}) *resource.ObjectBag
ExpectedResources(rsrc interface{}, labels map[string]string, dependent, aggregated *resource.ObjectBag) (*resource.ObjectBag, error)
Observables(scheme *runtime.Scheme, rsrc interface{}, labels map[string]string, expected *resource.ObjectBag) []resource.Observable
Mutate(rsrc interface{}, rsrclabels map[string]string, status interface{}, expected, dependent, observed *resource.ObjectBag) (*resource.ObjectBag, error)
Differs(expected metav1.Object, observed metav1.Object) bool
UpdateComponentStatus(rsrc, status interface{}, reconciled *resource.ObjectBag, err error)
Finalize(rsrc, status interface{}, observed *resource.ObjectBag) error
}
/*
Copyright 2018 The Kubernetes Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package customresource
import (
component "github.com/kubernetes-sigs/application/pkg/component"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"reflect"
"strings"
)
// Labels return custom resource label
func (hv *HandleValue) Labels() map[string]string {
c := hv.Handle.(metav1.Object)
return map[string]string{
component.LabelCR: strings.Trim(reflect.TypeOf(c).String(), "*"),
component.LabelCRName: c.GetName(),
component.LabelCRNamespace: c.GetNamespace(),
}
}
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package customresource contains component definition
package customresource
/*
Copyright 2018 The Kubernetes Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package customresource
import (
"github.com/kubernetes-sigs/application/pkg/component"
)
// Handle is an interface for operating on CRs
type Handle interface {
ApplyDefaults()
Validate() error
Components() []component.Component
UpdateRsrcStatus(interface{}, error) bool
NewRsrc() Handle
NewStatus() interface{}
}
/*
Copyright 2018 The Kubernetes Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package customresource
// HandleValue is to add methods for an interface object
type HandleValue struct {
Handle
}
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package finalizer contains storage releated code
// +k8s:deepcopy-gen=package,register
package finalizer
/*
Copyright 2018 The Kubernetes Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package finalizer
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// const fileds
const (
Cleanup = "cleanup"
)
// Add adds the finalizer string
func Add(o metav1.Object, new string) {
exists := false
existing := o.GetFinalizers()
for _, f := range existing {
if f == new {
exists = true
break
}
}
if !exists {
existing = append(existing, new)
o.SetFinalizers(existing)
}
}
// Remove removes the finalizer string
func Remove(o metav1.Object, new string) {
foundat := -1
existing := o.GetFinalizers()
for i, f := range existing {
if f == new {
foundat = i
break
}
}
if foundat != -1 {
existing[foundat] = existing[len(existing)-1]
o.SetFinalizers(existing[:len(existing)-1])
}
}
// EnsureStandard adds standard finalizers
func EnsureStandard(o metav1.Object) {
Add(o, Cleanup)
}
// +build !ignore_autogenerated
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
package finalizer
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package resource contains resource methods
package resource
/*
Copyright 2018 The Kubernetes Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package resource
import (
"bufio"
"bytes"
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/kubernetes/scheme"
"os"
"reflect"
"strings"
"text/template"
)
// objFromReader reads Object from []byte spec
func objFromReader(b *bufio.Reader, data interface{}, list metav1.ListInterface) (*Object, error) {
var exdoc bytes.Buffer
r := yaml.NewYAMLReader(b)
doc, err := r.Read()
if err == nil {
tmpl, e := template.New("tmpl").Parse(string(doc))
err = e
if err == nil {
err = tmpl.Execute(&exdoc, data)
if err == nil {
d := scheme.Codecs.UniversalDeserializer()
obj, _, e := d.Decode(exdoc.Bytes(), nil, nil)
err = e
if err == nil {
return &Object{
Obj: obj.DeepCopyObject().(metav1.Object),
ObjList: list,
Lifecycle: LifecycleManaged,
}, nil
}
}
}
}
return nil, err
}
// ObjFromString populates Object from string spec
func ObjFromString(spec string, values interface{}, list metav1.ListInterface) (*Object, error) {
return objFromReader(bufio.NewReader(strings.NewReader(spec)), values, list)
}
// ObjFromFile populates Object from file
func ObjFromFile(path string, values interface{}, list metav1.ListInterface) (*Object, error) {
f, err := os.Open(path)
if err == nil {
return objFromReader(bufio.NewReader(f), values, list)
}
return nil, err
}
// ObservablesFromObjects returns ObservablesFromObjects
func ObservablesFromObjects(scheme *runtime.Scheme, bag *ObjectBag, labels map[string]string) []Observable {
var gk schema.GroupKind
var observables []Observable
gkmap := map[schema.GroupKind]struct{}{}
for _, obj := range bag.Items() {
if obj.ObjList != nil {
ro := obj.Obj.(runtime.Object)
kinds, _, err := scheme.ObjectKinds(ro)
if err == nil {
// Expect only 1 kind. If there is more than one kind this is probably an edge case such as ListOptions.
if len(kinds) != 1 {
err = fmt.Errorf("Expected exactly 1 kind for Object %T, but found %s kinds", ro, kinds)
}
}
// Cache the Group and Kind for the OwnerType
if err == nil {
gk = schema.GroupKind{Group: kinds[0].Group, Kind: kinds[0].Kind}
} else {
gk = ro.GetObjectKind().GroupVersionKind().GroupKind()
}
if _, ok := gkmap[gk]; !ok {
gkmap[gk] = struct{}{}
observable := Observable{
ObjList: obj.ObjList,
Labels: labels,
}
observables = append(observables, observable)
}
} else {
observable := Observable{
Obj: obj.Obj,
}
observables = append(observables, observable)
}
}
return observables
}
// ReferredObject returns a reffered object
func ReferredObject(obj metav1.Object, name, namespace string) Object {
obj.SetName(name)
obj.SetNamespace(namespace)
return Object{
Lifecycle: LifecycleReferred,
Obj: obj,
}
}
// Add adds to the Object bag
func (b *ObjectBag) Add(objs ...Object) {
b.objects = append(b.objects, objs...)
}
// Items get items from the Object bag
func (b *ObjectBag) Items() []Object {
return b.objects
}
// Objs get items from the Object bag
func (b *ObjectBag) Objs() []metav1.Object {
var objs []metav1.Object
for _, o := range b.Items() {
objs = append(objs, o.Obj)
}
return objs
}
// Get returns an item which matched the kind and name
func (b *ObjectBag) Get(inobj metav1.Object, name, namespace string) metav1.Object {
inobj.SetName(name)
inobj.SetNamespace(namespace)
for _, obj := range b.Items() {
otype := reflect.TypeOf(obj.Obj).String()
intype := reflect.TypeOf(inobj).String()
if otype == intype && obj.Obj.GetName() == inobj.GetName() && obj.Obj.GetNamespace() == inobj.GetNamespace() {
return obj.Obj
}
}
return nil
}
// Delete returns an item which matched the kind and name
func (b *ObjectBag) Delete(inobj metav1.Object) {
for i, obj := range b.objects {
otype := reflect.TypeOf(obj.Obj).String()
intype := reflect.TypeOf(inobj).String()
if otype == intype && obj.Obj.GetName() == inobj.GetName() && obj.Obj.GetNamespace() == inobj.GetNamespace() {
b.objects[i] = b.objects[len(b.objects)-1]
b.objects = b.objects[:len(b.objects)-1]
break
}
}
}
// Validate validates the LocalObjectReference
func (s *LocalObjectReference) Validate(fp *field.Path, sfield string, errs field.ErrorList, required bool) field.ErrorList {
fp = fp.Child(sfield)
if s == nil {
if required {
errs = append(errs, field.Required(fp, "Required "+sfield+" missing"))
}
return errs
}
if s.Name == "" {
errs = append(errs, field.Required(fp.Child("name"), "name is required"))
}
return errs
}
/*
Copyright 2018 The Kubernetes Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package resource
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Common const definitions
const (
LifecycleManaged = "managed"
LifecycleReferred = "referred"
)
// ObjectBag abstracts dealing with group of objects
// For now it is a simple list
type ObjectBag struct {
objects []Object
}
// Object is a container to capture the k8s resource info to be used by controller
type Object struct {
// Lifecycle can be: managed, reference
Lifecycle string
// Obj refers to the resource object can be: sts, service, secret, pvc, ..
Obj metav1.Object
// ObjList refers to the list of resource objects
ObjList metav1.ListInterface
}
// Observable captures the k8s resource info and selector to fetch child resources
type Observable struct {
// ObjList refers to the list of resource objects
ObjList metav1.ListInterface
// Obj refers to the resource object can be: sts, service, secret, pvc, ..
Obj metav1.Object
// Labels list of labels
Labels map[string]string
// Typemeta - needed for go test fake client
Type metav1.TypeMeta
// Namespace - only observe resources in this namespace
Namespace string
}
// LocalObjectReference with validation
type LocalObjectReference struct {
corev1.LocalObjectReference `json:",inline"`
}
// GetObjectFn is a type for any function that returns resource info
type GetObjectFn func(interface{}) (*Object, error)
# Minimal Go logging using klog
This package implements the [logr interface](https://github.com/go-logr/logr)
in terms of Kubernetes' [klog](https://github.com/kubernetes/klog). This
provides a relatively minimalist API to logging in Go, backed by a well-proven
implementation.
This is a BETA grade implementation.
// Package klogr implements github.com/go-logr/logr.Logger in terms of
// k8s.io/klog.
package klogr
import (
"bytes"
"encoding/json"
"fmt"
"runtime"
"sort"
"github.com/go-logr/logr"
"k8s.io/klog"
)
// New returns a logr.Logger which is implemented by klog.
func New() logr.Logger {
return klogger{
level: 0,
prefix: "",
values: nil,
}
}
type klogger struct {
level int
prefix string
values []interface{}
}
func (l klogger) clone() klogger {
return klogger{
level: l.level,
prefix: l.prefix,
values: copySlice(l.values),
}
}
func copySlice(in []interface{}) []interface{} {
out := make([]interface{}, len(in))
copy(out, in)
return out
}
// Magic string for intermediate frames that we should ignore.
const autogeneratedFrameName = "<autogenerated>"
// Discover how many frames we need to climb to find the caller. This approach
// was suggested by Ian Lance Taylor of the Go team, so it *should* be safe
// enough (famous last words).
func framesToCaller() int {
// 1 is the immediate caller. 3 should be too many.
for i := 1; i < 3; i++ {
_, file, _, _ := runtime.Caller(i + 1) // +1 for this function's frame
if file != autogeneratedFrameName {
return i
}
}
return 1 // something went wrong, this is safe
}
// trimDuplicates will deduplicates elements provided in multiple KV tuple
// slices, whilst maintaining the distinction between where the items are
// contained.
func trimDuplicates(kvLists ...[]interface{}) [][]interface{} {
// maintain a map of all seen keys
seenKeys := map[interface{}]struct{}{}
// build the same number of output slices as inputs
outs := make([][]interface{}, len(kvLists))
// iterate over the input slices backwards, as 'later' kv specifications
// of the same key will take precedence over earlier ones
for i := len(kvLists) - 1; i >= 0; i-- {
// initialise this output slice
outs[i] = []interface{}{}
// obtain a reference to the kvList we are processing
kvList := kvLists[i]
// start iterating at len(kvList) - 2 (i.e. the 2nd last item) for
// slices that have an even number of elements.
// We add (len(kvList) % 2) here to handle the case where there is an
// odd number of elements in a kvList.
// If there is an odd number, then the last element in the slice will
// have the value 'null'.
for i2 := len(kvList) - 2 + (len(kvList) % 2); i2 >= 0; i2 -= 2 {
k := kvList[i2]
// if we have already seen this key, do not include it again
if _, ok := seenKeys[k]; ok {
continue
}
// make a note that we've observed a new key
seenKeys[k] = struct{}{}
// attempt to obtain the value of the key
var v interface{}
// i2+1 should only ever be out of bounds if we handling the first
// iteration over a slice with an odd number of elements
if i2+1 < len(kvList) {
v = kvList[i2+1]
}
// add this KV tuple to the *start* of the output list to maintain
// the original order as we are iterating over the slice backwards
outs[i] = append([]interface{}{k, v}, outs[i]...)
}
}
return outs
}
func flatten(kvList ...interface{}) string {
keys := make([]string, 0, len(kvList))
vals := make(map[string]interface{}, len(kvList))
for i := 0; i < len(kvList); i += 2 {
k, ok := kvList[i].(string)
if !ok {
panic(fmt.Sprintf("key is not a string: %s", pretty(kvList[i])))
}
var v interface{}
if i+1 < len(kvList) {
v = kvList[i+1]
}
keys = append(keys, k)
vals[k] = v
}
sort.Strings(keys)
buf := bytes.Buffer{}
for i, k := range keys {
v := vals[k]
if i > 0 {
buf.WriteRune(' ')
}
buf.WriteString(pretty(k))
buf.WriteString("=")
buf.WriteString(pretty(v))
}
return buf.String()
}
func pretty(value interface{}) string {
jb, _ := json.Marshal(value)
return string(jb)
}
func (l klogger) Info(msg string, kvList ...interface{}) {
if l.Enabled() {
msgStr := flatten("msg", msg)
trimmed := trimDuplicates(l.values, kvList)
fixedStr := flatten(trimmed[0]...)
userStr := flatten(trimmed[1]...)
klog.InfoDepth(framesToCaller(), l.prefix, " ", msgStr, " ", fixedStr, " ", userStr)
}
}
func (l klogger) Enabled() bool {
return bool(klog.V(klog.Level(l.level)))
}
func (l klogger) Error(err error, msg string, kvList ...interface{}) {
msgStr := flatten("msg", msg)
var loggableErr interface{}
if err != nil {
loggableErr = err.Error()
}
errStr := flatten("error", loggableErr)
trimmed := trimDuplicates(l.values, kvList)
fixedStr := flatten(trimmed[0]...)
userStr := flatten(trimmed[1]...)
klog.ErrorDepth(framesToCaller(), l.prefix, " ", msgStr, " ", errStr, " ", fixedStr, " ", userStr)
}
func (l klogger) V(level int) logr.InfoLogger {
new := l.clone()
new.level = level
return new
}
// WithName returns a new logr.Logger with the specified name appended. klogr
// uses '/' characters to separate name elements. Callers should not pass '/'
// in the provided name string, but this library does not actually enforce that.
func (l klogger) WithName(name string) logr.Logger {
new := l.clone()
if len(l.prefix) > 0 {
new.prefix = l.prefix + "/"
}
new.prefix += name
return new
}
func (l klogger) WithValues(kvList ...interface{}) logr.Logger {
new := l.clone()
new.values = append(new.values, kvList...)
return new
}
var _ logr.Logger = klogger{}
var _ logr.InfoLogger = klogger{}
......@@ -136,9 +136,9 @@ github.com/elastic/go-elasticsearch/v7/estransport
github.com/elastic/go-elasticsearch/v7/internal/version
# github.com/emicklei/go-restful v2.11.1+incompatible => github.com/emicklei/go-restful v2.9.5+incompatible
github.com/emicklei/go-restful
github.com/emicklei/go-restful/log
# github.com/emicklei/go-restful-openapi v1.0.0 => github.com/emicklei/go-restful-openapi v1.0.0
github.com/emicklei/go-restful-openapi
github.com/emicklei/go-restful/log
# github.com/emirpasic/gods v1.12.0 => github.com/emirpasic/gods v1.12.0
github.com/emirpasic/gods/containers
github.com/emirpasic/gods/lists
......@@ -342,22 +342,6 @@ github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/informers/externalv
github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/informers/externalversions/volumesnapshot
github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/informers/externalversions/volumesnapshot/v1beta1
github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/listers/volumesnapshot/v1beta1
# github.com/kubernetes-sigs/application v0.0.0-20191210100950-18cc93526ab4 => github.com/kubesphere/application v0.0.0-20191210100950-18cc93526ab4
github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1
github.com/kubernetes-sigs/application/pkg/client/clientset/versioned
github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake
github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/scheme
github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1
github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1/fake
github.com/kubernetes-sigs/application/pkg/client/informers/externalversions
github.com/kubernetes-sigs/application/pkg/client/informers/externalversions/app
github.com/kubernetes-sigs/application/pkg/client/informers/externalversions/app/v1beta1
github.com/kubernetes-sigs/application/pkg/client/informers/externalversions/internalinterfaces
github.com/kubernetes-sigs/application/pkg/client/listers/app/v1beta1
github.com/kubernetes-sigs/application/pkg/component
github.com/kubernetes-sigs/application/pkg/customresource
github.com/kubernetes-sigs/application/pkg/finalizer
github.com/kubernetes-sigs/application/pkg/resource
# github.com/kubesphere/sonargo v0.0.2 => github.com/kubesphere/sonargo v0.0.2
github.com/kubesphere/sonargo/sonar
# github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de => github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de
......@@ -1338,6 +1322,7 @@ k8s.io/gengo/parser
k8s.io/gengo/types
# k8s.io/klog v1.0.0 => k8s.io/klog v1.0.0
k8s.io/klog
k8s.io/klog/klogr
# k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a => k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
k8s.io/kube-openapi/cmd/openapi-gen/
k8s.io/kube-openapi/cmd/openapi-gen/args
......@@ -1374,7 +1359,22 @@ openpitrix.io/openpitrix/pkg/util/reflectutil
openpitrix.io/openpitrix/pkg/util/stringutil
openpitrix.io/openpitrix/pkg/util/yamlutil
openpitrix.io/openpitrix/pkg/version
# sigs.k8s.io/application v0.8.3 => kubesphere.io/application v1.0.0
sigs.k8s.io/application/controllers
sigs.k8s.io/application/pkg/apis/app/v1beta1
sigs.k8s.io/application/pkg/client/clientset/versioned
sigs.k8s.io/application/pkg/client/clientset/versioned/fake
sigs.k8s.io/application/pkg/client/clientset/versioned/scheme
sigs.k8s.io/application/pkg/client/clientset/versioned/typed/app/v1beta1
sigs.k8s.io/application/pkg/client/clientset/versioned/typed/app/v1beta1/fake
sigs.k8s.io/application/pkg/client/informers/externalversions
sigs.k8s.io/application/pkg/client/informers/externalversions/app
sigs.k8s.io/application/pkg/client/informers/externalversions/app/v1beta1
sigs.k8s.io/application/pkg/client/informers/externalversions/internalinterfaces
sigs.k8s.io/application/pkg/client/listers/app/v1beta1
# sigs.k8s.io/controller-runtime v0.5.0 => sigs.k8s.io/controller-runtime v0.4.0
sigs.k8s.io/controller-runtime
sigs.k8s.io/controller-runtime/pkg/builder
sigs.k8s.io/controller-runtime/pkg/cache
sigs.k8s.io/controller-runtime/pkg/cache/internal
sigs.k8s.io/controller-runtime/pkg/client
......@@ -1382,6 +1382,7 @@ sigs.k8s.io/controller-runtime/pkg/client/apiutil
sigs.k8s.io/controller-runtime/pkg/client/config
sigs.k8s.io/controller-runtime/pkg/controller
sigs.k8s.io/controller-runtime/pkg/controller/controllerutil
sigs.k8s.io/controller-runtime/pkg/conversion
sigs.k8s.io/controller-runtime/pkg/envtest
sigs.k8s.io/controller-runtime/pkg/envtest/printer
sigs.k8s.io/controller-runtime/pkg/event
......@@ -1407,6 +1408,7 @@ sigs.k8s.io/controller-runtime/pkg/source
sigs.k8s.io/controller-runtime/pkg/source/internal
sigs.k8s.io/controller-runtime/pkg/webhook
sigs.k8s.io/controller-runtime/pkg/webhook/admission
sigs.k8s.io/controller-runtime/pkg/webhook/conversion
sigs.k8s.io/controller-runtime/pkg/webhook/internal/certwatcher
sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics
# sigs.k8s.io/controller-tools v0.2.4 => sigs.k8s.io/controller-tools v0.2.4
......
Copyright {{.Year}} {{.Holder}}
SPDX-License-Identifier: Apache-2.0
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package controllers
import (
"context"
"fmt"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/client-go/util/retry"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
appv1beta1 "sigs.k8s.io/application/pkg/apis/app/v1beta1"
)
const (
loggerCtxKey = "logger"
)
// ApplicationReconciler reconciles a Application object
type ApplicationReconciler struct {
client.Client
Mapper meta.RESTMapper
Log logr.Logger
Scheme *runtime.Scheme
}
// +kubebuilder:rbac:groups=app.k8s.io,resources=applications,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=app.k8s.io,resources=applications/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=*,resources=*,verbs=list;get;update;patch;watch
func (r *ApplicationReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
rootCtx := context.Background()
logger := r.Log.WithValues("application", req.NamespacedName)
ctx := context.WithValue(rootCtx, loggerCtxKey, logger)
var app appv1beta1.Application
err := r.Get(ctx, req.NamespacedName, &app)
if err != nil {
if apierrors.IsNotFound(err) {
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}
// Application is in the process of being deleted, so no need to do anything.
if app.DeletionTimestamp != nil {
return ctrl.Result{}, nil
}
resources, errs := r.updateComponents(ctx, &app)
newApplicationStatus := r.getNewApplicationStatus(ctx, &app, resources, &errs)
newApplicationStatus.ObservedGeneration = app.Generation
if equality.Semantic.DeepEqual(newApplicationStatus, &app.Status) {
return ctrl.Result{}, nil
}
err = r.updateApplicationStatus(ctx, req.NamespacedName, newApplicationStatus)
return ctrl.Result{}, err
}
func (r *ApplicationReconciler) updateComponents(ctx context.Context, app *appv1beta1.Application) ([]*unstructured.Unstructured, []error) {
var errs []error
resources := r.fetchComponentListResources(ctx, app.Spec.ComponentGroupKinds, app.Spec.Selector, app.Namespace, &errs)
if app.Spec.AddOwnerRef {
ownerRef := metav1.NewControllerRef(app, appv1beta1.GroupVersion.WithKind("Application"))
*ownerRef.Controller = false
if err := r.setOwnerRefForResources(ctx, *ownerRef, resources); err != nil {
errs = append(errs, err)
}
}
return resources, errs
}
func (r *ApplicationReconciler) getNewApplicationStatus(ctx context.Context, app *appv1beta1.Application, resources []*unstructured.Unstructured, errList *[]error) *appv1beta1.ApplicationStatus {
objectStatuses := r.objectStatuses(ctx, resources, errList)
errs := utilerrors.NewAggregate(*errList)
aggReady, countReady := aggregateReady(objectStatuses)
newApplicationStatus := app.Status.DeepCopy()
newApplicationStatus.ComponentList = appv1beta1.ComponentList{
Objects: objectStatuses,
}
newApplicationStatus.ComponentsReady = fmt.Sprintf("%d/%d", countReady, len(objectStatuses))
if errs != nil {
setReadyUnknownCondition(newApplicationStatus, "ComponentsReadyUnknown", "failed to aggregate all components' statuses, check the Error condition for details")
} else if aggReady {
setReadyCondition(newApplicationStatus, "ComponentsReady", "all components ready")
} else {
setNotReadyCondition(newApplicationStatus, "ComponentsNotReady", fmt.Sprintf("%d components not ready", len(objectStatuses)-countReady))
}
if errs != nil {
setErrorCondition(newApplicationStatus, "ErrorSeen", errs.Error())
} else {
clearErrorCondition(newApplicationStatus)
}
return newApplicationStatus
}
func (r *ApplicationReconciler) fetchComponentListResources(ctx context.Context, groupKinds []metav1.GroupKind, selector *metav1.LabelSelector, namespace string, errs *[]error) []*unstructured.Unstructured {
logger := getLoggerOrDie(ctx)
var resources []*unstructured.Unstructured
if selector == nil {
logger.Info("No selector is specified")
return resources
}
for _, gk := range groupKinds {
mapping, err := r.Mapper.RESTMapping(schema.GroupKind{
Group: appv1beta1.StripVersion(gk.Group),
Kind: gk.Kind,
})
if err != nil {
logger.Info("NoMappingForGK", "gk", gk.String())
continue
}
list := &unstructured.UnstructuredList{}
list.SetGroupVersionKind(mapping.GroupVersionKind)
if err = r.Client.List(ctx, list, client.InNamespace(namespace), client.MatchingLabels(selector.MatchLabels)); err != nil {
logger.Error(err, "unable to list resources for GVK", "gvk", mapping.GroupVersionKind)
*errs = append(*errs, err)
continue
}
for _, u := range list.Items {
resource := u
resources = append(resources, &resource)
}
}
return resources
}
func (r *ApplicationReconciler) setOwnerRefForResources(ctx context.Context, ownerRef metav1.OwnerReference, resources []*unstructured.Unstructured) error {
logger := getLoggerOrDie(ctx)
for _, resource := range resources {
ownerRefs := resource.GetOwnerReferences()
ownerRefFound := false
for i, refs := range ownerRefs {
if ownerRef.Kind == refs.Kind &&
ownerRef.APIVersion == refs.APIVersion &&
ownerRef.Name == refs.Name {
ownerRefFound = true
if ownerRef.UID != refs.UID {
ownerRefs[i] = ownerRef
}
}
}
if !ownerRefFound {
ownerRefs = append(ownerRefs, ownerRef)
}
resource.SetOwnerReferences(ownerRefs)
err := r.Client.Update(ctx, resource)
if err != nil {
// We log this error, but we continue and try to set the ownerRefs on the other resources.
logger.Error(err, "ErrorSettingOwnerRef", "gvk", resource.GroupVersionKind().String(),
"namespace", resource.GetNamespace(), "name", resource.GetName())
}
}
return nil
}
func (r *ApplicationReconciler) objectStatuses(ctx context.Context, resources []*unstructured.Unstructured, errs *[]error) []appv1beta1.ObjectStatus {
logger := getLoggerOrDie(ctx)
var objectStatuses []appv1beta1.ObjectStatus
for _, resource := range resources {
os := appv1beta1.ObjectStatus{
Group: resource.GroupVersionKind().Group,
Kind: resource.GetKind(),
Name: resource.GetName(),
Link: resource.GetSelfLink(),
}
s, err := status(resource)
if err != nil {
logger.Error(err, "unable to compute status for resource", "gvk", resource.GroupVersionKind().String(),
"namespace", resource.GetNamespace(), "name", resource.GetName())
*errs = append(*errs, err)
}
os.Status = s
objectStatuses = append(objectStatuses, os)
}
return objectStatuses
}
func aggregateReady(objectStatuses []appv1beta1.ObjectStatus) (bool, int) {
countReady := 0
for _, os := range objectStatuses {
if os.Status == StatusReady {
countReady++
}
}
if countReady == len(objectStatuses) {
return true, countReady
}
return false, countReady
}
func (r *ApplicationReconciler) updateApplicationStatus(ctx context.Context, nn types.NamespacedName, status *appv1beta1.ApplicationStatus) error {
if err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
original := &appv1beta1.Application{}
if err := r.Get(ctx, nn, original); err != nil {
return err
}
original.Status = *status
if err := r.Client.Status().Update(ctx, original); err != nil {
return err
}
return nil
}); err != nil {
return fmt.Errorf("failed to update status of Application %s/%s: %v", nn.Namespace, nn.Name, err)
}
return nil
}
func (r *ApplicationReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&appv1beta1.Application{}).
Complete(r)
}
func getLoggerOrDie(ctx context.Context) logr.Logger {
logger, ok := ctx.Value(loggerCtxKey).(logr.Logger)
if !ok {
panic("context didn't contain logger")
}
return logger
}
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package controllers
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
appv1beta1 "sigs.k8s.io/application/pkg/apis/app/v1beta1"
)
func setReadyCondition(appStatus *appv1beta1.ApplicationStatus, reason, message string) {
setCondition(appStatus, appv1beta1.Ready, corev1.ConditionTrue, reason, message)
}
// NotReady - shortcut to set ready condition to false
func setNotReadyCondition(appStatus *appv1beta1.ApplicationStatus, reason, message string) {
setCondition(appStatus, appv1beta1.Ready, corev1.ConditionFalse, reason, message)
}
// Unknown - shortcut to set ready condition to unknown
func setReadyUnknownCondition(appStatus *appv1beta1.ApplicationStatus, reason, message string) {
setCondition(appStatus, appv1beta1.Ready, corev1.ConditionUnknown, reason, message)
}
// setErrorCondition - shortcut to set error condition
func setErrorCondition(appStatus *appv1beta1.ApplicationStatus, reason, message string) {
setCondition(appStatus, appv1beta1.Error, corev1.ConditionTrue, reason, message)
}
// clearErrorCondition - shortcut to set error condition
func clearErrorCondition(appStatus *appv1beta1.ApplicationStatus) {
setCondition(appStatus, appv1beta1.Error, corev1.ConditionFalse, "NoError", "No error seen")
}
func setCondition(appStatus *appv1beta1.ApplicationStatus, ctype appv1beta1.ConditionType, status corev1.ConditionStatus, reason, message string) {
var c *appv1beta1.Condition
for i := range appStatus.Conditions {
if appStatus.Conditions[i].Type == ctype {
c = &appStatus.Conditions[i]
}
}
if c == nil {
addCondition(appStatus, ctype, status, reason, message)
} else {
// check message ?
if c.Status == status && c.Reason == reason && c.Message == message {
return
}
now := metav1.Now()
c.LastUpdateTime = now
if c.Status != status {
c.LastTransitionTime = now
}
c.Status = status
c.Reason = reason
c.Message = message
}
}
func addCondition(appStatus *appv1beta1.ApplicationStatus, ctype appv1beta1.ConditionType, status corev1.ConditionStatus, reason, message string) {
now := metav1.Now()
c := appv1beta1.Condition{
Type: ctype,
LastUpdateTime: now,
LastTransitionTime: now,
Status: status,
Reason: reason,
Message: message,
}
appStatus.Conditions = append(appStatus.Conditions, c)
}
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package controllers
import (
"strings"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)
// Constants defining labels
const (
StatusReady = "Ready"
StatusInProgress = "InProgress"
StatusUnknown = "Unknown"
StatusDisabled = "Disabled"
)
func status(u *unstructured.Unstructured) (string, error) {
gk := u.GroupVersionKind().GroupKind()
switch gk.String() {
case "StatefulSet.apps":
return stsStatus(u)
case "Deployment.apps":
return deploymentStatus(u)
case "ReplicaSet.apps":
return replicasetStatus(u)
case "DaemonSet.apps":
return daemonsetStatus(u)
case "PersistentVolumeClaim":
return pvcStatus(u)
case "Service":
return serviceStatus(u)
case "Pod":
return podStatus(u)
case "PodDisruptionBudget.policy":
return pdbStatus(u)
case "ReplicationController":
return replicationControllerStatus(u)
case "Job.batch":
return jobStatus(u)
default:
return statusFromStandardConditions(u)
}
}
// Status from standard conditions
func statusFromStandardConditions(u *unstructured.Unstructured) (string, error) {
condition := StatusReady
// Check Ready condition
_, cs, found, err := getConditionOfType(u, StatusReady)
if err != nil {
return StatusUnknown, err
}
if found && cs == corev1.ConditionFalse {
condition = StatusInProgress
}
// Check InProgress condition
_, cs, found, err = getConditionOfType(u, StatusInProgress)
if err != nil {
return StatusUnknown, err
}
if found && cs == corev1.ConditionTrue {
condition = StatusInProgress
}
return condition, nil
}
// Statefulset
func stsStatus(u *unstructured.Unstructured) (string, error) {
sts := &appsv1.StatefulSet{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, sts); err != nil {
return StatusUnknown, err
}
if sts.Status.ObservedGeneration == sts.Generation &&
sts.Status.Replicas == *sts.Spec.Replicas &&
sts.Status.ReadyReplicas == *sts.Spec.Replicas &&
sts.Status.CurrentReplicas == *sts.Spec.Replicas {
return StatusReady, nil
}
return StatusInProgress, nil
}
// Deployment
func deploymentStatus(u *unstructured.Unstructured) (string, error) {
deployment := &appsv1.Deployment{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, deployment); err != nil {
return StatusUnknown, err
}
replicaFailure := false
progressing := false
available := false
for _, condition := range deployment.Status.Conditions {
switch condition.Type {
case appsv1.DeploymentProgressing:
if condition.Status == corev1.ConditionTrue && condition.Reason == "NewReplicaSetAvailable" {
progressing = true
}
case appsv1.DeploymentAvailable:
if condition.Status == corev1.ConditionTrue {
available = true
}
case appsv1.DeploymentReplicaFailure:
if condition.Status == corev1.ConditionTrue {
replicaFailure = true
break
}
}
}
if deployment.Status.ObservedGeneration == deployment.Generation &&
deployment.Status.Replicas == *deployment.Spec.Replicas &&
deployment.Status.ReadyReplicas == *deployment.Spec.Replicas &&
deployment.Status.AvailableReplicas == *deployment.Spec.Replicas &&
deployment.Status.Conditions != nil && len(deployment.Status.Conditions) > 0 &&
(progressing || available) && !replicaFailure {
return StatusReady, nil
}
return StatusInProgress, nil
}
// Replicaset
func replicasetStatus(u *unstructured.Unstructured) (string, error) {
rs := &appsv1.ReplicaSet{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, rs); err != nil {
return StatusUnknown, err
}
replicaFailure := false
for _, condition := range rs.Status.Conditions {
switch condition.Type {
case appsv1.ReplicaSetReplicaFailure:
if condition.Status == corev1.ConditionTrue {
replicaFailure = true
break
}
}
}
if rs.Status.ObservedGeneration == rs.Generation &&
rs.Status.Replicas == *rs.Spec.Replicas &&
rs.Status.ReadyReplicas == *rs.Spec.Replicas &&
rs.Status.AvailableReplicas == *rs.Spec.Replicas && !replicaFailure {
return StatusReady, nil
}
return StatusInProgress, nil
}
// Daemonset
func daemonsetStatus(u *unstructured.Unstructured) (string, error) {
ds := &appsv1.DaemonSet{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, ds); err != nil {
return StatusUnknown, err
}
if ds.Status.ObservedGeneration == ds.Generation &&
ds.Status.DesiredNumberScheduled == ds.Status.NumberAvailable &&
ds.Status.DesiredNumberScheduled == ds.Status.NumberReady {
return StatusReady, nil
}
return StatusInProgress, nil
}
// PVC
func pvcStatus(u *unstructured.Unstructured) (string, error) {
pvc := &corev1.PersistentVolumeClaim{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, pvc); err != nil {
return StatusUnknown, err
}
if pvc.Status.Phase == corev1.ClaimBound {
return StatusReady, nil
}
return StatusInProgress, nil
}
// Service
func serviceStatus(u *unstructured.Unstructured) (string, error) {
service := &corev1.Service{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, service); err != nil {
return StatusUnknown, err
}
stype := service.Spec.Type
if stype == corev1.ServiceTypeClusterIP || stype == corev1.ServiceTypeNodePort || stype == corev1.ServiceTypeExternalName ||
stype == corev1.ServiceTypeLoadBalancer && isEmpty(service.Spec.ClusterIP) &&
len(service.Status.LoadBalancer.Ingress) > 0 && !hasEmptyIngressIP(service.Status.LoadBalancer.Ingress) {
return StatusReady, nil
}
return StatusInProgress, nil
}
// Pod
func podStatus(u *unstructured.Unstructured) (string, error) {
pod := &corev1.Pod{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, pod); err != nil {
return StatusUnknown, err
}
for _, condition := range pod.Status.Conditions {
if condition.Type == corev1.PodReady && (condition.Reason == "PodCompleted" || condition.Status == corev1.ConditionTrue) {
return StatusReady, nil
}
}
return StatusInProgress, nil
}
// PodDisruptionBudget
func pdbStatus(u *unstructured.Unstructured) (string, error) {
pdb := &policyv1beta1.PodDisruptionBudget{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, pdb); err != nil {
return StatusUnknown, err
}
if pdb.Status.ObservedGeneration == pdb.Generation &&
pdb.Status.CurrentHealthy >= pdb.Status.DesiredHealthy {
return StatusReady, nil
}
return StatusInProgress, nil
}
func replicationControllerStatus(u *unstructured.Unstructured) (string, error) {
rc := &corev1.ReplicationController{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, rc); err != nil {
return StatusUnknown, err
}
if rc.Status.ObservedGeneration == rc.Generation &&
rc.Status.Replicas == *rc.Spec.Replicas &&
rc.Status.ReadyReplicas == *rc.Spec.Replicas &&
rc.Status.AvailableReplicas == *rc.Spec.Replicas {
return StatusReady, nil
}
return StatusInProgress, nil
}
func jobStatus(u *unstructured.Unstructured) (string, error) {
job := &batchv1.Job{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, job); err != nil {
return StatusUnknown, err
}
if job.Status.StartTime == nil {
return StatusInProgress, nil
}
return StatusReady, nil
}
func hasEmptyIngressIP(ingress []corev1.LoadBalancerIngress) bool {
for _, i := range ingress {
if isEmpty(i.IP) {
return true
}
}
return false
}
func isEmpty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
func getConditionOfType(u *unstructured.Unstructured, conditionType string) (string, corev1.ConditionStatus, bool, error) {
conditions, found, err := unstructured.NestedSlice(u.Object, "status", "conditions")
if err != nil || !found {
return "", corev1.ConditionFalse, false, err
}
for _, c := range conditions {
condition, ok := c.(map[string]interface{})
if !ok {
continue
}
t, found := condition["type"]
if !found {
continue
}
condType, ok := t.(string)
if !ok {
continue
}
if condType == conditionType {
reason := condition["reason"].(string)
conditionStatus := condition["status"].(string)
return reason, corev1.ConditionStatus(conditionStatus), true, nil
}
}
return "", corev1.ConditionFalse, false, nil
}
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1
import (
"regexp"
"strings"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
......@@ -89,7 +79,9 @@ type ApplicationSpec struct {
AddOwnerRef bool `json:"addOwnerRef,omitempty"`
// Info contains human readable key,value pairs for the Application.
Info []InfoItem `json:"info,omitempty"`
// +patchStrategy=merge
// +patchMergeKey=name
Info []InfoItem `json:"info,omitempty" patchStrategy:"merge" patchMergeKey:"name"`
// AssemblyPhase represents the current phase of the application's assembly.
// An empty value is equivalent to "Succeeded".
......@@ -97,14 +89,12 @@ type ApplicationSpec struct {
}
// ComponentList is a generic status holder for the top level resource
// +k8s:deepcopy-gen=true
type ComponentList struct {
// Object status array for all matching objects
Objects []ObjectStatus `json:"components,omitempty"`
}
// ObjectStatus is a generic status holder for objects
// +k8s:deepcopy-gen=true
type ObjectStatus struct {
// Link to object
Link string `json:"link,omitempty"`
......@@ -122,7 +112,6 @@ type ObjectStatus struct {
type ConditionType string
// Condition describes the state of an object at a certain point.
// +k8s:deepcopy-gen=true
type Condition struct {
// Type of condition.
Type ConditionType `json:"type" protobuf:"bytes,1,opt,name=type,casttype=StatefulSetConditionType"`
......@@ -156,6 +145,9 @@ type ApplicationStatus struct {
// Resources embeds a list of object statuses
// +optional
ComponentList `json:",inline,omitempty"`
// ComponentsReady: status of the components in the format ready/total
// +optional
ComponentsReady string `json:"componentsReady,omitempty"`
}
// ImageSpec contains information about an image used as an icon.
......@@ -270,6 +262,8 @@ type ServiceSelector struct {
Port *int32 `json:"port,omitempty"`
// The optional HTTP path.
Path string `json:"path,omitempty"`
// Protocol for the service
Protocol string `json:"protocol,omitempty"`
}
// IngressSelector selects an Ingress.
......@@ -280,9 +274,11 @@ type IngressSelector struct {
Host string `json:"host,omitempty"`
// The optional HTTP path.
Path string `json:"path,omitempty"`
// Protocol for the ingress
Protocol string `json:"protocol,omitempty"`
}
// ApplicationAssemblyPhase tracks the Application CRD phases: pending, succeded, failed
// ApplicationAssemblyPhase tracks the Application CRD phases: pending, succeeded, failed
type ApplicationAssemblyPhase string
// Constants
......@@ -300,10 +296,16 @@ const (
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:object:root=true
// +kubebuilder:resource:categories=all,shortName=app
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Type",type=string,description="The type of the application",JSONPath=`.spec.descriptor.type`,priority=0
// +kubebuilder:printcolumn:name="Version",type=string,description="The creation date",JSONPath=`.spec.descriptor.version`,priority=0
// +kubebuilder:printcolumn:name="Owner",type=boolean,description="The application object owns the matched resources",JSONPath=`.spec.addOwnerRef`,priority=0
// +kubebuilder:printcolumn:name="Ready",type=string,description="Numbers of components ready",JSONPath=`.status.componentsReady`,priority=0
// +kubebuilder:printcolumn:name="Age",type=date,description="The creation date",JSONPath=`.metadata.creationTimestamp`,priority=0
// Application is the Schema for the applications API
// +k8s:openapi-gen=true
type Application struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
......@@ -312,7 +314,7 @@ type Application struct {
Status ApplicationStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:object:root=true
// ApplicationList contains a list of Application
type ApplicationList struct {
......@@ -324,3 +326,18 @@ type ApplicationList struct {
func init() {
SchemeBuilder.Register(&Application{}, &ApplicationList{})
}
// StripVersion the version part of gv
func StripVersion(gv string) string {
if gv == "" {
return gv
}
re := regexp.MustCompile(`^[vV][0-9].*`)
// If it begins with only version, (group is nil), return empty string which maps to core group
if re.MatchString(gv) {
return ""
}
return strings.Split(gv, "/")[0]
}
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package v1beta1 contains API Schema definitions for the app v1beta1 API group
// +kubebuilder:object:generate=true
// +groupName=app.k8s.io
package v1beta1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "app.k8s.io", Version: "v1beta1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
// GroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: "app.k8s.io", Version: "v1beta1"}
)
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
\ No newline at end of file
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -16,12 +16,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
// Code generated by controller-gen. DO NOT EDIT.
package v1beta1
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
......@@ -32,7 +32,6 @@ func (in *Application) DeepCopyInto(out *Application) {
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 Application.
......@@ -65,7 +64,6 @@ func (in *ApplicationList) DeepCopyInto(out *ApplicationList) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationList.
......@@ -107,7 +105,6 @@ func (in *ApplicationSpec) DeepCopyInto(out *ApplicationSpec) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationSpec.
......@@ -131,7 +128,6 @@ func (in *ApplicationStatus) DeepCopyInto(out *ApplicationStatus) {
}
}
in.ComponentList.DeepCopyInto(&out.ComponentList)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationStatus.
......@@ -152,7 +148,6 @@ func (in *ComponentList) DeepCopyInto(out *ComponentList) {
*out = make([]ObjectStatus, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentList.
......@@ -170,7 +165,6 @@ func (in *Condition) DeepCopyInto(out *Condition) {
*out = *in
in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime)
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition.
......@@ -187,7 +181,6 @@ func (in *Condition) DeepCopy() *Condition {
func (in *ConfigMapKeySelector) DeepCopyInto(out *ConfigMapKeySelector) {
*out = *in
out.ObjectReference = in.ObjectReference
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapKeySelector.
......@@ -203,7 +196,6 @@ func (in *ConfigMapKeySelector) DeepCopy() *ConfigMapKeySelector {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContactData) DeepCopyInto(out *ContactData) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContactData.
......@@ -244,7 +236,6 @@ func (in *Descriptor) DeepCopyInto(out *Descriptor) {
*out = make([]Link, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Descriptor.
......@@ -260,7 +251,6 @@ func (in *Descriptor) DeepCopy() *Descriptor {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ImageSpec) DeepCopyInto(out *ImageSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageSpec.
......@@ -281,7 +271,6 @@ func (in *InfoItem) DeepCopyInto(out *InfoItem) {
*out = new(InfoItemSource)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfoItem.
......@@ -317,7 +306,6 @@ func (in *InfoItemSource) DeepCopyInto(out *InfoItemSource) {
*out = new(IngressSelector)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfoItemSource.
......@@ -334,7 +322,6 @@ func (in *InfoItemSource) DeepCopy() *InfoItemSource {
func (in *IngressSelector) DeepCopyInto(out *IngressSelector) {
*out = *in
out.ObjectReference = in.ObjectReference
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressSelector.
......@@ -350,7 +337,6 @@ func (in *IngressSelector) DeepCopy() *IngressSelector {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Link) DeepCopyInto(out *Link) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Link.
......@@ -366,7 +352,6 @@ func (in *Link) DeepCopy() *Link {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ObjectStatus) DeepCopyInto(out *ObjectStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStatus.
......@@ -383,7 +368,6 @@ func (in *ObjectStatus) DeepCopy() *ObjectStatus {
func (in *SecretKeySelector) DeepCopyInto(out *SecretKeySelector) {
*out = *in
out.ObjectReference = in.ObjectReference
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretKeySelector.
......@@ -405,7 +389,6 @@ func (in *ServiceSelector) DeepCopyInto(out *ServiceSelector) {
*out = new(int32)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceSelector.
......
......@@ -14,17 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
package versioned
import (
"fmt"
appv1beta1 "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1"
discovery "k8s.io/client-go/discovery"
rest "k8s.io/client-go/rest"
flowcontrol "k8s.io/client-go/util/flowcontrol"
appv1beta1 "sigs.k8s.io/application/pkg/client/clientset/versioned/typed/app/v1beta1"
)
type Interface interface {
......
......@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated clientset.
package versioned
......@@ -14,19 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
clientset "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned"
appv1beta1 "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1"
fakeappv1beta1 "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1/fake"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
fakediscovery "k8s.io/client-go/discovery/fake"
"k8s.io/client-go/testing"
clientset "sigs.k8s.io/application/pkg/client/clientset/versioned"
appv1beta1 "sigs.k8s.io/application/pkg/client/clientset/versioned/typed/app/v1beta1"
fakeappv1beta1 "sigs.k8s.io/application/pkg/client/clientset/versioned/typed/app/v1beta1/fake"
)
// NewSimpleClientset returns a clientset that will respond with the provided objects.
......
......@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated fake clientset.
package fake
......@@ -14,17 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
appv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
appv1beta1 "sigs.k8s.io/application/pkg/apis/app/v1beta1"
)
var scheme = runtime.NewScheme()
......
......@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
// This package contains the scheme of the automatically generated clientset.
package scheme
......@@ -14,17 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
package scheme
import (
appv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
appv1beta1 "sigs.k8s.io/application/pkg/apis/app/v1beta1"
)
var Scheme = runtime.NewScheme()
......
......@@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
package v1beta1
import (
v1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
"github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/scheme"
rest "k8s.io/client-go/rest"
v1beta1 "sigs.k8s.io/application/pkg/apis/app/v1beta1"
"sigs.k8s.io/application/pkg/client/clientset/versioned/scheme"
)
type AppV1beta1Interface interface {
......@@ -29,7 +29,7 @@ type AppV1beta1Interface interface {
ApplicationsGetter
}
// AppV1beta1Client is used to interact with features provided by the app.k8s.io group.
// AppV1beta1Client is used to interact with features provided by the app group.
type AppV1beta1Client struct {
restClient rest.Interface
}
......
......@@ -14,19 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
package v1beta1
import (
"time"
v1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
scheme "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/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"
v1beta1 "sigs.k8s.io/application/pkg/apis/app/v1beta1"
scheme "sigs.k8s.io/application/pkg/client/clientset/versioned/scheme"
)
// ApplicationsGetter has a method to return a ApplicationInterface.
......
......@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated typed clients.
package v1beta1
......@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
// Package fake has the automatically generated clients.
package fake
......@@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1beta1 "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1"
rest "k8s.io/client-go/rest"
testing "k8s.io/client-go/testing"
v1beta1 "sigs.k8s.io/application/pkg/client/clientset/versioned/typed/app/v1beta1"
)
type FakeAppV1beta1 struct {
......
......@@ -14,18 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
v1beta1 "sigs.k8s.io/application/pkg/apis/app/v1beta1"
)
// FakeApplications implements ApplicationInterface
......@@ -34,9 +34,9 @@ type FakeApplications struct {
ns string
}
var applicationsResource = schema.GroupVersionResource{Group: "app.k8s.io", Version: "v1beta1", Resource: "applications"}
var applicationsResource = schema.GroupVersionResource{Group: "app", Version: "v1beta1", Resource: "applications"}
var applicationsKind = schema.GroupVersionKind{Group: "app.k8s.io", Version: "v1beta1", Kind: "Application"}
var applicationsKind = schema.GroupVersionKind{Group: "app", Version: "v1beta1", Kind: "Application"}
// Get takes name of the application, and returns the corresponding application object, and an error if there is any.
func (c *FakeApplications) Get(name string, options v1.GetOptions) (result *v1beta1.Application, err error) {
......
......@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by client-gen. DO NOT EDIT.
package v1beta1
......
......@@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by informer-gen. DO NOT EDIT.
package app
import (
v1beta1 "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions/app/v1beta1"
internalinterfaces "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions/internalinterfaces"
v1beta1 "sigs.k8s.io/application/pkg/client/informers/externalversions/app/v1beta1"
internalinterfaces "sigs.k8s.io/application/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to each of this group's versions.
......
......@@ -14,21 +14,21 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by informer-gen. DO NOT EDIT.
package v1beta1
import (
time "time"
appv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
versioned "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned"
internalinterfaces "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions/internalinterfaces"
v1beta1 "github.com/kubernetes-sigs/application/pkg/client/listers/app/v1beta1"
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"
appv1beta1 "sigs.k8s.io/application/pkg/apis/app/v1beta1"
versioned "sigs.k8s.io/application/pkg/client/clientset/versioned"
internalinterfaces "sigs.k8s.io/application/pkg/client/informers/externalversions/internalinterfaces"
v1beta1 "sigs.k8s.io/application/pkg/client/listers/app/v1beta1"
)
// ApplicationInformer provides access to a shared informer and lister for
......
......@@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by informer-gen. DO NOT EDIT.
package v1beta1
import (
internalinterfaces "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions/internalinterfaces"
internalinterfaces "sigs.k8s.io/application/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to all the informers in this group version.
......
......@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by informer-gen. DO NOT EDIT.
package externalversions
......@@ -23,13 +23,13 @@ import (
sync "sync"
time "time"
versioned "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned"
app "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions/app"
internalinterfaces "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions/internalinterfaces"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
versioned "sigs.k8s.io/application/pkg/client/clientset/versioned"
app "sigs.k8s.io/application/pkg/client/informers/externalversions/app"
internalinterfaces "sigs.k8s.io/application/pkg/client/informers/externalversions/internalinterfaces"
)
// SharedInformerOption defines the functional option type for SharedInformerFactory.
......
......@@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by informer-gen. DO NOT EDIT.
package externalversions
import (
"fmt"
v1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
v1beta1 "sigs.k8s.io/application/pkg/apis/app/v1beta1"
)
// GenericInformer is type of SharedIndexInformer which will locate and delegate to other
......@@ -52,7 +52,7 @@ func (f *genericInformer) Lister() cache.GenericLister {
// TODO extend this to unknown resources with a client pool
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
switch resource {
// Group=app.k8s.io, Version=v1beta1
// Group=app, Version=v1beta1
case v1beta1.SchemeGroupVersion.WithResource("applications"):
return &genericInformer{resource: resource.GroupResource(), informer: f.App().V1beta1().Applications().Informer()}, nil
......
......@@ -14,17 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by informer-gen. DO NOT EDIT.
package internalinterfaces
import (
time "time"
versioned "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
cache "k8s.io/client-go/tools/cache"
versioned "sigs.k8s.io/application/pkg/client/clientset/versioned"
)
// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer.
......
......@@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by lister-gen. DO NOT EDIT.
package v1beta1
import (
v1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
v1beta1 "sigs.k8s.io/application/pkg/apis/app/v1beta1"
)
// ApplicationLister helps list Applications.
......
......@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by main. DO NOT EDIT.
// Code generated by lister-gen. DO NOT EDIT.
package v1beta1
......
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# editor and IDE paraphernalia
.idea
*.swp
*.swo
*~
# Vscode files
.vscode
linters-settings:
lll:
line-length: 170
dupl:
threshold: 400
# Contributing guidelines
## Sign the CLA
Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests.
Please see https://git.k8s.io/community/CLA.md for more info
## Contributing steps
1. Submit an issue describing your proposed change to the repo in question.
1. The [repo owners](OWNERS) will respond to your issue promptly.
1. If your proposed change is accepted, and you haven't already done so, sign a Contributor License Agreement (see details above).
1. Fork the desired repo, develop and test your code changes.
1. Submit a pull request.
## Test locally
1. Setup tools
```bash
$ curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.17.1
```
1. Test
```bash
GO111MODULE=on TRACE=1 ./hack/check-everything.sh
```
# FAQ
### Q: How do I know which type of object a controller references?
**A**: Each controller should only reconcile one object type. Other
affected objects should be mapped to a single type of root object, using
the `EnqueueRequestForOwner` or `EnqueueRequestsFromMapFunc` event
handlers, and potentially indices. Then, your Reconcile method should
attempt to reconcile *all* state for that given root objects.
### Q: How do I have different logic in my reconciler for different types of events (e.g. create, update, delete)?
**A**: You should not. Reconcile functions should be idempotent, and
should always reconcile state by reading all the state it needs, then
writing updates. This allows your reconciler to correctly respond to
generic events, adjust to skipped or coalesced events, and easily deal
with application startup. The controller will enqueue reconcile requests
for both old and new objects if a mapping changes, but it's your
responsibility to make sure you have enough information to be able clean
up state that's no longer referenced.
### Q: My cache might be stale if I read from a cache! How should I deal with that?
**A**: There are several different approaches that can be taken, depending
on your situation.
- When you can, take advantage of optimistic locking: use deterministic
names for objects you create, so that the Kubernetes API server will
warn you if the object already exists. Many controllers in Kubernetes
take this approach: the StatefulSet controller appends a specific number
to each pod that it creates, while the Deployment controller hashes the
pod template spec and appends that.
- In the few cases when you cannot take advantage of deterministic names
(e.g. when using generateName), it may be useful in to track which
actions you took, and assume that they need to be repeated if they don't
occur after a given time (e.g. using a requeue result). This is what
the ReplicaSet controller does.
In general, write your controller with the assumption that information
will eventually be correct, but may be slightly out of date. Make sure
that your reconcile function enforces the entire state of the world each
time it runs. If none of this works for you, you can always construct
a client that reads directly from the API server, but this is generally
considered to be a last resort, and the two approaches above should
generally cover most circumstances.
### Q: Where's the fake client? How do I use it?
**A**: The fake client
[exists](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client/fake),
but we generally recommend using
[envtest.Environment](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/envtest#Environment)
to test against a real API server. In our experience, tests using fake
clients gradually re-implement poorly-written impressions of a real API
server, which leads to hard-to-maintain, complex test code.
### Q: How should I write tests? Any suggestions for getting started?
- Use the aforementioned
[envtest.Environment](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/envtest#Environment)
to spin up a real API server instead of trying to mock one out.
- Structure your tests to check that the state of the world is as you
expect it, *not* that a particular set of API calls were made, when
working with Kubernetes APIs. This will allow you to more easily
refactor and improve the internals of your controllers without changing
your tests.
- Remember that any time you're interacting with the API server, changes
may have some delay between write time and reconcile time.
### Q: What are these errors about no Kind being registered for a type?
**A**: You're probably missing a fully-set-up Scheme. Schemes record the
mapping between Go types and group-version-kinds in Kubernetes. In
general, your application should have its own Scheme containing the types
from the API groups that it needs (be they Kubernetes types or your own).
See the [scheme builder
docs](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/scheme) for
more information.
# See the OWNERS docs: https://git.k8s.io/community/contributors/devel/owners.md
owners:
- directxman12
- droot
- pwittrock
approvers:
- controller-runtime-admins
- controller-runtime-maintainers
reviewers:
- controller-runtime-admins
- controller-runtime-maintainers
# See the OWNERS docs: https://git.k8s.io/community/contributors/devel/owners.md
aliases:
controller-runtime-admins:
- directxman12
- droot
- pwittrock
controller-runtime-maintainers: []
[![Build Status](https://travis-ci.org/kubernetes-sigs/controller-runtime.svg?branch=master)](https://travis-ci.org/kubernetes-sigs/controller-runtime "Travis")
[![Go Report Card](https://goreportcard.com/badge/sigs.k8s.io/controller-runtime)](https://goreportcard.com/report/sigs.k8s.io/controller-runtime)
# Kubernetes controller-runtime Project
The Kubernetes controller-runtime Project is a set of go libraries for building Controllers.
Documentation:
- [Package overview](https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg)
- [Basic controller using builder](https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/builder#example-Builder)
- [Creating a manager](https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/manager#example-New)
- [Creating a controller](https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/controller#example-New)
- [Examples](https://github.com/kubernetes-sigs/controller-runtime/blob/master/examples)
# Versioning, Maintenance, and Compatibility
The full documentation can be found at [VERSIONING.md](VERSIONING.md), but TL;DR:
Users:
- We follow [Semantic Versioning (semver)](https://semver.org)
- Use releases with your dependency management to ensure that you get compatible code
- The master branch contains all the latest code, some of which may break compatibility (so "normal" `go get` is not recommended)
Contributors:
- All code PR must be labeled with :bug: (patch fixes), :sparkles: (backwards-compatible features), or :warning: (breaking changes)
- Breaking changes will find their way into the next major release, other changes will go into an semi-immediate patch or minor release
- For a quick PR template suggesting the right information, use one of these PR templates:
* [Breaking Changes/Features](/.github/PULL_REQUEST_TEMPLATE/breaking_change.md)
* [Backwards-Compatible Features](/.github/PULL_REQUEST_TEMPLATE/compat_feature.md)
* [Bug fixes](/.github/PULL_REQUEST_TEMPLATE/bug_fix.md)
* [Documentation Changes](/.github/PULL_REQUEST_TEMPLATE/docs.md)
* [Test/Build/Other Changes](/.github/PULL_REQUEST_TEMPLATE/other.md)
## FAQ
See [FAQ.md](FAQ.md)
## Community, discussion, contribution, and support
Learn how to engage with the Kubernetes community on the [community page](http://kubernetes.io/community/).
controller-runtime is a subproject of the [kubebuilder](https://github.com/kubernetes-sigs/kubebuilder) project
in sig apimachinery.
You can reach the maintainers of this project at:
- Slack channel: [#kubebuilder](http://slack.k8s.io/#kubebuilder)
- Google Group: [kubebuilder@googlegroups.com](https://groups.google.com/forum/#!forum/kubebuilder)
## Contributing
Contributions are greatly appreciated. The maintainers actively manage the issues list, and try to highlight issues suitable for newcomers.
The project follows the typical GitHub pull request model. See [CONTRIBUTING.md](CONTRIBUTING.md) for more details.
Before starting any work, please either comment on an existing issue, or file a new one.
## Code of conduct
Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).
# Defined below are the security contacts for this repo.
#
# They are the contact point for the Product Security Team to reach out
# to for triaging and handling of incoming issues.
#
# The below names agree to abide by the
# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy)
# and will be removed and replaced if they violate that agreement.
#
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
# INSTRUCTIONS AT https://kubernetes.io/security/
directxman12
pwittrock
droot
Logging Guidelines
==================
controller-runtime uses a kind of logging called *structured logging*. If
you've used a library like Zap or logrus before, you'll be familiar with
the concepts we use. If you've only used a logging library like the "log"
package (in the Go standard library) or "glog" (in Kubernetes), you'll
need to adjust how you think about logging a bit.
### Getting Started With Structured Logging
With structured logging, we associate a *constant* log message with some
variable key-value pairs. For instance, suppose we wanted to log that we
were starting reconciliation on a pod. In the Go standard libary logger,
we might write:
```go
log.Printf("starting reconciliation for pod %s/%s", podNamespace, podName)
```
In controller-runtime, we'd instead write:
```go
logger.Info("starting reconciliation", "pod", req.NamespacedNamed)
```
or even write
```go
func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Response, error) {
logger := logger.WithValues("pod", req.NamespacedName)
// do some stuff
logger.Info("starting reconciliation")
}
```
Notice how we've broken out the information that we want to convey into
a constant message (`"starting reconciliation"`) and some key-value pairs
that convey variable information (`"pod", req.NamespacedName`). We've
there-by added "structure" to our logs, which makes them easier to save
and search later, as well as correlate with metrics and events.
All of controller-runtime's logging is done via
[logr](https://github.com/go-logr/logr), a generic interface for
structured logging. You can use whichever logging library you want to
implement the actual mechanics of the logging. controller-runtime
provides some helpers to make it easy to use
[Zap](https://go.uber.org/zap) as the implementation.
You can configure the logging implementation using
`"sigs.k8s.io/controller-runtime/pkg/log".SetLogger`. That
package also contains the convenience functions for setting up Zap.
You can get a handle to the the "root" logger using
`"sigs.k8s.io/controller-runtime/pkg/log".Log`, and can then call
`WithName` to create individual named loggers. You can call `WithName`
repeatedly to chain names together:
```go
logger := log.Log.WithName("controller").WithName("replicaset")
// in reconcile...
logger = logger.WithValues("replicaset", req.NamespacedName)
// later on in reconcile...
logger.Info("doing things with pods", "pod", newPod)
```
As seen above, you can also call `WithValue` to create a new sub-logger
that always attaches some key-value pairs to a logger.
Finally, you can use `V(1)` to mark a particular log line as "debug" logs:
```go
logger.V(1).Info("this is particularly verbose!", "state of the world",
allKubernetesObjectsEverywhere)
```
While it's possible to use higher log levels, it's recommended that you
stick with `V(1)` or V(0)` (which is equivalent to not specifying `V`),
and then filter later based on key-value pairs or messages; different
numbers tend to lose meaning easily over time, and you'll be left
wondering why particular logs lines are at `V(5)` instead of `V(7)`.
## Logging errors
Errors should *always* be logged with `log.Error`, which allows logr
implementations to provide special handling of errors (for instance,
providing stack traces in debug mode).
It's acceptable to log call `log.Error` with a nil error object. This
conveys that an error occurred in some capacity, but that no actual
`error` object was involved.
## Logging messages
- Don't put variable content in your messages -- use key-value pairs for
that. Never use `fmt.Sprintf` in your message.
- Try to match the terminology in your messages with your key-value pairs
-- for instance, if you have a key-value pairs `api version`, use the
term `APIVersion` instead of `GroupVersion` in your message.
## Logging Kubernetes Objects
Kubernetes objects should be logged directly, like `log.Info("this is
a Kubernetes object", "pod", somePod)`. controller-runtime provides
a special encoder for Zap that will transform Kubernetes objects into
`name, namespace, apiVersion, kind` objects, when available and not in
development mode. Other logr implementations should implement similar
logic.
## Logging Structured Values (Key-Value pairs)
- Use lower-case, space separated keys. For example `object` for objects,
`api version` for `APIVersion`
- Be consistent across your application, and with controller-runtime when
possible.
- Try to be brief but descriptive.
- Match terminology in keys with terminology in the message.
- Be careful logging non-Kubernetes objects verbatim if they're very
large.
### Groups, Versions, and Kinds
- Kinds should not be logged alone (they're meaningless alone). Use
a `GroupKind` object to log them instead, or a `GroupVersionKind` when
version is relevant.
- If you need to log an API version string, use `api version` as the key
(formatted as with a `GroupVersion`, or as received directly from API
discovery).
### Objects and Types
- If code works with a generic Kubernetes `runtime.Object`, use the
`object` key. For specific objects, prefer the resource name as the key
(e.g. `pod` for `v1.Pod` objects).
- For non-Kubernetes objects, the `object` key may also be used, if you
accept a generic interface.
- When logging a raw type, log it using the `type` key, with a value of
`fmt.Sprintf("%T", typ)`
- If there's specific context around a type, the key may be more specific,
but should end with `type` -- for instance, `OwnerType` should be logged
as `owner` in the context of `log.Error(err, "Could not get ObjectKinds
for OwnerType", `owner type`, fmt.Sprintf("%T"))`. When possible, favor
communicating kind instead.
### Multiple things
- When logging multiple things, simply pluralize the key.
### controller-runtime Specifics
- Reconcile requests should be logged as `request`, although normal code
should favor logging the key.
- Reconcile keys should be logged as with the same key as if you were
logging the object directly (e.g. `log.Info("reconciling pod", "pod",
req.NamespacedName)`). This ends up having a similar effect to logging
the object directly.
# Versioning and Branching in controller-runtime
*NB*: this also applies to controller-tools.
## TL;DR:
### Users
- We follow [Semantic Versioning (semver)](https://semver.org)
- Use releases with your dependency management to ensure that you get
compatible code
- The master branch contains all the latest code, some of which may break
compatibility (so "normal" `go get` is not recommended)
### Contributors
- All code PR must be labeled with :bug: (patch fixes), :sparkles:
(backwards-compatible features), or :warning: (breaking changes)
- Breaking changes will find their way into the next major release, other
changes will go into an semi-immediate patch or minor release
- Please *try* to avoid breaking changes when you can. They make users
face difficult decisions ("when do I go through the pain of
upgrading?"), and make life hard for maintainers and contributors
(dealing with differences on stable branches).
### Mantainers
Don't be lazy, read the rest of this doc :-)
## Overview
controller-runtime (and friends) follow [Semantic
Versioning](https://semver.org). I'd recommend reading the aforementioned
link if you're not familiar, but essentially, for any given release X.Y.Z:
- an X (*major*) release indicates a set of backwards-compatible code.
Changing X means there's a breaking change.
- a Y (*minor*) release indicates a minimum feature set. Changing Y means
the addition of a backwards-compatible feature.
- a Z (*patch*) release indicates minimum set of bugfixes. Changing
Z means a backwards-compatible change that doesn't add functionality.
*NB*: If the major release is `0`, any minor release may contain breaking
changes.
These guarantees extend to all code exposed in public APIs of
controller-runtime. This includes code both in controller-runtime itself,
*plus types from dependencies in public APIs*. Types and functions not in
public APIs are not considered part of the guarantee.
In order to easily maintain the guarantees, we have a couple of processes
that we follow.
## Branches
controller-runtime contains two types of branches: the *master* branch and
*release-X* branches.
The *master* branch is where development happens. All the latest and
greatest code, including breaking changes, happens on master.
The *release-X* branches contain stable, backwards compatible code. Every
major (X) release, a new such branch is created. It is from these
branches that minor and patch releases are tagged. If some cases, it may
be necessary open PRs for bugfixes directly against stable branches, but
this should generally not be the case.
The maintainers are responsible for updating the contents of this branch;
generally, this is done just before a release using release tooling that
filters and checks for changes tagged as breaking (see below).
### Tooling
* [release-notes.sh](hack/release/release-notes.sh): generate release notes
for a range of commits, and check for next version type (***TODO***)
* [verify-emoji.sh](hack/release/verify-emoji.sh): check that
your PR and/or commit messages have the right versioning icon
(***TODO***).
## PR Process
Every PR should be annotated with an icon indicating whether it's
a:
- Breaking change: :warning: (`:warning:`)
- Non-breaking feature: :sparkles: (`:sparkles:`)
- Patch fix: :bug: (`:bug:`)
- Docs: :book: (`:book:`)
- Infra/Tests/Other: :running: (`:running:`)
- No release note: :ghost: (`:ghost:`)
Use :ghost: (no release note) only for the PRs that change or revert unreleased
changes, which don't deserve a release note. Please don't abuse it.
You can also use the equivalent emoji directly, since GitHub doesn't
render the `:xyz:` aliases in PR titles.
Individual commits should not be tagged separately, but will generally be
assumed to match the PR. For instance, if you have a bugfix in with
a breaking change, it's generally encouraged to submit the bugfix
separately, but if you must put them in one PR, mark the commit
separately.
### Commands and Workflow
controller-runtime follows the standard Kubernetes workflow: any PR needs
`lgtm` and `approved` labels, PRs authors must have signed the CNCF CLA,
and PRs must pass the tests before being merged. See [the contributor
docs](https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#the-testing-and-merge-workflow)
for more info.
We use the same priority and kind labels as Kubernetes. See the labels
tab in GitHub for the full list.
The standard Kubernetes comment commands should work in
controller-runtime. See [Prow](https://prow.k8s.io/command-help) for
a command reference.
## Release Process
Minor and patch releases are generally done immediately after a feature or
bugfix is landed, or sometimes a series of features tied together.
Minor releases will only be tagged on the *most recent* major release
branch, except in exceptional circumstances. Patches will be backported
to maintained stable versions, as needed.
Major releases are done shortly after a breaking change is merged -- once
a breaking change is merged, the next release *must* be a major revision.
We don't intend to have a lot of these, so we may put off merging breaking
PRs until a later date.
### Exact Steps
Follow the release-specific steps below, then follow the general steps
after that.
#### Minor and patch releases
1. Update the release-X branch with the latest set of changes by calling
`git rebase master` from the release branch.
#### Major releases
1. Create a new release branch named `release-X` (where `X` is the new
version) off of master.
#### General
2. Generate release notes using the release note tooling.
3. Add a release for controller-runtime on GitHub, using those release
notes, with a title of `vX.Y.Z`.
4. Do a similar process for
[controller-tools](https://github.com/kubernetes-sigs/controller-tools)
5. Announce the release in `#kubebuilder` on Slack with a pinned message.
6. Potentially update
[kubebuilder](https://github.com/kubernetes-sigs/kubebuilder) as well.
### Breaking Changes
Try to avoid breaking changes. They make life difficult for users, who
have to rewrite their code when they eventually upgrade, and for
maintainers/contributors, who have to deal with differences between master
and stable branches.
That being said, we'll occasionally want to make breaking changes. They'll
be merged onto master, and will then trigger a major release (see [Release
Process](#release-process)). Because breaking changes induce a major
revision, the maintainers may delay a particular breaking change until
a later date when they are ready to make a major revision with a few
breaking changes.
If you're going to make a breaking change, please make sure to explain in
detail why it's helpful. Is it necessary to cleanly resolve an issue?
Does it improve API ergonomics?
Maintainers should treat breaking changes with caution, and evaluate
potential non-breaking solutions (see below).
Note that API breakage in public APIs due to dependencies will trigger
a major revision, so you may occasionally need to have a major release
anyway, due to changes in libraries like `k8s.io/client-go` or
`k8s.io/apimachinery`.
*NB*: Pre-1.0 releases treat breaking changes a bit more lightly. We'll
still consider carefully, but the pre-1.0 timeframe is useful for
converging on a ergonomic API.
#### Avoiding breaking changes
##### Solutions to avoid
- **Confusingly duplicate methods, functions, or variables.**
For instance, suppose we have an interface method `List(ctx
context.Context, options *ListOptions, obj runtime.Object) error`, and
we decide to switch it so that options come at the end, parametrically.
Adding a new interface method `ListParametric(ctx context.Context, obj
runtime.Object, options... ListOption)` is probably not the right
solution:
- Users will intuitively see `List`, and use that in new projects, even
if it's marked as deprecated.
- Users who don't notice the deprecation may be confused as to the
difference between `List` and `ListParametric`.
- It's not immediately obvious in isolation (e.g. in surrounding code)
why the method is called `ListParametric`, and may cause confusion
when reading code that makes use of that method.
In this case, it may be better to make the breaking change, and then
eventually do a major release.
## Why don't we...
### Use "next"-style branches
Development branches:
- don't win us much in terms of maintenance in the case of breaking
changes (we still have to merge/manage multiple branches for development
and stable)
- can be confusing to contributors, who often expect master to have the
latest changes.
### Never break compatibility
Never doing a new major release could be an admirable goal, but gradually
leads to API cruft.
Since one of the goals of controller-runtime is to be a friendly and
intuitive API, we want to avoid too much API cruft over time, and
occaisonal breaking changes in major releases help accomplish that goal.
Furthermore, our dependency on Kubernetes libraries makes this difficult
(see below)
### Always assume we've broken compatibility
*a.k.a. k8s.io/client-go style*
While this makes life easier (a bit) for maintainers, it's problematic for
users. While breaking changes arrive sooner, upgrading becomes very
painful.
Furthermore, we still have to maintain stable branches for bugfixes, so
the maintenance burden isn't lessened by a ton.
### Extend compatibility guarantees to all dependencies
This is very difficult with the number of Kubernetes dependencies we have.
Kubernetes dependencies tend to either break compatibility every major
release (e.g. k8s.io/client-go, which loosely follows semver), or at
a whim (many other Kubernetes libraries).
If we limit to the few objects we expose, we can better inform users about
how *controller-runtime itself* has changed in a given release. Then,
users can make informed decisions about how to proceed with any direct
uses of Kubernetes dependencies their controller-runtime-based application
may have.
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllerruntime
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
// Builder builds an Application ControllerManagedBy (e.g. Operator) and returns a manager.Manager to start it.
type Builder = builder.Builder
// Request contains the information necessary to reconcile a Kubernetes object. This includes the
// information to uniquely identify the object - its Name and Namespace. It does NOT contain information about
// any specific Event or the object contents itself.
type Request = reconcile.Request
// Result contains the result of a Reconciler invocation.
type Result = reconcile.Result
// Manager initializes shared dependencies such as Caches and Clients, and provides them to Runnables.
// A Manager is required to create Controllers.
type Manager = manager.Manager
// Options are the arguments for creating a new Manager
type Options = manager.Options
// SchemeBuilder builds a new Scheme for mapping go types to Kubernetes GroupVersionKinds.
type SchemeBuilder = scheme.Builder
// GroupVersion contains the "group" and the "version", which uniquely identifies the API.
type GroupVersion = schema.GroupVersion
// GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying
// concepts during lookup stages without having partially valid types
type GroupResource = schema.GroupResource
// TypeMeta describes an individual object in an API response or request
// with strings representing the type of the object and its API schema version.
// Structures that are versioned or persisted should inline TypeMeta.
//
// +k8s:deepcopy-gen=false
type TypeMeta = metav1.TypeMeta
// ObjectMeta is metadata that all persisted resources must have, which includes all objects
// users must create.
type ObjectMeta = metav1.ObjectMeta
var (
// GetConfigOrDie creates a *rest.Config for talking to a Kubernetes apiserver.
// If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
// in cluster and use the cluster provided kubeconfig.
//
// Will log an error and exit if there is an error creating the rest.Config.
GetConfigOrDie = config.GetConfigOrDie
// GetConfig creates a *rest.Config for talking to a Kubernetes apiserver.
// If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
// in cluster and use the cluster provided kubeconfig.
//
// Config precedence
//
// * --kubeconfig flag pointing at a file
//
// * KUBECONFIG environment variable pointing at a file
//
// * In-cluster config if running in cluster
//
// * $HOME/.kube/config if exists
GetConfig = config.GetConfig
// NewControllerManagedBy returns a new controller builder that will be started by the provided Manager
NewControllerManagedBy = builder.ControllerManagedBy
// NewWebhookManagedBy returns a new webhook builder that will be started by the provided Manager
NewWebhookManagedBy = builder.WebhookManagedBy
// NewManager returns a new Manager for creating Controllers.
NewManager = manager.New
// CreateOrUpdate creates or updates the given object obj in the Kubernetes
// cluster. The object's desired state should be reconciled with the existing
// state using the passed in ReconcileFn. obj must be a struct pointer so that
// obj can be updated with the content returned by the Server.
//
// It returns the executed operation and an error.
CreateOrUpdate = controllerutil.CreateOrUpdate
// SetControllerReference sets owner as a Controller OwnerReference on owned.
// This is used for garbage collection of the owned object and for
// reconciling the owner object on changes to owned (with a Watch + EnqueueRequestForOwner).
// Since only one OwnerReference can be a controller, it returns an error if
// there is another OwnerReference with Controller flag set.
SetControllerReference = controllerutil.SetControllerReference
// SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned
// which is closed on one of these signals. If a second signal is caught, the program
// is terminated with exit code 1.
SetupSignalHandler = signals.SetupSignalHandler
// Log is the base logger used by controller-runtime. It delegates
// to another logr.Logger. You *must* call SetLogger to
// get any actual logging.
Log = log.Log
// SetLogger sets a concrete logging implementation for all deferred Loggers.
SetLogger = log.SetLogger
)
# Kubernetes Community Code of Conduct
Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package controllerruntime provides tools to construct Kubernetes-style
// controllers that manipulate both Kubernetes CRDs and aggregated/built-in
// Kubernetes APIs.
//
// It defines easy helpers for the common use cases when building CRDs, built
// on top of customizable layers of abstraction. Common cases should be easy,
// and uncommon cases should be possible. In general, controller-runtime tries
// to guide users towards Kubernetes controller best-practices.
//
// Getting Started
//
// The main entrypoint for controller-runtime is this root package, which
// contains all of the common types needed to get started building controllers:
// import (
// controllers "sigs.k8s.io/controller-runtime"
// )
//
// The examples in this package walk through a basic controller setup. The
// kubebuilder book (https://book.kubebuilder.io) has some more in-depth
// walkthroughs.
//
// controller-runtime favors structs with sane defaults over constructors, so
// it's fairly common to see structs being used directly in controller-runtime.
//
// Organization
//
// A brief-ish walkthrough of the layout of this library can be found below. Each
// package contains more information about how to use it.
//
// Frequently asked questions about using controller-runtime and designing
// controllers can be found at
// https://github.com/kubernetes-sigs/controller-runtime/blob/master/FAQ.md.
//
// Managers
//
// Every controller and webhook is ultimately run by a Manager (pkg/manager). A
// manager is responsible for running controllers and webhooks, and setting up
// common dependencies (pkg/runtime/inject), like shared caches and clients, as
// well as managing leader election (pkg/leaderelection). Managers are
// generally configured to gracefully shut down controllers on pod termination
// by wiring up a signal handler (pkg/manager/signals).
//
// Controllers
//
// Controllers (pkg/controller) use events (pkg/events) to eventually trigger
// reconcile requests. They may be constructed manually, but are often
// constructed with a Builder (pkg/builder), which eases the wiring of event
// sources (pkg/source), like Kubernetes API object changes, to event handlers
// (pkg/handler), like "enqueue a reconcile request for the object owner".
// Predicates (pkg/predicate) can be used to filter which events actually
// trigger reconciles. There are pre-written utilities for the common cases, and
// interfaces and helpers for advanced cases.
//
// Reconcilers
//
// Controller logic is implemented in terms of Reconcilers (pkg/reconcile). A
// Reconciler implements a function which takes a reconcile Request containing
// the name and namespace of the object to reconcile, reconciles the object,
// and returns a Response or an error indicating whether to requeue for a
// second round of processing.
//
// Clients and Caches
//
// Reconcilers use Clients (pkg/client) to access API objects. The default
// client provided by the manager reads from a local shared cache (pkg/cache)
// and writes directly to the API server, but clients can be constructed that
// only talk to the API server, without a cache. The Cache will auto-populate
// with watched objects, as well as when other structured objects are
// requested. Caches may also have indexes, which can be created via a
// FieldIndexer (pkg/client) obtained from the manager. Indexes can used to
// quickly and easily look up all objects with certain fields set. Reconcilers
// may retrieve event recorders (pkg/recorder) to emit events using the
// manager.
//
// Schemes
//
// Clients, Caches, and many other things in Kubernetes use Schemes
// (pkg/scheme) to associate Go types to Kubernetes API Kinds
// (Group-Version-Kinds, to be specific).
//
// Webhooks
//
// Similarly, webhooks (pkg/webhook/admission) may be implemented directly, but
// are often constructed using a builder (pkg/webhook/admission/builder). They
// are run via a server (pkg/webhook) which is managed by a Manager.
//
// Logging and Metrics
//
// Logging (pkg/log) in controller-runtime is done via structured logs, using a
// log set of interfaces called logr
// (https://godoc.org/github.com/go-logr/logr). While controller-runtime
// provides easy setup for using Zap (https://go.uber.org/zap, pkg/log/zap),
// you can provide any implementation of logr as the base logger for
// controller-runtime.
//
// Metrics (pkg/metrics) provided by controller-runtime are registered into a
// controller-runtime-specific Prometheus metrics registry. The manager can
// serve these by an HTTP endpoint, and additional metrics may be registered to
// this Registry as normal.
//
// Testing
//
// You can easily build integration and unit tests for your controllers and
// webhooks using the test Environment (pkg/envtest). This will automatically
// stand up a copy of etcd and kube-apiserver, and provide the correct options
// to connect to the API server. It's designed to work well with the Ginkgo
// testing framework, but should work with any testing setup.
package controllerruntime
module sigs.k8s.io/controller-runtime
go 1.13
require (
github.com/evanphx/json-patch v4.5.0+incompatible
github.com/go-logr/logr v0.1.0
github.com/go-logr/zapr v0.1.0
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 // indirect
github.com/googleapis/gnostic v0.3.1 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/onsi/ginkgo v1.8.0
github.com/onsi/gomega v1.5.0
github.com/prometheus/client_golang v0.9.2
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910
go.uber.org/atomic v1.3.2 // indirect
go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.9.1
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7
gomodules.xyz/jsonpatch/v2 v2.0.1
gopkg.in/fsnotify.v1 v1.4.7
gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/api v0.0.0-20190918155943-95b840bb6a1f
k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90
k8s.io/utils v0.0.0-20190801114015-581e00157fb1
sigs.k8s.io/testing_frameworks v0.1.2
sigs.k8s.io/yaml v1.1.0
)
此差异已折叠。
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
"fmt"
"strings"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
)
// Supporting mocking out functions for testing
var newController = controller.New
var getGvk = apiutil.GVKForObject
// Builder builds a Controller.
type Builder struct {
apiType runtime.Object
mgr manager.Manager
predicates []predicate.Predicate
managedObjects []runtime.Object
watchRequest []watchRequest
config *rest.Config
ctrl controller.Controller
ctrlOptions controller.Options
name string
}
// ControllerManagedBy returns a new controller builder that will be started by the provided Manager
func ControllerManagedBy(m manager.Manager) *Builder {
return &Builder{mgr: m}
}
// ForType defines the type of Object being *reconciled*, and configures the ControllerManagedBy to respond to create / delete /
// update events by *reconciling the object*.
// This is the equivalent of calling
// Watches(&source.Kind{Type: apiType}, &handler.EnqueueRequestForObject{})
//
// Deprecated: Use For
func (blder *Builder) ForType(apiType runtime.Object) *Builder {
return blder.For(apiType)
}
// For defines the type of Object being *reconciled*, and configures the ControllerManagedBy to respond to create / delete /
// update events by *reconciling the object*.
// This is the equivalent of calling
// Watches(&source.Kind{Type: apiType}, &handler.EnqueueRequestForObject{})
func (blder *Builder) For(apiType runtime.Object) *Builder {
blder.apiType = apiType
return blder
}
// Owns defines types of Objects being *generated* by the ControllerManagedBy, and configures the ControllerManagedBy to respond to
// create / delete / update events by *reconciling the owner object*. This is the equivalent of calling
// Watches(&handler.EnqueueRequestForOwner{&source.Kind{Type: <ForType-apiType>}, &handler.EnqueueRequestForOwner{OwnerType: apiType, IsController: true})
func (blder *Builder) Owns(apiType runtime.Object) *Builder {
blder.managedObjects = append(blder.managedObjects, apiType)
return blder
}
type watchRequest struct {
src source.Source
eventhandler handler.EventHandler
}
// Watches exposes the lower-level ControllerManagedBy Watches functions through the builder. Consider using
// Owns or For instead of Watches directly.
func (blder *Builder) Watches(src source.Source, eventhandler handler.EventHandler) *Builder {
blder.watchRequest = append(blder.watchRequest, watchRequest{src: src, eventhandler: eventhandler})
return blder
}
// WithConfig sets the Config to use for configuring clients. Defaults to the in-cluster config or to ~/.kube/config.
//
// Deprecated: Use ControllerManagedBy(Manager) and this isn't needed.
func (blder *Builder) WithConfig(config *rest.Config) *Builder {
blder.config = config
return blder
}
// WithEventFilter sets the event filters, to filter which create/update/delete/generic events eventually
// trigger reconciliations. For example, filtering on whether the resource version has changed.
// Defaults to the empty list.
func (blder *Builder) WithEventFilter(p predicate.Predicate) *Builder {
blder.predicates = append(blder.predicates, p)
return blder
}
// WithOptions overrides the controller options use in doController. Defaults to empty.
func (blder *Builder) WithOptions(options controller.Options) *Builder {
blder.ctrlOptions = options
return blder
}
// Named sets the name of the controller to the given name. The name shows up
// in metrics, among other things, and thus should be a prometheus compatible name
// (underscores and alphanumeric characters only).
//
// By default, controllers are named using the lowercase version of their kind.
func (blder *Builder) Named(name string) *Builder {
blder.name = name
return blder
}
// Complete builds the Application ControllerManagedBy.
func (blder *Builder) Complete(r reconcile.Reconciler) error {
_, err := blder.Build(r)
return err
}
// Build builds the Application ControllerManagedBy and returns the Controller it created.
func (blder *Builder) Build(r reconcile.Reconciler) (controller.Controller, error) {
if r == nil {
return nil, fmt.Errorf("must provide a non-nil Reconciler")
}
if blder.mgr == nil {
return nil, fmt.Errorf("must provide a non-nil Manager")
}
// Set the Config
blder.loadRestConfig()
// Set the ControllerManagedBy
if err := blder.doController(r); err != nil {
return nil, err
}
// Set the Watch
if err := blder.doWatch(); err != nil {
return nil, err
}
return blder.ctrl, nil
}
func (blder *Builder) doWatch() error {
// Reconcile type
src := &source.Kind{Type: blder.apiType}
hdler := &handler.EnqueueRequestForObject{}
err := blder.ctrl.Watch(src, hdler, blder.predicates...)
if err != nil {
return err
}
// Watches the managed types
for _, obj := range blder.managedObjects {
src := &source.Kind{Type: obj}
hdler := &handler.EnqueueRequestForOwner{
OwnerType: blder.apiType,
IsController: true,
}
if err := blder.ctrl.Watch(src, hdler, blder.predicates...); err != nil {
return err
}
}
// Do the watch requests
for _, w := range blder.watchRequest {
if err := blder.ctrl.Watch(w.src, w.eventhandler, blder.predicates...); err != nil {
return err
}
}
return nil
}
func (blder *Builder) loadRestConfig() {
if blder.config == nil {
blder.config = blder.mgr.GetConfig()
}
}
func (blder *Builder) getControllerName() (string, error) {
if blder.name != "" {
return blder.name, nil
}
gvk, err := getGvk(blder.apiType, blder.mgr.GetScheme())
if err != nil {
return "", err
}
return strings.ToLower(gvk.Kind), nil
}
func (blder *Builder) doController(r reconcile.Reconciler) error {
name, err := blder.getControllerName()
if err != nil {
return err
}
ctrlOptions := blder.ctrlOptions
ctrlOptions.Reconciler = r
blder.ctrl, err = newController(name, blder.mgr, ctrlOptions)
return err
}
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package builder provides wraps other controller-runtime libraries and exposes simple
// patterns for building common Controllers.
//
// Projects built with the builder package can trivially be rebased on top of the underlying
// packages if the project requires more customized behavior in the future.
package builder
import (
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
)
var log = logf.RuntimeLog.WithName("builder")
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
"net/http"
"net/url"
"strings"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"sigs.k8s.io/controller-runtime/pkg/webhook/conversion"
)
// WebhookBuilder builds a Webhook.
type WebhookBuilder struct {
apiType runtime.Object
gvk schema.GroupVersionKind
mgr manager.Manager
config *rest.Config
}
func WebhookManagedBy(m manager.Manager) *WebhookBuilder {
return &WebhookBuilder{mgr: m}
}
// TODO(droot): update the GoDoc for conversion.
// For takes a runtime.Object which should be a CR.
// If the given object implements the admission.Defaulter interface, a MutatingWebhook will be wired for this type.
// If the given object implements the admission.Validator interface, a ValidatingWebhook will be wired for this type.
func (blder *WebhookBuilder) For(apiType runtime.Object) *WebhookBuilder {
blder.apiType = apiType
return blder
}
// Complete builds the webhook.
func (blder *WebhookBuilder) Complete() error {
// Set the Config
blder.loadRestConfig()
// Set the Webhook if needed
return blder.registerWebhooks()
}
func (blder *WebhookBuilder) loadRestConfig() {
if blder.config == nil {
blder.config = blder.mgr.GetConfig()
}
}
func (blder *WebhookBuilder) registerWebhooks() error {
// Create webhook(s) for each type
var err error
blder.gvk, err = apiutil.GVKForObject(blder.apiType, blder.mgr.GetScheme())
if err != nil {
return err
}
blder.registerDefaultingWebhook()
blder.registerValidatingWebhook()
err = blder.registerConversionWebhook()
if err != nil {
return err
}
return nil
}
// registerDefaultingWebhook registers a defaulting webhook if th
func (blder *WebhookBuilder) registerDefaultingWebhook() {
defaulter, isDefaulter := blder.apiType.(admission.Defaulter)
if !isDefaulter {
log.Info("skip registering a mutating webhook, admission.Defaulter interface is not implemented", "GVK", blder.gvk)
return
}
mwh := admission.DefaultingWebhookFor(defaulter)
if mwh != nil {
path := generateMutatePath(blder.gvk)
// Checking if the path is already registered.
// If so, just skip it.
if !blder.isAlreadyHandled(path) {
log.Info("Registering a mutating webhook",
"GVK", blder.gvk,
"path", path)
blder.mgr.GetWebhookServer().Register(path, mwh)
}
}
}
func (blder *WebhookBuilder) registerValidatingWebhook() {
validator, isValidator := blder.apiType.(admission.Validator)
if !isValidator {
log.Info("skip registering a validating webhook, admission.Validator interface is not implemented", "GVK", blder.gvk)
return
}
vwh := admission.ValidatingWebhookFor(validator)
if vwh != nil {
path := generateValidatePath(blder.gvk)
// Checking if the path is already registered.
// If so, just skip it.
if !blder.isAlreadyHandled(path) {
log.Info("Registering a validating webhook",
"GVK", blder.gvk,
"path", path)
blder.mgr.GetWebhookServer().Register(path, vwh)
}
}
}
func (blder *WebhookBuilder) registerConversionWebhook() error {
ok, err := conversion.IsConvertible(blder.mgr.GetScheme(), blder.apiType)
if err != nil {
log.Error(err, "conversion check failed", "object", blder.apiType)
return err
}
if ok {
if !blder.isAlreadyHandled("/convert") {
blder.mgr.GetWebhookServer().Register("/convert", &conversion.Webhook{})
}
log.Info("conversion webhook enabled", "object", blder.apiType)
}
return nil
}
func (blder *WebhookBuilder) isAlreadyHandled(path string) bool {
if blder.mgr.GetWebhookServer().WebhookMux == nil {
return false
}
h, p := blder.mgr.GetWebhookServer().WebhookMux.Handler(&http.Request{URL: &url.URL{Path: path}})
if p == path && h != nil {
return true
}
return false
}
func generateMutatePath(gvk schema.GroupVersionKind) string {
return "/mutate-" + strings.Replace(gvk.Group, ".", "-", -1) + "-" +
gvk.Version + "-" + strings.ToLower(gvk.Kind)
}
func generateValidatePath(gvk schema.GroupVersionKind) string {
return "/validate-" + strings.Replace(gvk.Group, ".", "-", -1) + "-" +
gvk.Version + "-" + strings.ToLower(gvk.Kind)
}
/*
Copyright 2018 The Kubernetes Authors
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
......@@ -11,25 +14,27 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package component
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
/*
Package conversion provides interface definitions that an API Type needs to
implement for it to be supported by the generic conversion webhook handler
defined under pkg/webhook/conversion.
*/
package conversion
// Common const definitions
const (
LifecycleManaged = "managed"
LifecycleReferred = "referred"
)
import "k8s.io/apimachinery/pkg/runtime"
// Component represents a logical collection of resources
type Component struct {
Handle
Name string
CR metav1.Object
OwnerRef *metav1.OwnerReference
// Convertible defines capability of a type to convertible i.e. it can be converted to/from a hub type.
type Convertible interface {
runtime.Object
ConvertTo(dst Hub) error
ConvertFrom(src Hub) error
}
// KVMap is a map[string]string
type KVMap map[string]string
// Hub marks that a given type is the hub type for conversion. This means that
// all conversions will first convert to the hub type, then convert from the hub
// type to the destination type. All types besides the hub type should implement
// Convertible.
type Hub interface {
runtime.Object
Hub()
}
package conversion
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
)
// Decoder knows how to decode the contents of a CRD version conversion
// request into a concrete object.
// TODO(droot): consider reusing decoder from admission pkg for this.
type Decoder struct {
codecs serializer.CodecFactory
}
// NewDecoder creates a Decoder given the runtime.Scheme
func NewDecoder(scheme *runtime.Scheme) (*Decoder, error) {
return &Decoder{codecs: serializer.NewCodecFactory(scheme)}, nil
}
// Decode decodes the inlined object.
func (d *Decoder) Decode(content []byte) (runtime.Object, *schema.GroupVersionKind, error) {
deserializer := d.codecs.UniversalDeserializer()
return deserializer.Decode(content, nil, nil)
}
// DecodeInto decodes the inlined object in the into the passed-in runtime.Object.
func (d *Decoder) DecodeInto(content []byte, into runtime.Object) error {
deserializer := d.codecs.UniversalDeserializer()
return runtime.DecodeInto(deserializer, content, into)
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册