install kubernetes image puller/kip operator from che-operator (#541)

* Allow the che-operator to install the Kubernetes Image Puller operator and configure a KubernetesImagePuller CR in the same namespace that che is installed in.

Adds a new field to the CheCluster CR, imagePuller, that will install the operator and create a CR if enabled.  If disabled, it will uninstall and remove the image puller artifacts.

Signed-off-by: Tom George <tgeorge@redhat.com>
pull/569/head
Tom George 2020-12-08 08:53:24 -06:00 committed by GitHub
parent 867f428e25
commit a6848bf1b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
196 changed files with 18264 additions and 977 deletions

View File

@ -15,12 +15,17 @@ import (
"context"
"flag"
"fmt"
"os"
"runtime"
image_puller_api "github.com/che-incubator/kubernetes-image-puller-operator/pkg/apis"
"github.com/eclipse/che-operator/pkg/util"
operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
packagesv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
"github.com/operator-framework/operator-sdk/pkg/k8sutil"
"github.com/prometheus/common/log"
"github.com/sirupsen/logrus"
"os"
"runtime"
"github.com/eclipse/che-operator/pkg/apis"
"github.com/eclipse/che-operator/pkg/controller"
@ -124,6 +129,26 @@ func main() {
os.Exit(1)
}
if err := image_puller_api.AddToScheme(mgr.GetScheme()); err != nil {
logrus.Error(err, "")
os.Exit(1)
}
if err := packagesv1.AddToScheme(mgr.GetScheme()); err != nil {
logrus.Error(err, "")
os.Exit(1)
}
if err := operatorsv1alpha1.AddToScheme(mgr.GetScheme()); err != nil {
log.Error(err, "")
os.Exit(1)
}
if err := operatorsv1.AddToScheme(mgr.GetScheme()); err != nil {
log.Error(err, "")
os.Exit(1)
}
// Setup all Controllers
if err := controller.AddToManager(mgr); err != nil {
log.Error(err, "")

View File

@ -231,6 +231,42 @@ spec:
`latest` images, and `IfNotPresent` in other cases.
type: string
type: object
imagePuller:
description: Kubernetes Image Puller configuration
properties:
enable:
description: Install and configure the Kubernetes Image Puller Operator.
If true and no spec is provided, it will create a default KubernetesImagePuller
object to be managed by the Operator. If false, the KubernetesImagePuller
object will be deleted, and the operator will be uninstalled,
regardless of whether or not a spec is provided.
type: boolean
spec:
description: A KubernetesImagePullerSpec to configure the image
puller in the CheCluster
properties:
cachingCPULimit:
type: string
cachingCPURequest:
type: string
cachingIntervalHours:
type: string
cachingMemoryLimit:
type: string
cachingMemoryRequest:
type: string
configMapName:
type: string
daemonsetName:
type: string
deploymentName:
type: string
images:
type: string
nodeSelector:
type: string
type: object
type: object
k8s:
description: Configuration settings specific to Che installations made
on upstream Kubernetes.

View File

@ -90,7 +90,7 @@ metadata:
operatorframework.io/suggested-namespace: eclipse-che
repository: https://github.com/eclipse/che-operator
support: Eclipse Foundation
name: eclipse-che-preview-kubernetes.v7.23.0-38.nightly
name: eclipse-che-preview-kubernetes.v7.23.0-42.nightly
namespace: placeholder
spec:
apiservicedefinitions: {}
@ -442,6 +442,27 @@ spec:
- get
- list
- watch
- apiGroups:
- che.eclipse.org
resources:
- kubernetesimagepullers
verbs:
- '*'
- apiGroups:
- operators.coreos.com
resources:
- subscriptions
- clusterserviceversions
- operatorgroups
verbs:
- '*'
- apiGroups:
- packages.operators.coreos.com
resources:
- packagemanifests
verbs:
- get
- list
serviceAccountName: che-operator
strategy: deployment
installModes:
@ -473,4 +494,4 @@ spec:
maturity: stable
provider:
name: Eclipse Foundation
version: 7.23.0-38.nightly
version: 7.23.0-42.nightly

View File

@ -231,6 +231,42 @@ spec:
`latest` images, and `IfNotPresent` in other cases.
type: string
type: object
imagePuller:
description: Kubernetes Image Puller configuration
properties:
enable:
description: Install and configure the Kubernetes Image Puller Operator.
If true and no spec is provided, it will create a default KubernetesImagePuller
object to be managed by the Operator. If false, the KubernetesImagePuller
object will be deleted, and the operator will be uninstalled,
regardless of whether or not a spec is provided.
type: boolean
spec:
description: A KubernetesImagePullerSpec to configure the image
puller in the CheCluster
properties:
cachingCPULimit:
type: string
cachingCPURequest:
type: string
cachingIntervalHours:
type: string
cachingMemoryLimit:
type: string
cachingMemoryRequest:
type: string
configMapName:
type: string
daemonsetName:
type: string
deploymentName:
type: string
images:
type: string
nodeSelector:
type: string
type: object
type: object
k8s:
description: Configuration settings specific to Che installations made
on upstream Kubernetes.

View File

@ -82,7 +82,7 @@ metadata:
operatorframework.io/suggested-namespace: eclipse-che
repository: https://github.com/eclipse/che-operator
support: Eclipse Foundation
name: eclipse-che-preview-openshift.v7.23.0-38.nightly
name: eclipse-che-preview-openshift.v7.23.0-44.nightly
namespace: placeholder
spec:
apiservicedefinitions: {}
@ -463,6 +463,27 @@ spec:
- get
- list
- watch
- apiGroups:
- che.eclipse.org
resources:
- kubernetesimagepullers
verbs:
- '*'
- apiGroups:
- operators.coreos.com
resources:
- subscriptions
- clusterserviceversions
- operatorgroups
verbs:
- '*'
- apiGroups:
- packages.operators.coreos.com
resources:
- packagemanifests
verbs:
- get
- list
serviceAccountName: che-operator
strategy: deployment
installModes:
@ -493,4 +514,4 @@ spec:
maturity: stable
provider:
name: Eclipse Foundation
version: 7.23.0-38.nightly
version: 7.23.0-44.nightly

View File

@ -232,6 +232,42 @@ spec:
`latest` images, and `IfNotPresent` in other cases.
type: string
type: object
imagePuller:
description: Kubernetes Image Puller configuration
properties:
enable:
description: Install and configure the Kubernetes Image Puller Operator.
If true and no spec is provided, it will create a default KubernetesImagePuller
object to be managed by the Operator. If false, the KubernetesImagePuller
object will be deleted, and the operator will be uninstalled,
regardless of whether or not a spec is provided.
type: boolean
spec:
description: A KubernetesImagePullerSpec to configure the image
puller in the CheCluster
properties:
cachingCPULimit:
type: string
cachingCPURequest:
type: string
cachingIntervalHours:
type: string
cachingMemoryLimit:
type: string
cachingMemoryRequest:
type: string
configMapName:
type: string
daemonsetName:
type: string
deploymentName:
type: string
images:
type: string
nodeSelector:
type: string
type: object
type: object
k8s:
description: Configuration settings specific to Che installations made
on upstream Kubernetes.

View File

@ -96,3 +96,24 @@ rules:
- get
- list
- watch
- apiGroups:
- che.eclipse.org
resources:
- kubernetesimagepullers
verbs:
- '*'
- apiGroups:
- operators.coreos.com
resources:
- subscriptions
- clusterserviceversions
- operatorgroups
verbs:
- '*'
- apiGroups:
- packages.operators.coreos.com
resources:
- packagemanifests
verbs:
- get
- list

13
go.mod
View File

@ -3,18 +3,20 @@ module github.com/eclipse/che-operator
go 1.13
require (
github.com/che-incubator/kubernetes-image-puller-operator v0.0.0-20200901231735-f852a5a3ea5c // indirect
github.com/che-incubator/kubernetes-image-puller-operator v0.0.0-20200901231735-f852a5a3ea5c
github.com/google/go-cmp v0.4.0
github.com/openshift/api v3.9.1-0.20190924102528-32369d4db2ad+incompatible
github.com/operator-framework/api v0.3.20
github.com/operator-framework/operator-lifecycle-manager v0.0.0-20191115003340-16619cd27fa5
github.com/operator-framework/operator-sdk v0.15.2
github.com/prometheus/common v0.7.0
github.com/sirupsen/logrus v1.4.2
golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914
gopkg.in/yaml.v2 v2.2.5
k8s.io/api v0.17.0
k8s.io/apimachinery v0.17.1
gopkg.in/yaml.v2 v2.2.8
k8s.io/api v0.18.2
k8s.io/apimachinery v0.18.2
k8s.io/client-go v12.0.0+incompatible
sigs.k8s.io/controller-runtime v0.4.0
sigs.k8s.io/controller-runtime v0.6.0
)
// Pinned to kubernetes-1.16.2
@ -40,6 +42,7 @@ replace (
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.0.0-20191016115753-cf0698c3a16b
k8s.io/metrics => k8s.io/metrics v0.0.0-20191016113814-3b1a734dba6e
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.0.0-20191016112829-06bb3c9d77c9
sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.4.0
)
replace github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 // Required by Helm

60
go.sum
View File

@ -3,7 +3,6 @@ bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
@ -27,33 +26,24 @@ github.com/Azure/azure-sdk-for-go v32.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo
github.com/Azure/azure-sdk-for-go v38.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest v0.9.3 h1:OZEIaBbMdUE/Js+BQKlpO81XlISgipr6yDJ+PSwsgi4=
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
github.com/Azure/go-autorest/autorest/adal v0.8.1 h1:pZdL8o72rK+avFWl+p9nE8RWi1JInZrWJYlnpfXJwHk=
github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/autorest/to v0.1.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@ -123,6 +113,7 @@ github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
@ -175,10 +166,10 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/prometheus-operator v0.34.0 h1:TF9qaydNeUamLKs0hniaapa4FBz8U8TIlRRtJX987A4=
github.com/coreos/prometheus-operator v0.34.0/go.mod h1:Li6rMllG/hYIyXfMuvUwhyC+hqwJVHdsDdP21hypT1M=
github.com/coreos/rkt v1.30.0/go.mod h1:O634mlH6U7qk87poQifK6M2rsFNt+FyUTWNMnP1hF1U=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
@ -198,7 +189,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
github.com/deislabs/oras v0.7.0/go.mod h1:sqMKPG3tMyIX9xwXUBRLhZ24o+uT4y6jgBD2RzUTKDM=
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dhui/dktest v0.3.0/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc=
@ -227,6 +217,7 @@ github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
@ -260,6 +251,7 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M=
github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
github.com/go-bindata/go-bindata/v3 v3.1.3/go.mod h1:1/zrpXsLD8YDIbhZRqXzm1Ghc7NhEvIN9+Z6R5/xH4I=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -321,6 +313,7 @@ github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9k
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
@ -369,7 +362,6 @@ github.com/google/cadvisor v0.34.0/go.mod h1:1nql6U13uTHaLYB8rLS5x9IJc2qT6Xd/Tr1
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@ -401,7 +393,6 @@ github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTV
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gophercloud/gophercloud v0.2.0 h1:lD2Bce2xBAMNNcFZ0dObTpXkGLlVIb33RPVUNVpw6ic=
github.com/gophercloud/gophercloud v0.2.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
@ -469,7 +460,6 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@ -543,6 +533,8 @@ github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPx
github.com/miekg/dns v0.0.0-20181005163659-0d29b283ac0f/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mikefarah/yaml/v2 v2.4.0/go.mod h1:ahVqZF4n1W4NqwvVnZzC4es67xsW9uR/RRf2RRxieJU=
github.com/mikefarah/yq/v2 v2.4.1/go.mod h1:i8SYf1XdgUvY2OFwSqGAtWOOgimD2McJ6iutoxRm4k0=
github.com/mindprince/gonvml v0.0.0-20171110221305-fee913ce8fb2/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY=
github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
@ -587,12 +579,19 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
@ -607,7 +606,11 @@ github.com/openshift/prom-label-proxy v0.1.1-0.20191016113035-b8153a7f39f1/go.mo
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.0/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/operator-framework/api v0.0.0-20200120235816-80fd2f1a09c9 h1:HfxMEPJ0djo/RNfrmli3kI2oKS6IeuIZWu1Q5Rewt/o=
github.com/operator-framework/api v0.0.0-20200120235816-80fd2f1a09c9/go.mod h1:S5IdlJvmKkF84K2tBvsrqJbI2FVy03P88R75snpRxJo=
github.com/operator-framework/api v0.3.20 h1:2Ks8GXXl/H2sV9ll2iQBUO65ABQ5VuzN3IKEZCJWljo=
github.com/operator-framework/api v0.3.20/go.mod h1:Xbje9x0SHmh0nihE21kpesB38vk3cyxnE6JdDS8Jo1Q=
github.com/operator-framework/operator-lifecycle-manager v0.0.0-20191115003340-16619cd27fa5 h1:rjaihxY50c5C+kbQIK4s36R8zxByATYrgRbua4eiG6o=
github.com/operator-framework/operator-lifecycle-manager v0.0.0-20191115003340-16619cd27fa5/go.mod h1:zL34MNy92LPutBH5gQK+gGhtgTUlZZX03I2G12vWHF4=
github.com/operator-framework/operator-registry v1.5.1/go.mod h1:agrQlkWOo1q8U1SAaLSS2WQ+Z9vswNT2M2HFib9iuLY=
github.com/operator-framework/operator-registry v1.5.3/go.mod h1:agrQlkWOo1q8U1SAaLSS2WQ+Z9vswNT2M2HFib9iuLY=
@ -634,6 +637,8 @@ github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -688,6 +693,7 @@ github.com/rubenv/sql-migrate v0.0.0-20191025130928-9355dd04f4b3/go.mod h1:WS0rl
github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
@ -696,6 +702,7 @@ github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9Nz
github.com/shurcooL/githubv4 v0.0.0-20190718010115-4ba037080260/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
github.com/shurcooL/githubv4 v0.0.0-20191102174205-af46314aec7b/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
@ -711,6 +718,7 @@ github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@ -731,6 +739,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/syndtr/gocapability v0.0.0-20160928074757-e7cb7fa329f4/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tektoncd/pipeline v0.10.1/go.mod h1:D2X0exT46zYx95BU7ByM8+erpjoN7thmUBvlKThOszU=
github.com/tektoncd/plumbing v0.0.0-20191216083742-847dcf196de9/go.mod h1:QZHgU07PRBTRF6N57w4+ApRu8OgfYLFNqCDlfEZaD9Y=
@ -802,7 +812,6 @@ golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152 h1:ZC1Xn5A1nlpSmQCIva4bZ3ob3lmhYIefc+GU+DLg1Ow=
golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -862,7 +871,6 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 h1:N66aaryRB3Ax92gH0v3hp1QYZ3zWWCCUR/j8Ifh45Ss=
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 h1:MlY3mEfbnWGmUi4rtHOtNnnnN4UJRGSyLPx+DXA5Sq4=
golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -915,10 +923,12 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934 h1:u/E0NqCIWRDAo9WCFo6Ko49njPFDLSd3z+X1HgWDMpE=
golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -956,13 +966,14 @@ golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDq
golang.org/x/tools v0.0.0-20190807223507-b346f7fd45de/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191010171213-8abd42400456/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191018212557-ed542cd5b28a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191030203535-5e247c9ad0a0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112005509-a3f652f18032/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200115165105-de0b1760071a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
@ -1028,6 +1039,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
gopkg.in/imdario/mergo.v0 v0.3.7/go.mod h1:9qPP6AGrlC1G2PTNXko614FwGZvorN7MiBU0Eppok+U=
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
@ -1038,6 +1050,7 @@ gopkg.in/jcmturner/gokrb5.v7 v7.3.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuv
gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=
gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v1 v1.1.2/go.mod h1:QpYS+a4WhS+DTlyQIi6Ka7MS3SuR9a055rgXNEe6EiA=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
@ -1053,10 +1066,11 @@ gopkg.in/yaml.v2 v2.0.0/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.1.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
@ -1076,6 +1090,7 @@ k8s.io/apiextensions-apiserver v0.0.0-20191016113550-5357c4baaf65 h1:kThoiqgMsSw
k8s.io/apiextensions-apiserver v0.0.0-20191016113550-5357c4baaf65/go.mod h1:5BINdGqggRXXKnDgpwoJ7PyQH8f+Ypp02fvVNcIFy9s=
k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8 h1:Iieh/ZEgT3BWwbLD5qEKcY06jKuPEl6zC7gPSehoLw4=
k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ=
k8s.io/apiserver v0.0.0-20191016112112-5190913f932d h1:leksCBKKBrPJmW1jV4dZUvwqmVtXpKdzpHsqXfFS094=
k8s.io/apiserver v0.0.0-20191016112112-5190913f932d/go.mod h1:7OqfAolfWxUM/jJ/HBLyE+cdaWFBUoo5Q5pHgJVj2ws=
k8s.io/autoscaler v0.0.0-20190607113959-1b4f1855cb8e/go.mod h1:QEXezc9uKPT91dwqhSJq3GNI3B1HxFRQHiku9kmrsSA=
k8s.io/cli-runtime v0.0.0-20191016114015-74ad18325ed5/go.mod h1:sDl6WKSQkDM6zS1u9F49a0VooQ3ycYFBFLqd2jf2Xfo=
@ -1103,13 +1118,11 @@ k8s.io/kube-aggregator v0.0.0-20191016112429-9587704a8ad4/go.mod h1:+aW0UZgSXdTS
k8s.io/kube-controller-manager v0.0.0-20191016114939-2b2b218dc1df/go.mod h1:WgrTcPKYAfNa9C0LV1UeK+XqfbSOUH1WGq/vX5UiW40=
k8s.io/kube-openapi v0.0.0-20190320154901-5e45bb682580/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d h1:Xpe6sK+RY4ZgCTyZ3y273UmFmURhjtoJiwOMbQsXitY=
k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/kube-proxy v0.0.0-20191016114407-2e83b6f20229/go.mod h1:2Hxci1uzXO5ipP0h9n2+h18fvNkBTpYlckk5dOPu8zg=
k8s.io/kube-scheduler v0.0.0-20191016114748-65049c67a58b/go.mod h1:BgDUHHC5Wl0xcBUQgo2XEprE5nG5i9tlRR4iNgEFbL0=
k8s.io/kube-state-metrics v1.7.2 h1:6vdtgXrrRRMSgnyDmgua+qvgCYv954JNfxXAtDkeLVQ=
k8s.io/kube-state-metrics v1.7.2/go.mod h1:U2Y6DRi07sS85rmVPmBFlmv+2peBcL8IWGjM+IjYA/E=
k8s.io/kubectl v0.0.0-20191016120415-2ed914427d51/go.mod h1:gL826ZTIfD4vXTGlmzgTbliCAT9NGiqpCqK2aNYv5MQ=
k8s.io/kubelet v0.0.0-20191016114556-7841ed97f1b2/go.mod h1:SBvrtLbuePbJygVXGGCMtWKH07+qrN2dE1iMnteSG8E=
@ -1138,6 +1151,7 @@ rsc.io/letsencrypt v0.0.1/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg=
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
sigs.k8s.io/controller-tools v0.2.4/go.mod h1:m/ztfQNocGYBgTTCmFdnK94uVvgxeZeE3LtJvd/jIzA=
sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
@ -1145,4 +1159,6 @@ sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH
sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=

View File

@ -19,6 +19,7 @@ package v1
// IMPORTANT These 2 last steps are important to ensure backward compatibility with already existing `CheCluster` CRs that were created when no schema was provided.
import (
chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/pkg/apis/che/v1alpha1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
@ -50,6 +51,9 @@ type CheClusterSpec struct {
// Configuration settings specific to Che installations made on upstream Kubernetes.
// +optional
K8s CheClusterSpecK8SOnly `json:"k8s"`
// Kubernetes Image Puller configuration
// +optional
ImagePuller CheClusterSpecImagePuller `json:"imagePuller"`
}
// +k8s:openapi-gen=true
@ -519,6 +523,20 @@ type CheClusterSpecMetrics struct {
Enable bool `json:"enable"`
}
// +k8s:openapi-gen=true
// Configuration settings for installation and configuration of the Kubernetes Image Puller
// See https://github.com/che-incubator/kubernetes-image-puller-operator
type CheClusterSpecImagePuller struct {
// Install and configure the Kubernetes Image Puller Operator. If true and no spec is provided,
// it will create a default KubernetesImagePuller object to be managed by the Operator.
// If false, the KubernetesImagePuller object will be deleted, and the operator will be uninstalled,
// regardless of whether or not a spec is provided.
Enable bool `json:"enable"`
// A KubernetesImagePullerSpec to configure the image puller in the CheCluster
// +optional
Spec chev1alpha1.KubernetesImagePullerSpec `json:"spec"`
}
// CheClusterStatus defines the observed state of Che installation
type CheClusterStatus struct {
// Indicates if or not a Postgres instance has been correctly provisioned
@ -618,3 +636,7 @@ func (c *CheCluster) IsAirGapMode() bool {
return c.Spec.Server.AirGapContainerRegistryHostname != "" ||
c.Spec.Server.AirGapContainerRegistryOrganization != ""
}
func (c *CheCluster) IsImagePullerSpecEmpty() bool {
return c.Spec.ImagePuller.Spec == (chev1alpha1.KubernetesImagePullerSpec{})
}

View File

@ -79,6 +79,7 @@ func (in *CheClusterSpec) DeepCopyInto(out *CheClusterSpec) {
out.Storage = in.Storage
out.Metrics = in.Metrics
out.K8s = in.K8s
out.ImagePuller = in.ImagePuller
return
}
@ -126,6 +127,23 @@ func (in *CheClusterSpecDB) DeepCopy() *CheClusterSpecDB {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CheClusterSpecImagePuller) DeepCopyInto(out *CheClusterSpecImagePuller) {
*out = *in
out.Spec = in.Spec
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CheClusterSpecImagePuller.
func (in *CheClusterSpecImagePuller) DeepCopy() *CheClusterSpecImagePuller {
if in == nil {
return nil
}
out := new(CheClusterSpecImagePuller)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CheClusterSpecK8SOnly) DeepCopyInto(out *CheClusterSpecK8SOnly) {
*out = *in

View File

@ -42,6 +42,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/discovery"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/event"
@ -71,10 +72,15 @@ func newReconciler(mgr manager.Manager) (reconcile.Reconciler, error) {
if err != nil {
return nil, err
}
discoveryClient, err := discovery.NewDiscoveryClientForConfig(mgr.GetConfig())
if err != nil {
return nil, err
}
return &ReconcileChe{
client: mgr.GetClient(),
nonCachedClient: noncachedClient,
scheme: mgr.GetScheme(),
discoveryClient: discoveryClient,
}, nil
}
@ -254,6 +260,9 @@ type ReconcileChe struct {
// to simply read objects thta we don't intend
// to further watch
nonCachedClient client.Client
// A discovery client to check for the existence of certain APIs registered
// in the API Server
discoveryClient discovery.DiscoveryInterface
scheme *runtime.Scheme
tests bool
}
@ -275,12 +284,15 @@ const (
// Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, error) {
clusterAPI := deploy.ClusterAPI{
Client: r.client,
Scheme: r.scheme,
Client: r.client,
NonCachedClient: r.nonCachedClient,
DiscoveryClient: r.discoveryClient,
Scheme: r.scheme,
}
// Fetch the CheCluster instance
tests := r.tests
instance, err := r.GetCR(request)
if err != nil {
if errors.IsNotFound(err) {
// Request object not found, could have been deleted after reconcile request.
@ -293,11 +305,20 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
}
deployContext := &deploy.DeployContext{
ClusterAPI: clusterAPI,
CheCluster: instance,
ClusterAPI: clusterAPI,
CheCluster: instance,
InternalService: deploy.InternalService{},
}
// Reconcile the imagePuller section of the CheCluster
imagePullerResult, err := deploy.ReconcileImagePuller(deployContext)
if err != nil {
return imagePullerResult, err
}
if imagePullerResult.Requeue || imagePullerResult.RequeueAfter > 0 {
return imagePullerResult, err
}
isOpenShift, isOpenShift4, err := util.DetectOpenShift()
if err != nil {
logrus.Errorf("An error occurred when detecting current infra: %s", err)
@ -369,6 +390,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
return reconcile.Result{}, err
}
}
}
// Read proxy configuration

View File

@ -13,11 +13,15 @@ package che
import (
"context"
"fmt"
"io/ioutil"
"os"
"reflect"
"time"
chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/pkg/apis/che/v1alpha1"
identity_provider "github.com/eclipse/che-operator/pkg/deploy/identity-provider"
"github.com/google/go-cmp/cmp"
"github.com/eclipse/che-operator/pkg/deploy"
@ -28,6 +32,9 @@ import (
oauth "github.com/openshift/api/oauth/v1"
routev1 "github.com/openshift/api/route/v1"
userv1 "github.com/openshift/api/user/v1"
operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
packagesv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
appsv1 "k8s.io/api/apps/v1"
@ -37,9 +44,13 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/discovery"
fakeDiscovery "k8s.io/client-go/discovery/fake"
fakeclientset "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
@ -47,8 +58,94 @@ import (
)
var (
name = "eclipse-che"
namespace = "eclipse-che"
name = "eclipse-che"
namespace = "eclipse-che"
csvName = "kubernetes-imagepuller-operator.v0.0.4"
packageManifest = &packagesv1.PackageManifest{
ObjectMeta: metav1.ObjectMeta{
Name: "kubernetes-imagepuller-operator",
Namespace: namespace,
},
Status: packagesv1.PackageManifestStatus{
CatalogSource: "community-operators",
CatalogSourceNamespace: "olm",
DefaultChannel: "stable",
PackageName: "kubernetes-imagepuller-operator",
},
}
operatorGroup = &operatorsv1.OperatorGroup{
ObjectMeta: metav1.ObjectMeta{
Name: "kubernetes-imagepuller-operator",
Namespace: namespace,
},
Spec: operatorsv1.OperatorGroupSpec{
TargetNamespaces: []string{
namespace,
},
},
}
subscription = &operatorsv1alpha1.Subscription{
ObjectMeta: metav1.ObjectMeta{
Name: "kubernetes-imagepuller-operator",
Namespace: namespace,
},
Spec: &operatorsv1alpha1.SubscriptionSpec{
CatalogSource: "community-operators",
Channel: "stable",
CatalogSourceNamespace: "olm",
InstallPlanApproval: operatorsv1alpha1.ApprovalAutomatic,
Package: "kubernetes-imagepuller-operator",
},
}
wrongSubscription = &operatorsv1alpha1.Subscription{
ObjectMeta: metav1.ObjectMeta{
Name: "kubernetes-imagepuller-operator",
Namespace: namespace,
},
Spec: &operatorsv1alpha1.SubscriptionSpec{
CatalogSource: "community-operators",
Channel: "beta",
CatalogSourceNamespace: "olm",
InstallPlanApproval: operatorsv1alpha1.ApprovalAutomatic,
Package: "kubernetes-imagepuller-operator",
},
}
valueTrue = true
defaultImagePuller = &chev1alpha1.KubernetesImagePuller{
TypeMeta: metav1.TypeMeta{
APIVersion: "che.eclipse.org/v1alpha1",
Kind: "KubernetesImagePuller",
},
ObjectMeta: metav1.ObjectMeta{
Name: "eclipse-che-image-puller",
Namespace: namespace,
Labels: map[string]string{
"app.kubernetes.io/part-of": name,
"app": "che",
"component": "kubernetes-image-puller",
},
ResourceVersion: "1",
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "org.eclipse.che/v1",
Kind: "CheCluster",
Controller: &valueTrue,
BlockOwnerDeletion: &valueTrue,
Name: "eclipse-che",
},
},
},
Spec: chev1alpha1.KubernetesImagePullerSpec{
DeploymentName: "kubernetes-image-puller",
ConfigMapName: "k8s-image-puller",
},
}
clusterServiceVersion = &operatorsv1alpha1.ClusterServiceVersion{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: csvName,
},
}
)
func init() {
@ -62,14 +159,286 @@ func init() {
}
}
func TestImagePullerConfiguration(t *testing.T) {
type testCase struct {
name string
initCR *orgv1.CheCluster
initObjects []runtime.Object
expectedCR *orgv1.CheCluster
expectedOperatorGroup *operatorsv1.OperatorGroup
expectedSubscription *operatorsv1alpha1.Subscription
expectedImagePuller *chev1alpha1.KubernetesImagePuller
shouldDelete bool
}
testCases := []testCase{
{
name: "image puller enabled, no operatorgroup, should create an operatorgroup",
initCR: InitCheCRWithImagePullerEnabled(),
initObjects: []runtime.Object{
packageManifest,
},
expectedOperatorGroup: operatorGroup,
},
{
name: "image puller enabled, operatorgroup exists, should create a subscription",
initCR: InitCheCRWithImagePullerEnabled(),
initObjects: []runtime.Object{
packageManifest,
operatorGroup,
},
expectedSubscription: subscription,
},
{
name: "image puller enabled, subscription created but has changed, should update subscription, this shouldn't happen",
initCR: InitCheCRWithImagePullerEnabled(),
initObjects: []runtime.Object{
packageManifest,
operatorGroup,
wrongSubscription,
},
expectedSubscription: subscription,
},
{
name: "image puller enabled, subscription created, should add finalizer",
initCR: InitCheCRWithImagePullerEnabled(),
expectedCR: ExpectedCheCRWithImagePullerFinalizer(),
initObjects: []runtime.Object{
packageManifest,
operatorGroup,
subscription,
},
},
{
name: "image puller enabled with finalizer but default values are empty, subscription exists, should update the CR",
initCR: InitCheCRWithImagePullerFinalizer(),
expectedCR: &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{
Kind: "CheCluster",
APIVersion: "org.eclipse.che/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
ResourceVersion: "1",
Finalizers: []string{
"kubernetesimagepullers.finalizers.che.eclipse.org",
},
},
Spec: orgv1.CheClusterSpec{
ImagePuller: orgv1.CheClusterSpecImagePuller{
Enable: true,
Spec: chev1alpha1.KubernetesImagePullerSpec{
DeploymentName: "kubernetes-image-puller",
ConfigMapName: "k8s-image-puller",
},
},
},
},
initObjects: []runtime.Object{
packageManifest,
operatorGroup,
subscription,
},
},
{
name: "image puller enabled default values already set, subscription exists, should create a KubernetesImagePuller",
initCR: InitCheCRWithImagePullerEnabledAndDefaultValuesSet(),
initObjects: []runtime.Object{
packageManifest,
operatorGroup,
subscription,
},
expectedImagePuller: defaultImagePuller,
},
{
name: "image puller enabled, KubernetesImagePuller created and spec in CheCluster is different, should update the KubernetesImagePuller",
initCR: InitCheCRWithImagePullerEnabledAndNewValuesSet(),
initObjects: []runtime.Object{
packageManifest,
operatorGroup,
subscription,
defaultImagePuller,
},
expectedImagePuller: &chev1alpha1.KubernetesImagePuller{
TypeMeta: metav1.TypeMeta{Kind: "KubernetesImagePuller", APIVersion: "che.eclipse.org/v1alpha1"},
ObjectMeta: metav1.ObjectMeta{
ResourceVersion: "2",
Name: name + "-image-puller",
Namespace: namespace,
Labels: map[string]string{
"app": "che",
"component": "kubernetes-image-puller",
"app.kubernetes.io/part-of": "eclipse-che",
},
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "org.eclipse.che/v1",
Kind: "CheCluster",
BlockOwnerDeletion: &valueTrue,
Controller: &valueTrue,
Name: name,
},
},
},
Spec: chev1alpha1.KubernetesImagePullerSpec{
ConfigMapName: "k8s-image-puller-trigger-update",
DeploymentName: "kubernetes-image-puller-trigger-update",
},
},
},
{
name: "image puller already created, imagePuller disabled, should delete everything",
initCR: InitCheCRWithImagePullerDisabled(),
initObjects: []runtime.Object{
packageManifest,
operatorGroup,
subscription,
clusterServiceVersion,
defaultImagePuller,
},
shouldDelete: true,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
logf.SetLogger(zap.LoggerTo(os.Stdout, true))
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
packagesv1.AddToScheme(scheme.Scheme)
operatorsv1alpha1.AddToScheme(scheme.Scheme)
operatorsv1.AddToScheme(scheme.Scheme)
chev1alpha1.AddToScheme(scheme.Scheme)
testCase.initObjects = append(testCase.initObjects, testCase.initCR)
cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
nonCachedClient := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
clientSet := fakeclientset.NewSimpleClientset()
fakeDiscovery, ok := clientSet.Discovery().(*fakeDiscovery.FakeDiscovery)
fakeDiscovery.Fake.Resources = []*metav1.APIResourceList{
{
GroupVersion: "packages.operators.coreos.com/v1",
APIResources: []metav1.APIResource{
{
Kind: "PackageManifest",
},
},
},
{
GroupVersion: "operators.coreos.com/v1alpha1",
APIResources: []metav1.APIResource{
{Kind: "OperatorGroup"},
{Kind: "Subscription"},
{Kind: "ClusterServiceVersion"},
},
},
{
GroupVersion: "che.eclipse.org/v1alpha1",
APIResources: []metav1.APIResource{
{Kind: "KubernetesImagePuller"},
},
},
}
if !ok {
t.Error("Error creating fake discovery client")
os.Exit(1)
}
r := &ReconcileChe{
client: cli,
nonCachedClient: nonCachedClient,
discoveryClient: fakeDiscovery,
scheme: scheme.Scheme,
}
req := reconcile.Request{
NamespacedName: types.NamespacedName{
Name: name,
Namespace: namespace,
},
}
_, err := r.Reconcile(req)
if err != nil {
t.Fatalf("Error reconciling: %v", err)
}
if testCase.expectedOperatorGroup != nil {
gotOperatorGroup := &operatorsv1.OperatorGroup{}
err := r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: testCase.expectedOperatorGroup.Namespace, Name: testCase.expectedOperatorGroup.Name}, gotOperatorGroup)
if err != nil {
t.Errorf("Error getting OperatorGroup: %v", err)
}
if !reflect.DeepEqual(testCase.expectedOperatorGroup.Spec.TargetNamespaces, gotOperatorGroup.Spec.TargetNamespaces) {
t.Errorf("Error expected target namespace %v but got %v", testCase.expectedOperatorGroup.Spec.TargetNamespaces, gotOperatorGroup.Spec.TargetNamespaces)
}
}
if testCase.expectedSubscription != nil {
gotSubscription := &operatorsv1alpha1.Subscription{}
err := r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: testCase.expectedSubscription.Namespace, Name: testCase.expectedSubscription.Name}, gotSubscription)
if err != nil {
t.Errorf("Error getting Subscription: %v", err)
}
if !reflect.DeepEqual(testCase.expectedSubscription.Spec, gotSubscription.Spec) {
t.Errorf("Error, subscriptions differ (-want +got) %v", cmp.Diff(testCase.expectedSubscription.Spec, gotSubscription.Spec))
}
}
// if expectedCR is not set, don't check it
if testCase.expectedCR != nil && !reflect.DeepEqual(testCase.initCR, testCase.expectedCR) {
gotCR := &orgv1.CheCluster{}
err = r.client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: name}, gotCR)
if err != nil {
t.Errorf("Error getting CheCluster: %v", err)
}
if !reflect.DeepEqual(testCase.expectedCR, gotCR) {
t.Errorf("Expected CR and CR returned from API server are different (-want +got): %v", cmp.Diff(testCase.expectedCR, gotCR))
}
}
if testCase.expectedImagePuller != nil {
gotImagePuller := &chev1alpha1.KubernetesImagePuller{}
err = r.client.Get(context.TODO(), types.NamespacedName{Namespace: testCase.expectedImagePuller.Namespace, Name: testCase.expectedImagePuller.Name}, gotImagePuller)
if err != nil {
t.Errorf("Error getting KubernetesImagePuller: %v", err)
}
if !reflect.DeepEqual(testCase.expectedImagePuller, gotImagePuller) {
t.Errorf("Expected KubernetesImagePuller and KubernetesImagePuller returned from API server differ (-want, +got): %v", cmp.Diff(testCase.expectedImagePuller, gotImagePuller))
}
}
if testCase.shouldDelete {
imagePuller := &chev1alpha1.KubernetesImagePuller{}
err = r.client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: name + "-image-puller"}, imagePuller)
if err == nil || !errors.IsNotFound(err) {
t.Fatalf("Should not have found KubernetesImagePuller: %v", err)
}
clusterServiceVersion := &operatorsv1alpha1.ClusterServiceVersion{}
err = r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: csvName}, clusterServiceVersion)
if err == nil || !errors.IsNotFound(err) {
t.Fatalf("Should not have found ClusterServiceVersion: %v", err)
}
subscription := &operatorsv1alpha1.Subscription{}
err = r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "kubernetes-imagepuller-operator"}, subscription)
if err == nil || !errors.IsNotFound(err) {
t.Fatalf("Should not have found subscription: %v", err)
}
operatorGroup := &operatorsv1.OperatorGroup{}
err = r.nonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "kubernetes-imagepuller-operator"}, operatorGroup)
if err == nil || !errors.IsNotFound(err) {
t.Fatalf("Should not have found subscription: %v", err)
}
}
})
}
}
func TestCheController(t *testing.T) {
// Set the logger to development mode for verbose logs.
logf.SetLogger(logf.ZapLogger(true))
cl, scheme := Init()
cl, dc, scheme := Init()
// Create a ReconcileChe object with the scheme and fake client
r := &ReconcileChe{client: cl, nonCachedClient: cl, scheme: &scheme, tests: true}
r := &ReconcileChe{client: cl, nonCachedClient: cl, scheme: &scheme, discoveryClient: dc, tests: true}
// get CR
cheCR := &orgv1.CheCluster{}
@ -287,10 +656,10 @@ func TestConfiguringLabelsForRoutes(t *testing.T) {
// Set the logger to development mode for verbose logs.
logf.SetLogger(logf.ZapLogger(true))
cl, scheme := Init()
cl, dc, scheme := Init()
// Create a ReconcileChe object with the scheme and fake client
r := &ReconcileChe{client: cl, nonCachedClient: cl, scheme: &scheme, tests: true}
r := &ReconcileChe{client: cl, nonCachedClient: cl, scheme: &scheme, discoveryClient: dc, tests: true}
// get CR
cheCR := &orgv1.CheCluster{}
@ -339,10 +708,10 @@ func TestConfiguringInternalNetworkTest(t *testing.T) {
// Set the logger to development mode for verbose logs.
logf.SetLogger(logf.ZapLogger(true))
cl, scheme := Init()
cl, discoveryClient, scheme := Init()
// Create a ReconcileChe object with the scheme and fake client
r := &ReconcileChe{client: cl, nonCachedClient: cl, scheme: &scheme, tests: true}
r := &ReconcileChe{client: cl, nonCachedClient: cl, discoveryClient: discoveryClient, scheme: &scheme, tests: true}
// get CR
cheCR := &orgv1.CheCluster{}
@ -486,7 +855,7 @@ func TestConfiguringInternalNetworkTest(t *testing.T) {
}
}
func Init() (client.Client, runtime.Scheme) {
func Init() (client.Client, discovery.DiscoveryInterface, runtime.Scheme) {
pgPod := &corev1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
@ -536,10 +905,19 @@ func Init() (client.Client, runtime.Scheme) {
Namespace: namespace,
},
}
packageManifest := &packagesv1.PackageManifest{
ObjectMeta: metav1.ObjectMeta{
Name: "kubernetes-imagepuller-operator",
Namespace: namespace,
},
}
// Objects to track in the fake client.
objs := []runtime.Object{
cheCR, pgPod, userList, route,
cheCR, pgPod, userList, route, packageManifest,
}
oAuthClient := &oauth.OAuthClient{}
users := &userv1.UserList{}
user := &userv1.User{}
@ -551,7 +929,127 @@ func Init() (client.Client, runtime.Scheme) {
scheme.AddKnownTypes(oauth.SchemeGroupVersion, oAuthClient)
scheme.AddKnownTypes(userv1.SchemeGroupVersion, users, user)
scheme.AddKnownTypes(console.GroupVersion, &console.ConsoleLink{})
chev1alpha1.AddToScheme(scheme)
packagesv1.AddToScheme(scheme)
operatorsv1.AddToScheme(scheme)
operatorsv1alpha1.AddToScheme(scheme)
cli := fakeclientset.NewSimpleClientset()
fakeDiscovery, ok := cli.Discovery().(*fakeDiscovery.FakeDiscovery)
if !ok {
fmt.Errorf("Error creating fake discovery client")
os.Exit(1)
}
// Create a fake client to mock API calls
return fake.NewFakeClient(objs...), *scheme
return fake.NewFakeClient(objs...), fakeDiscovery, *scheme
}
func InitCheCRWithImagePullerEnabled() *orgv1.CheCluster {
return &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: orgv1.CheClusterSpec{
ImagePuller: orgv1.CheClusterSpecImagePuller{
Enable: true,
},
},
}
}
func InitCheCRWithImagePullerFinalizer() *orgv1.CheCluster {
return &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Finalizers: []string{
"kubernetesimagepullers.finalizers.che.eclipse.org",
},
},
Spec: orgv1.CheClusterSpec{
ImagePuller: orgv1.CheClusterSpecImagePuller{
Enable: true,
},
},
}
}
func ExpectedCheCRWithImagePullerFinalizer() *orgv1.CheCluster {
return &orgv1.CheCluster{
TypeMeta: metav1.TypeMeta{
Kind: "CheCluster",
APIVersion: "org.eclipse.che/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Finalizers: []string{
"kubernetesimagepullers.finalizers.che.eclipse.org",
},
ResourceVersion: "1",
},
Spec: orgv1.CheClusterSpec{
ImagePuller: orgv1.CheClusterSpecImagePuller{
Enable: true,
},
},
}
}
func InitCheCRWithImagePullerDisabled() *orgv1.CheCluster {
return &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: orgv1.CheClusterSpec{
ImagePuller: orgv1.CheClusterSpecImagePuller{
Enable: false,
},
},
}
}
func InitCheCRWithImagePullerEnabledAndDefaultValuesSet() *orgv1.CheCluster {
return &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Finalizers: []string{
"kubernetesimagepullers.finalizers.che.eclipse.org",
},
},
Spec: orgv1.CheClusterSpec{
ImagePuller: orgv1.CheClusterSpecImagePuller{
Enable: true,
Spec: chev1alpha1.KubernetesImagePullerSpec{
DeploymentName: "kubernetes-image-puller",
ConfigMapName: "k8s-image-puller",
},
},
},
}
}
func InitCheCRWithImagePullerEnabledAndNewValuesSet() *orgv1.CheCluster {
return &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Finalizers: []string{
"kubernetesimagepullers.finalizers.che.eclipse.org",
},
},
Spec: orgv1.CheClusterSpec{
ImagePuller: orgv1.CheClusterSpecImagePuller{
Enable: true,
Spec: chev1alpha1.KubernetesImagePullerSpec{
DeploymentName: "kubernetes-image-puller-trigger-update",
ConfigMapName: "k8s-image-puller-trigger-update",
},
},
},
}
}

View File

@ -13,6 +13,7 @@ package che
import (
"context"
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
"github.com/eclipse/che-operator/pkg/util"
"github.com/sirupsen/logrus"

View File

@ -15,6 +15,7 @@ package deploy
import (
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/discovery"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@ -25,23 +26,25 @@ type ProvisioningStatus struct {
}
type DeployContext struct {
CheCluster *orgv1.CheCluster
ClusterAPI ClusterAPI
Proxy *Proxy
CheCluster *orgv1.CheCluster
ClusterAPI ClusterAPI
Proxy *Proxy
InternalService InternalService
DefaultCheHost string
DefaultCheHost string
}
type InternalService struct {
KeycloakHost string
KeycloakHost string
DevfileRegistryHost string
PluginRegistryHost string
CheHost string
PluginRegistryHost string
CheHost string
}
type ClusterAPI struct {
Client client.Client
Scheme *runtime.Scheme
Client client.Client
NonCachedClient client.Client
DiscoveryClient discovery.DiscoveryInterface
Scheme *runtime.Scheme
}
type Proxy struct {

View File

@ -85,6 +85,8 @@ const (
DefaultSecurityContextFsGroup = "1724"
DefaultSecurityContextRunAsUser = "1724"
KubernetesImagePullerOperatorCSV = "kubernetes-imagepuller-operator.v0.0.4"
DefaultServerExposureStrategy = "multi-host"
DefaultKubernetesSingleHostExposureType = "native"
DefaultOpenShiftSingleHostExposureType = "gateway"
@ -261,6 +263,10 @@ func DefaultSingleHostGatewayConfigSidecarImage(cr *orgv1.CheCluster) string {
return patchDefaultImageName(cr, defaultSingleHostGatewayConfigSidecarImage)
}
func DefaultKubernetesImagePullerOperatorCSV() string {
return KubernetesImagePullerOperatorCSV
}
func DefaultPullPolicyFromDockerImage(dockerImage string) string {
tag := "latest"
parts := strings.Split(dockerImage, ":")

View File

@ -0,0 +1,488 @@
package deploy
import (
"context"
"time"
chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/pkg/apis/che/v1alpha1"
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
"github.com/eclipse/che-operator/pkg/util"
operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
packagesv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var imagePullerFinalizerName = "kubernetesimagepullers.finalizers.che.eclipse.org"
// Reconcile the imagePuller section of the CheCluster CR. If imagePuller.enable is set to true, install the Kubernetes Image Puller operator and create
// a KubernetesImagePuller CR. Add a finalizer to the CheCluster CR. If false, remove the KubernetesImagePuller CR, uninstall the operator, and remove the finalizer.
func ReconcileImagePuller(ctx *DeployContext) (reconcile.Result, error) {
// Determine what server groups the API Server knows about
foundPackagesAPI, foundOperatorsAPI, _, err := CheckNeededImagePullerApis(ctx)
if err != nil {
logrus.Errorf("Error discovering image puller APIs: %v", err)
return reconcile.Result{}, err
}
// If the image puller should be installed but the APIServer doesn't know about PackageManifests/Subscriptions, log a warning and requeue
if ctx.CheCluster.Spec.ImagePuller.Enable && (!foundPackagesAPI || !foundOperatorsAPI) {
logrus.Infof("Couldn't find Operator Lifecycle Manager types to install the Kubernetes Image Puller Operator. Please install Operator Lifecycle Manager to install the operator or disable the image puller by setting spec.imagePuller.enable to false.")
return reconcile.Result{Requeue: true}, nil
}
if ctx.CheCluster.Spec.ImagePuller.Enable {
if foundOperatorsAPI && foundPackagesAPI {
packageManifest, err := GetPackageManifest(ctx)
if err != nil {
if errors.IsNotFound(err) {
logrus.Infof("There is no PackageManifest for the Kubernetes Image Puller Operator. Install the Operator Lifecycle Manager and the Community Operators Catalog")
return reconcile.Result{Requeue: true}, nil
}
logrus.Errorf("Error getting packagemanifest: %v", err)
return reconcile.Result{}, err
}
createdOperatorGroup, err := CreateOperatorGroupIfNotFound(ctx)
if err != nil {
logrus.Infof("Error creating OperatorGroup: %v", err)
return reconcile.Result{}, err
}
if createdOperatorGroup {
return reconcile.Result{Requeue: true}, nil
}
createdOperatorSubscription, err := CreateImagePullerSubscription(ctx, packageManifest)
if err != nil {
logrus.Infof("Error creating Subscription: %v", err)
return reconcile.Result{}, err
}
if createdOperatorSubscription {
return reconcile.Result{Requeue: true}, nil
}
subscriptionsAreEqual, err := CompareExpectedSubscription(ctx, packageManifest)
if err != nil {
logrus.Infof("Error checking Subscription equality: %v", err)
return reconcile.Result{}, nil
}
// If the Subscription Spec changed for some reason, update it
if !subscriptionsAreEqual {
updatedOperatorSubscription := GetExpectedSubscription(ctx, packageManifest)
logrus.Infof("Updating Subscription")
err = ctx.ClusterAPI.NonCachedClient.Update(context.TODO(), updatedOperatorSubscription, &client.UpdateOptions{})
if err != nil {
logrus.Errorf("Error updating Subscription: %v", err)
return reconcile.Result{}, err
}
return reconcile.Result{Requeue: true}, nil
}
// Add the image puller finalizer
if !HasImagePullerFinalizer(ctx.CheCluster) {
if err := ReconcileImagePullerFinalizer(ctx); err != nil {
return reconcile.Result{}, err
}
return reconcile.Result{Requeue: true}, nil
}
}
_, _, foundKubernetesImagePullerAPI, err := CheckNeededImagePullerApis(ctx)
if err != nil {
logrus.Errorf("Error discovering image puller APIs: %v", err)
return reconcile.Result{}, err
}
// If the KubernetesImagePuller API service exists, attempt to reconcile creation/update
if foundKubernetesImagePullerAPI {
// Check KubernetesImagePuller options
imagePuller := &chev1alpha1.KubernetesImagePuller{}
err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: ctx.CheCluster.Name + "-image-puller"}, imagePuller)
if err != nil {
if errors.IsNotFound(err) {
// If the image puller spec is empty, set default values, update the CheCluster CR and requeue
// These assignments are needed because the image puller operator updates the CR with a default configmap and deployment name
// if none are given. Without these, che-operator will be stuck in an update loop
if ctx.CheCluster.IsImagePullerSpecEmpty() {
logrus.Infof("Updating CheCluster to set KubernetesImagePuller default values")
_, err := UpdateImagePullerSpecIfEmpty(ctx)
if err != nil {
logrus.Errorf("Error updating CheCluster: %v", err)
return reconcile.Result{}, err
}
return reconcile.Result{Requeue: true}, nil
}
logrus.Infof("Creating KubernetesImagePuller for CheCluster %v", ctx.CheCluster.Name)
createdImagePuller, err := CreateKubernetesImagePuller(ctx)
if err != nil {
logrus.Error("Error creating KubernetesImagePuller: ", err)
return reconcile.Result{}, err
}
if createdImagePuller {
return reconcile.Result{}, nil
}
}
logrus.Errorf("Error getting KubernetesImagePuller: %v", err)
return reconcile.Result{}, err
}
// If ImagePuller specs are different, update the KubernetesImagePuller CR
if imagePuller.Spec != ctx.CheCluster.Spec.ImagePuller.Spec {
imagePuller.Spec = ctx.CheCluster.Spec.ImagePuller.Spec
logrus.Infof("Updating KubernetesImagePuller %v", imagePuller.Name)
if err = ctx.ClusterAPI.Client.Update(context.TODO(), imagePuller, &client.UpdateOptions{}); err != nil {
logrus.Errorf("Error updating KubernetesImagePuller: %v", err)
return reconcile.Result{}, err
}
return reconcile.Result{Requeue: true}, nil
}
} else {
logrus.Infof("Waiting 15 seconds for kubernetesimagepullers.che.eclipse.org API")
return reconcile.Result{RequeueAfter: 15 * time.Second}, nil
}
} else {
if foundOperatorsAPI && foundPackagesAPI {
removed, err := UninstallImagePullerOperator(ctx)
if err != nil {
logrus.Errorf("Error uninstalling Image Puller: %v", err)
return reconcile.Result{}, err
}
if removed {
return reconcile.Result{Requeue: true}, nil
}
if HasImagePullerFinalizer(ctx.CheCluster) {
err = DeleteImagePullerFinalizer(ctx)
if err != nil {
logrus.Errorf("Error deleting finalizer: %v", err)
return reconcile.Result{}, err
}
return reconcile.Result{Requeue: true}, nil
}
}
}
return reconcile.Result{}, nil
}
func HasImagePullerFinalizer(instance *orgv1.CheCluster) bool {
finalizers := instance.ObjectMeta.GetFinalizers()
for _, finalizer := range finalizers {
if finalizer == imagePullerFinalizerName {
return true
}
}
return false
}
func ReconcileImagePullerFinalizer(ctx *DeployContext) (err error) {
instance := ctx.CheCluster
if instance.ObjectMeta.DeletionTimestamp.IsZero() {
if !util.ContainsString(instance.ObjectMeta.Finalizers, imagePullerFinalizerName) {
ctx.CheCluster.ObjectMeta.Finalizers = append(instance.ObjectMeta.Finalizers, imagePullerFinalizerName)
logrus.Infof("Adding finalizer %v", imagePullerFinalizerName)
if err := ctx.ClusterAPI.Client.Update(context.Background(), instance); err != nil {
return err
}
}
} else {
if util.ContainsString(instance.ObjectMeta.Finalizers, imagePullerFinalizerName) {
clusterServiceVersionName := DefaultKubernetesImagePullerOperatorCSV()
logrus.Infof("Custom resource %s is being deleted. Deleting ClusterServiceVersion %s first", instance.Name, clusterServiceVersionName)
clusterServiceVersion := &operatorsv1alpha1.ClusterServiceVersion{}
err := ctx.ClusterAPI.NonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: instance.Namespace, Name: clusterServiceVersionName}, clusterServiceVersion)
if err != nil {
logrus.Errorf("Error getting ClusterServiceVersion: %v", err)
return err
}
if err := ctx.ClusterAPI.Client.Delete(context.TODO(), clusterServiceVersion); err != nil {
logrus.Errorf("Failed to delete %s ClusterServiceVersion: %s", clusterServiceVersionName, err)
return err
}
instance.ObjectMeta.Finalizers = util.DoRemoveString(instance.ObjectMeta.Finalizers, imagePullerFinalizerName)
logrus.Infof("Updating %s CR", instance.Name)
if err := ctx.ClusterAPI.Client.Update(context.Background(), instance); err != nil {
logrus.Errorf("Failed to update %s CR: %s", instance.Name, err)
return err
}
}
return nil
}
return nil
}
func DeleteImagePullerFinalizer(ctx *DeployContext) (err error) {
instance := ctx.CheCluster
instance.ObjectMeta.Finalizers = util.DoRemoveString(instance.ObjectMeta.Finalizers, imagePullerFinalizerName)
logrus.Infof("Removing image puller finalizer on %s CR", instance.Name)
if err := ctx.ClusterAPI.Client.Update(context.Background(), instance); err != nil {
logrus.Errorf("Failed to update %s CR: %s", instance.Name, err)
return err
}
return nil
}
// Returns true if the expected and actual Subscription specs have the same fields during Image Puller
// installation
func SubscriptionsAreEqual(expected *operatorsv1alpha1.Subscription, actual *operatorsv1alpha1.Subscription) bool {
return expected.Spec.CatalogSource == actual.Spec.CatalogSource &&
expected.Spec.CatalogSourceNamespace == actual.Spec.CatalogSourceNamespace &&
expected.Spec.Channel == actual.Spec.Channel &&
expected.Spec.InstallPlanApproval == actual.Spec.InstallPlanApproval &&
expected.Spec.Package == actual.Spec.Package
}
// Check if the API server can discover the API groups for packages.operators.coreos.com,
// operators.coreos.com, and che.eclipse.org.
// Returns:
// foundPackagesAPI - true if the server discovers the packages.operators.coreos.com API
// foundOperatorsAPI - true if the server discovers the operators.coreos.com API
// foundKubernetesImagePullerAPI - true if the server discovers the che.eclipse.org API
// error - any error returned by the call to discoveryClient.ServerGroups()
func CheckNeededImagePullerApis(ctx *DeployContext) (bool, bool, bool, error) {
groupList, err := ctx.ClusterAPI.DiscoveryClient.ServerGroups()
if err != nil {
return false, false, false, err
}
groups := groupList.Groups
foundPackagesAPI := false
foundOperatorsAPI := false
foundKubernetesImagePullerAPI := false
for _, group := range groups {
if group.Name == packagesv1.SchemeGroupVersion.Group {
foundPackagesAPI = true
}
if group.Name == operatorsv1alpha1.SchemeGroupVersion.Group {
foundOperatorsAPI = true
}
if group.Name == chev1alpha1.SchemeGroupVersion.Group {
foundKubernetesImagePullerAPI = true
}
}
return foundPackagesAPI, foundOperatorsAPI, foundKubernetesImagePullerAPI, nil
}
// Search for the kubernetes-imagepuller-operator PackageManifest
func GetPackageManifest(ctx *DeployContext) (*packagesv1.PackageManifest, error) {
packageManifest := &packagesv1.PackageManifest{}
err := ctx.ClusterAPI.NonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: "kubernetes-imagepuller-operator"}, packageManifest)
if err != nil {
return packageManifest, err
}
return packageManifest, nil
}
// Create an OperatorGroup in the CheCluster namespace if it does not exist. Returns true if the
// OperatorGroup was created, and any error returned during the List and Create operation
func CreateOperatorGroupIfNotFound(ctx *DeployContext) (bool, error) {
operatorGroupList := &operatorsv1.OperatorGroupList{}
err := ctx.ClusterAPI.NonCachedClient.List(context.TODO(), operatorGroupList, &client.ListOptions{Namespace: ctx.CheCluster.Namespace})
if err != nil {
return false, err
}
if len(operatorGroupList.Items) == 0 {
operatorGroup := &operatorsv1.OperatorGroup{
ObjectMeta: metav1.ObjectMeta{
Name: "kubernetes-imagepuller-operator",
Namespace: ctx.CheCluster.Namespace,
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(ctx.CheCluster, ctx.CheCluster.GroupVersionKind()),
},
},
Spec: operatorsv1.OperatorGroupSpec{
TargetNamespaces: []string{
ctx.CheCluster.Namespace,
},
},
}
logrus.Infof("Creating kubernetes image puller OperatorGroup")
if err = ctx.ClusterAPI.NonCachedClient.Create(context.TODO(), operatorGroup, &client.CreateOptions{}); err != nil {
return false, err
}
return true, nil
}
return false, nil
}
func CreateImagePullerSubscription(ctx *DeployContext, packageManifest *packagesv1.PackageManifest) (bool, error) {
imagePullerOperatorSubscription := &operatorsv1alpha1.Subscription{}
err := ctx.ClusterAPI.NonCachedClient.Get(context.TODO(), types.NamespacedName{
Name: "kubernetes-imagepuller-operator",
Namespace: ctx.CheCluster.Namespace,
}, imagePullerOperatorSubscription)
if err != nil {
if errors.IsNotFound(err) {
logrus.Info("Creating kubernetes image puller operator Subscription")
err = ctx.ClusterAPI.NonCachedClient.Create(context.TODO(), GetExpectedSubscription(ctx, packageManifest), &client.CreateOptions{})
if err != nil {
return false, err
}
return true, nil
}
return false, err
}
return false, nil
}
func GetExpectedSubscription(ctx *DeployContext, packageManifest *packagesv1.PackageManifest) *operatorsv1alpha1.Subscription {
return &operatorsv1alpha1.Subscription{
ObjectMeta: metav1.ObjectMeta{
Name: "kubernetes-imagepuller-operator",
Namespace: ctx.CheCluster.Namespace,
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(ctx.CheCluster, ctx.CheCluster.GroupVersionKind()),
},
},
Spec: &operatorsv1alpha1.SubscriptionSpec{
CatalogSource: packageManifest.Status.CatalogSource,
CatalogSourceNamespace: packageManifest.Status.CatalogSourceNamespace,
Channel: packageManifest.Status.DefaultChannel,
InstallPlanApproval: operatorsv1alpha1.ApprovalAutomatic,
Package: "kubernetes-imagepuller-operator",
},
}
}
func CompareExpectedSubscription(ctx *DeployContext, packageManifest *packagesv1.PackageManifest) (bool, error) {
expected := GetExpectedSubscription(ctx, packageManifest)
actual := &operatorsv1alpha1.Subscription{}
err := ctx.ClusterAPI.NonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: "kubernetes-imagepuller-operator"}, actual)
if err != nil {
return false, err
}
return SubscriptionsAreEqual(expected, actual), nil
}
// Update the CheCluster ImagePuller spec if the default values are not set
// returns the updated spec and an error during update
func UpdateImagePullerSpecIfEmpty(ctx *DeployContext) (orgv1.CheClusterSpecImagePuller, error) {
if ctx.CheCluster.Spec.ImagePuller.Spec.DeploymentName == "" {
ctx.CheCluster.Spec.ImagePuller.Spec.DeploymentName = "kubernetes-image-puller"
}
if ctx.CheCluster.Spec.ImagePuller.Spec.ConfigMapName == "" {
ctx.CheCluster.Spec.ImagePuller.Spec.ConfigMapName = "k8s-image-puller"
}
err := ctx.ClusterAPI.Client.Update(context.TODO(), ctx.CheCluster, &client.UpdateOptions{})
if err != nil {
return ctx.CheCluster.Spec.ImagePuller, err
}
return ctx.CheCluster.Spec.ImagePuller, nil
}
func CreateKubernetesImagePuller(ctx *DeployContext) (bool, error) {
imagePuller := GetExpectedKubernetesImagePuller(ctx)
err := ctx.ClusterAPI.Client.Create(context.TODO(), imagePuller, &client.CreateOptions{})
if err != nil {
return false, err
}
return true, nil
}
func GetExpectedKubernetesImagePuller(ctx *DeployContext) *chev1alpha1.KubernetesImagePuller {
return &chev1alpha1.KubernetesImagePuller{
ObjectMeta: metav1.ObjectMeta{
Name: ctx.CheCluster.Name + "-image-puller",
Namespace: ctx.CheCluster.Namespace,
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(ctx.CheCluster, ctx.CheCluster.GroupVersionKind()),
},
Labels: map[string]string{
"app.kubernetes.io/part-of": ctx.CheCluster.Name,
"app": "che",
"component": "kubernetes-image-puller",
},
},
Spec: ctx.CheCluster.Spec.ImagePuller.Spec,
}
}
// Unisntall the CSV, OperatorGroup, Subscription, KubernetesImagePuller, and update the CheCluster to remove
// the image puller spec. Returns true if the CheCluster was updated
func UninstallImagePullerOperator(ctx *DeployContext) (bool, error) {
updated := false
_, hasOperatorsAPIs, hasImagePullerAPIs, err := CheckNeededImagePullerApis(ctx)
if err != nil {
return updated, err
}
if hasImagePullerAPIs {
// Delete the KubernetesImagePuller
imagePuller := &chev1alpha1.KubernetesImagePuller{}
err := ctx.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: ctx.CheCluster.Name + "-image-puller"}, imagePuller)
if err != nil && !errors.IsNotFound(err) {
return updated, err
}
if imagePuller.Name != "" {
logrus.Infof("Deleting KubernetesImagePuller %v", imagePuller.Name)
if err = ctx.ClusterAPI.Client.Delete(context.TODO(), imagePuller, &client.DeleteOptions{}); err != nil && !errors.IsNotFound(err) {
return updated, err
}
}
}
if hasOperatorsAPIs {
// Delete the ClusterServiceVersion
csv := &operatorsv1alpha1.ClusterServiceVersion{}
err = ctx.ClusterAPI.NonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: DefaultKubernetesImagePullerOperatorCSV()}, csv)
if err != nil && !errors.IsNotFound(err) {
return updated, err
}
if csv.Name != "" {
logrus.Infof("Deleting ClusterServiceVersion %v", csv.Name)
err := ctx.ClusterAPI.NonCachedClient.Delete(context.TODO(), csv, &client.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
return updated, err
}
}
// Delete the Subscription
subscription := &operatorsv1alpha1.Subscription{}
err = ctx.ClusterAPI.NonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: "kubernetes-imagepuller-operator"}, subscription)
if err != nil && !errors.IsNotFound(err) {
return updated, err
}
if subscription.Name != "" {
logrus.Infof("Deleting Subscription %v", subscription.Name)
err := ctx.ClusterAPI.NonCachedClient.Delete(context.TODO(), subscription, &client.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
return updated, err
}
}
// Delete the OperatorGroup if it was created
operatorGroup := &operatorsv1.OperatorGroup{}
err = ctx.ClusterAPI.NonCachedClient.Get(context.TODO(), types.NamespacedName{Namespace: ctx.CheCluster.Namespace, Name: "kubernetes-imagepuller-operator"}, operatorGroup)
if err != nil && !errors.IsNotFound(err) {
return updated, err
}
if operatorGroup.Name != "" {
logrus.Infof("Deleting OperatorGroup %v", operatorGroup.Name)
err := ctx.ClusterAPI.NonCachedClient.Delete(context.TODO(), operatorGroup, &client.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
return updated, err
}
}
}
// Update CR to remove imagePullerSpec
if ctx.CheCluster.Spec.ImagePuller.Enable || ctx.CheCluster.Spec.ImagePuller.Spec != (chev1alpha1.KubernetesImagePullerSpec{}) {
ctx.CheCluster.Spec.ImagePuller.Spec = chev1alpha1.KubernetesImagePullerSpec{}
logrus.Infof("Updating CheCluster %v to remove image puller spec", ctx.CheCluster.Name)
err := ctx.ClusterAPI.Client.Update(context.TODO(), ctx.CheCluster, &client.UpdateOptions{})
if err != nil {
return updated, err
}
updated = true
}
return updated, nil
}

View File

@ -172,7 +172,7 @@ func GetCheConfigMapData(deployContext *deploy.DeployContext) (cheEnv map[string
keycloakClientId := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public")
ingressStrategy := util.GetServerExposureStrategy(deployContext.CheCluster, deploy.DefaultServerExposureStrategy)
ingressClass := util.GetValue(deployContext.CheCluster.Spec.K8s.IngressClass, deploy.DefaultIngressClass)
devfileRegistryURL:= deployContext.CheCluster.Status.DevfileRegistryURL
devfileRegistryURL := deployContext.CheCluster.Status.DevfileRegistryURL
pluginRegistryURL := deployContext.CheCluster.Status.PluginRegistryURL
cheLogLevel := util.GetValue(deployContext.CheCluster.Spec.Server.CheLogLevel, deploy.DefaultCheLogLevel)
cheDebug := util.GetValue(deployContext.CheCluster.Spec.Server.CheDebug, deploy.DefaultCheDebug)
@ -203,7 +203,7 @@ func GetCheConfigMapData(deployContext *deploy.DeployContext) (cheEnv map[string
CheHost: cheHost,
ChePort: "8080",
CheApi: cheAPI,
CheApiInternal: cheInternalAPI,
CheApiInternal: cheInternalAPI,
CheWebSocketEndpoint: wsprotocol + "://" + cheHost + "/api/websocket",
WebSocketEndpointMinor: wsprotocol + "://" + cheHost + "/api/websocket-minor",
CheDebugServer: cheDebug,

21
vendor/github.com/blang/semver/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,21 @@
language: go
matrix:
include:
- go: 1.4.3
- go: 1.5.4
- go: 1.6.3
- go: 1.7
- go: tip
allow_failures:
- go: tip
install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
script:
- echo "Test and track coverage" ; $HOME/gopath/bin/goveralls -package "." -service=travis-ci
-repotoken $COVERALLS_TOKEN
- echo "Build examples" ; cd examples && go build
- echo "Check if gofmt'd" ; diff -u <(echo -n) <(gofmt -d -s .)
env:
global:
secure: HroGEAUQpVq9zX1b1VIkraLiywhGbzvNnTZq2TMxgK7JHP8xqNplAeF1izrR2i4QLL9nsY+9WtYss4QuPvEtZcVHUobw6XnL6radF7jS1LgfYZ9Y7oF+zogZ2I5QUMRLGA7rcxQ05s7mKq3XZQfeqaNts4bms/eZRefWuaFZbkw=

22
vendor/github.com/blang/semver/LICENSE generated vendored Normal file
View File

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

194
vendor/github.com/blang/semver/README.md generated vendored Normal file
View File

@ -0,0 +1,194 @@
semver for golang [![Build Status](https://travis-ci.org/blang/semver.svg?branch=master)](https://travis-ci.org/blang/semver) [![GoDoc](https://godoc.org/github.com/blang/semver?status.png)](https://godoc.org/github.com/blang/semver) [![Coverage Status](https://img.shields.io/coveralls/blang/semver.svg)](https://coveralls.io/r/blang/semver?branch=master)
======
semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`.
Usage
-----
```bash
$ go get github.com/blang/semver
```
Note: Always vendor your dependencies or fix on a specific version tag.
```go
import github.com/blang/semver
v1, err := semver.Make("1.0.0-beta")
v2, err := semver.Make("2.0.0-beta")
v1.Compare(v2)
```
Also check the [GoDocs](http://godoc.org/github.com/blang/semver).
Why should I use this lib?
-----
- Fully spec compatible
- No reflection
- No regex
- Fully tested (Coverage >99%)
- Readable parsing/validation errors
- Fast (See [Benchmarks](#benchmarks))
- Only Stdlib
- Uses values instead of pointers
- Many features, see below
Features
-----
- Parsing and validation at all levels
- Comparator-like comparisons
- Compare Helper Methods
- InPlace manipulation
- Ranges `>=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1`
- Wildcards `>=1.x`, `<=2.5.x`
- Sortable (implements sort.Interface)
- database/sql compatible (sql.Scanner/Valuer)
- encoding/json compatible (json.Marshaler/Unmarshaler)
Ranges
------
A `Range` is a set of conditions which specify which versions satisfy the range.
A condition is composed of an operator and a version. The supported operators are:
- `<1.0.0` Less than `1.0.0`
- `<=1.0.0` Less than or equal to `1.0.0`
- `>1.0.0` Greater than `1.0.0`
- `>=1.0.0` Greater than or equal to `1.0.0`
- `1.0.0`, `=1.0.0`, `==1.0.0` Equal to `1.0.0`
- `!1.0.0`, `!=1.0.0` Not equal to `1.0.0`. Excludes version `1.0.0`.
Note that spaces between the operator and the version will be gracefully tolerated.
A `Range` can link multiple `Ranges` separated by space:
Ranges can be linked by logical AND:
- `>1.0.0 <2.0.0` would match between both ranges, so `1.1.1` and `1.8.7` but not `1.0.0` or `2.0.0`
- `>1.0.0 <3.0.0 !2.0.3-beta.2` would match every version between `1.0.0` and `3.0.0` except `2.0.3-beta.2`
Ranges can also be linked by logical OR:
- `<2.0.0 || >=3.0.0` would match `1.x.x` and `3.x.x` but not `2.x.x`
AND has a higher precedence than OR. It's not possible to use brackets.
Ranges can be combined by both AND and OR
- `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
Range usage:
```
v, err := semver.Parse("1.2.3")
range, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0")
if range(v) {
//valid
}
```
Example
-----
Have a look at full examples in [examples/main.go](examples/main.go)
```go
import github.com/blang/semver
v, err := semver.Make("0.0.1-alpha.preview+123.github")
fmt.Printf("Major: %d\n", v.Major)
fmt.Printf("Minor: %d\n", v.Minor)
fmt.Printf("Patch: %d\n", v.Patch)
fmt.Printf("Pre: %s\n", v.Pre)
fmt.Printf("Build: %s\n", v.Build)
// Prerelease versions array
if len(v.Pre) > 0 {
fmt.Println("Prerelease versions:")
for i, pre := range v.Pre {
fmt.Printf("%d: %q\n", i, pre)
}
}
// Build meta data array
if len(v.Build) > 0 {
fmt.Println("Build meta data:")
for i, build := range v.Build {
fmt.Printf("%d: %q\n", i, build)
}
}
v001, err := semver.Make("0.0.1")
// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE
v001.GT(v) == true
v.LT(v001) == true
v.GTE(v) == true
v.LTE(v) == true
// Or use v.Compare(v2) for comparisons (-1, 0, 1):
v001.Compare(v) == 1
v.Compare(v001) == -1
v.Compare(v) == 0
// Manipulate Version in place:
v.Pre[0], err = semver.NewPRVersion("beta")
if err != nil {
fmt.Printf("Error parsing pre release version: %q", err)
}
fmt.Println("\nValidate versions:")
v.Build[0] = "?"
err = v.Validate()
if err != nil {
fmt.Printf("Validation failed: %s\n", err)
}
```
Benchmarks
-----
BenchmarkParseSimple-4 5000000 390 ns/op 48 B/op 1 allocs/op
BenchmarkParseComplex-4 1000000 1813 ns/op 256 B/op 7 allocs/op
BenchmarkParseAverage-4 1000000 1171 ns/op 163 B/op 4 allocs/op
BenchmarkStringSimple-4 20000000 119 ns/op 16 B/op 1 allocs/op
BenchmarkStringLarger-4 10000000 206 ns/op 32 B/op 2 allocs/op
BenchmarkStringComplex-4 5000000 324 ns/op 80 B/op 3 allocs/op
BenchmarkStringAverage-4 5000000 273 ns/op 53 B/op 2 allocs/op
BenchmarkValidateSimple-4 200000000 9.33 ns/op 0 B/op 0 allocs/op
BenchmarkValidateComplex-4 3000000 469 ns/op 0 B/op 0 allocs/op
BenchmarkValidateAverage-4 5000000 256 ns/op 0 B/op 0 allocs/op
BenchmarkCompareSimple-4 100000000 11.8 ns/op 0 B/op 0 allocs/op
BenchmarkCompareComplex-4 50000000 30.8 ns/op 0 B/op 0 allocs/op
BenchmarkCompareAverage-4 30000000 41.5 ns/op 0 B/op 0 allocs/op
BenchmarkSort-4 3000000 419 ns/op 256 B/op 2 allocs/op
BenchmarkRangeParseSimple-4 2000000 850 ns/op 192 B/op 5 allocs/op
BenchmarkRangeParseAverage-4 1000000 1677 ns/op 400 B/op 10 allocs/op
BenchmarkRangeParseComplex-4 300000 5214 ns/op 1440 B/op 30 allocs/op
BenchmarkRangeMatchSimple-4 50000000 25.6 ns/op 0 B/op 0 allocs/op
BenchmarkRangeMatchAverage-4 30000000 56.4 ns/op 0 B/op 0 allocs/op
BenchmarkRangeMatchComplex-4 10000000 153 ns/op 0 B/op 0 allocs/op
See benchmark cases at [semver_test.go](semver_test.go)
Motivation
-----
I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like.
Contribution
-----
Feel free to make a pull request. For bigger changes create a issue first to discuss about it.
License
-----
See [LICENSE](LICENSE) file.

23
vendor/github.com/blang/semver/json.go generated vendored Normal file
View File

@ -0,0 +1,23 @@
package semver
import (
"encoding/json"
)
// MarshalJSON implements the encoding/json.Marshaler interface.
func (v Version) MarshalJSON() ([]byte, error) {
return json.Marshal(v.String())
}
// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
func (v *Version) UnmarshalJSON(data []byte) (err error) {
var versionString string
if err = json.Unmarshal(data, &versionString); err != nil {
return
}
*v, err = Parse(versionString)
return
}

17
vendor/github.com/blang/semver/package.json generated vendored Normal file
View File

@ -0,0 +1,17 @@
{
"author": "blang",
"bugs": {
"URL": "https://github.com/blang/semver/issues",
"url": "https://github.com/blang/semver/issues"
},
"gx": {
"dvcsimport": "github.com/blang/semver"
},
"gxVersion": "0.10.0",
"language": "go",
"license": "MIT",
"name": "semver",
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
"version": "3.5.1"
}

416
vendor/github.com/blang/semver/range.go generated vendored Normal file
View File

@ -0,0 +1,416 @@
package semver
import (
"fmt"
"strconv"
"strings"
"unicode"
)
type wildcardType int
const (
noneWildcard wildcardType = iota
majorWildcard wildcardType = 1
minorWildcard wildcardType = 2
patchWildcard wildcardType = 3
)
func wildcardTypefromInt(i int) wildcardType {
switch i {
case 1:
return majorWildcard
case 2:
return minorWildcard
case 3:
return patchWildcard
default:
return noneWildcard
}
}
type comparator func(Version, Version) bool
var (
compEQ comparator = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) == 0
}
compNE = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) != 0
}
compGT = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) == 1
}
compGE = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) >= 0
}
compLT = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) == -1
}
compLE = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) <= 0
}
)
type versionRange struct {
v Version
c comparator
}
// rangeFunc creates a Range from the given versionRange.
func (vr *versionRange) rangeFunc() Range {
return Range(func(v Version) bool {
return vr.c(v, vr.v)
})
}
// Range represents a range of versions.
// A Range can be used to check if a Version satisfies it:
//
// range, err := semver.ParseRange(">1.0.0 <2.0.0")
// range(semver.MustParse("1.1.1") // returns true
type Range func(Version) bool
// OR combines the existing Range with another Range using logical OR.
func (rf Range) OR(f Range) Range {
return Range(func(v Version) bool {
return rf(v) || f(v)
})
}
// AND combines the existing Range with another Range using logical AND.
func (rf Range) AND(f Range) Range {
return Range(func(v Version) bool {
return rf(v) && f(v)
})
}
// ParseRange parses a range and returns a Range.
// If the range could not be parsed an error is returned.
//
// Valid ranges are:
// - "<1.0.0"
// - "<=1.0.0"
// - ">1.0.0"
// - ">=1.0.0"
// - "1.0.0", "=1.0.0", "==1.0.0"
// - "!1.0.0", "!=1.0.0"
//
// A Range can consist of multiple ranges separated by space:
// Ranges can be linked by logical AND:
// - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0"
// - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2
//
// Ranges can also be linked by logical OR:
// - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x"
//
// AND has a higher precedence than OR. It's not possible to use brackets.
//
// Ranges can be combined by both AND and OR
//
// - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
func ParseRange(s string) (Range, error) {
parts := splitAndTrim(s)
orParts, err := splitORParts(parts)
if err != nil {
return nil, err
}
expandedParts, err := expandWildcardVersion(orParts)
if err != nil {
return nil, err
}
var orFn Range
for _, p := range expandedParts {
var andFn Range
for _, ap := range p {
opStr, vStr, err := splitComparatorVersion(ap)
if err != nil {
return nil, err
}
vr, err := buildVersionRange(opStr, vStr)
if err != nil {
return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err)
}
rf := vr.rangeFunc()
// Set function
if andFn == nil {
andFn = rf
} else { // Combine with existing function
andFn = andFn.AND(rf)
}
}
if orFn == nil {
orFn = andFn
} else {
orFn = orFn.OR(andFn)
}
}
return orFn, nil
}
// splitORParts splits the already cleaned parts by '||'.
// Checks for invalid positions of the operator and returns an
// error if found.
func splitORParts(parts []string) ([][]string, error) {
var ORparts [][]string
last := 0
for i, p := range parts {
if p == "||" {
if i == 0 {
return nil, fmt.Errorf("First element in range is '||'")
}
ORparts = append(ORparts, parts[last:i])
last = i + 1
}
}
if last == len(parts) {
return nil, fmt.Errorf("Last element in range is '||'")
}
ORparts = append(ORparts, parts[last:])
return ORparts, nil
}
// buildVersionRange takes a slice of 2: operator and version
// and builds a versionRange, otherwise an error.
func buildVersionRange(opStr, vStr string) (*versionRange, error) {
c := parseComparator(opStr)
if c == nil {
return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, ""))
}
v, err := Parse(vStr)
if err != nil {
return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err)
}
return &versionRange{
v: v,
c: c,
}, nil
}
// inArray checks if a byte is contained in an array of bytes
func inArray(s byte, list []byte) bool {
for _, el := range list {
if el == s {
return true
}
}
return false
}
// splitAndTrim splits a range string by spaces and cleans whitespaces
func splitAndTrim(s string) (result []string) {
last := 0
var lastChar byte
excludeFromSplit := []byte{'>', '<', '='}
for i := 0; i < len(s); i++ {
if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) {
if last < i-1 {
result = append(result, s[last:i])
}
last = i + 1
} else if s[i] != ' ' {
lastChar = s[i]
}
}
if last < len(s)-1 {
result = append(result, s[last:])
}
for i, v := range result {
result[i] = strings.Replace(v, " ", "", -1)
}
// parts := strings.Split(s, " ")
// for _, x := range parts {
// if s := strings.TrimSpace(x); len(s) != 0 {
// result = append(result, s)
// }
// }
return
}
// splitComparatorVersion splits the comparator from the version.
// Input must be free of leading or trailing spaces.
func splitComparatorVersion(s string) (string, string, error) {
i := strings.IndexFunc(s, unicode.IsDigit)
if i == -1 {
return "", "", fmt.Errorf("Could not get version from string: %q", s)
}
return strings.TrimSpace(s[0:i]), s[i:], nil
}
// getWildcardType will return the type of wildcard that the
// passed version contains
func getWildcardType(vStr string) wildcardType {
parts := strings.Split(vStr, ".")
nparts := len(parts)
wildcard := parts[nparts-1]
possibleWildcardType := wildcardTypefromInt(nparts)
if wildcard == "x" {
return possibleWildcardType
}
return noneWildcard
}
// createVersionFromWildcard will convert a wildcard version
// into a regular version, replacing 'x's with '0's, handling
// special cases like '1.x.x' and '1.x'
func createVersionFromWildcard(vStr string) string {
// handle 1.x.x
vStr2 := strings.Replace(vStr, ".x.x", ".x", 1)
vStr2 = strings.Replace(vStr2, ".x", ".0", 1)
parts := strings.Split(vStr2, ".")
// handle 1.x
if len(parts) == 2 {
return vStr2 + ".0"
}
return vStr2
}
// incrementMajorVersion will increment the major version
// of the passed version
func incrementMajorVersion(vStr string) (string, error) {
parts := strings.Split(vStr, ".")
i, err := strconv.Atoi(parts[0])
if err != nil {
return "", err
}
parts[0] = strconv.Itoa(i + 1)
return strings.Join(parts, "."), nil
}
// incrementMajorVersion will increment the minor version
// of the passed version
func incrementMinorVersion(vStr string) (string, error) {
parts := strings.Split(vStr, ".")
i, err := strconv.Atoi(parts[1])
if err != nil {
return "", err
}
parts[1] = strconv.Itoa(i + 1)
return strings.Join(parts, "."), nil
}
// expandWildcardVersion will expand wildcards inside versions
// following these rules:
//
// * when dealing with patch wildcards:
// >= 1.2.x will become >= 1.2.0
// <= 1.2.x will become < 1.3.0
// > 1.2.x will become >= 1.3.0
// < 1.2.x will become < 1.2.0
// != 1.2.x will become < 1.2.0 >= 1.3.0
//
// * when dealing with minor wildcards:
// >= 1.x will become >= 1.0.0
// <= 1.x will become < 2.0.0
// > 1.x will become >= 2.0.0
// < 1.0 will become < 1.0.0
// != 1.x will become < 1.0.0 >= 2.0.0
//
// * when dealing with wildcards without
// version operator:
// 1.2.x will become >= 1.2.0 < 1.3.0
// 1.x will become >= 1.0.0 < 2.0.0
func expandWildcardVersion(parts [][]string) ([][]string, error) {
var expandedParts [][]string
for _, p := range parts {
var newParts []string
for _, ap := range p {
if strings.Index(ap, "x") != -1 {
opStr, vStr, err := splitComparatorVersion(ap)
if err != nil {
return nil, err
}
versionWildcardType := getWildcardType(vStr)
flatVersion := createVersionFromWildcard(vStr)
var resultOperator string
var shouldIncrementVersion bool
switch opStr {
case ">":
resultOperator = ">="
shouldIncrementVersion = true
case ">=":
resultOperator = ">="
case "<":
resultOperator = "<"
case "<=":
resultOperator = "<"
shouldIncrementVersion = true
case "", "=", "==":
newParts = append(newParts, ">="+flatVersion)
resultOperator = "<"
shouldIncrementVersion = true
case "!=", "!":
newParts = append(newParts, "<"+flatVersion)
resultOperator = ">="
shouldIncrementVersion = true
}
var resultVersion string
if shouldIncrementVersion {
switch versionWildcardType {
case patchWildcard:
resultVersion, _ = incrementMinorVersion(flatVersion)
case minorWildcard:
resultVersion, _ = incrementMajorVersion(flatVersion)
}
} else {
resultVersion = flatVersion
}
ap = resultOperator + resultVersion
}
newParts = append(newParts, ap)
}
expandedParts = append(expandedParts, newParts)
}
return expandedParts, nil
}
func parseComparator(s string) comparator {
switch s {
case "==":
fallthrough
case "":
fallthrough
case "=":
return compEQ
case ">":
return compGT
case ">=":
return compGE
case "<":
return compLT
case "<=":
return compLE
case "!":
fallthrough
case "!=":
return compNE
}
return nil
}
// MustParseRange is like ParseRange but panics if the range cannot be parsed.
func MustParseRange(s string) Range {
r, err := ParseRange(s)
if err != nil {
panic(`semver: ParseRange(` + s + `): ` + err.Error())
}
return r
}

418
vendor/github.com/blang/semver/semver.go generated vendored Normal file
View File

@ -0,0 +1,418 @@
package semver
import (
"errors"
"fmt"
"strconv"
"strings"
)
const (
numbers string = "0123456789"
alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
alphanum = alphas + numbers
)
// SpecVersion is the latest fully supported spec version of semver
var SpecVersion = Version{
Major: 2,
Minor: 0,
Patch: 0,
}
// Version represents a semver compatible version
type Version struct {
Major uint64
Minor uint64
Patch uint64
Pre []PRVersion
Build []string //No Precendence
}
// Version to string
func (v Version) String() string {
b := make([]byte, 0, 5)
b = strconv.AppendUint(b, v.Major, 10)
b = append(b, '.')
b = strconv.AppendUint(b, v.Minor, 10)
b = append(b, '.')
b = strconv.AppendUint(b, v.Patch, 10)
if len(v.Pre) > 0 {
b = append(b, '-')
b = append(b, v.Pre[0].String()...)
for _, pre := range v.Pre[1:] {
b = append(b, '.')
b = append(b, pre.String()...)
}
}
if len(v.Build) > 0 {
b = append(b, '+')
b = append(b, v.Build[0]...)
for _, build := range v.Build[1:] {
b = append(b, '.')
b = append(b, build...)
}
}
return string(b)
}
// Equals checks if v is equal to o.
func (v Version) Equals(o Version) bool {
return (v.Compare(o) == 0)
}
// EQ checks if v is equal to o.
func (v Version) EQ(o Version) bool {
return (v.Compare(o) == 0)
}
// NE checks if v is not equal to o.
func (v Version) NE(o Version) bool {
return (v.Compare(o) != 0)
}
// GT checks if v is greater than o.
func (v Version) GT(o Version) bool {
return (v.Compare(o) == 1)
}
// GTE checks if v is greater than or equal to o.
func (v Version) GTE(o Version) bool {
return (v.Compare(o) >= 0)
}
// GE checks if v is greater than or equal to o.
func (v Version) GE(o Version) bool {
return (v.Compare(o) >= 0)
}
// LT checks if v is less than o.
func (v Version) LT(o Version) bool {
return (v.Compare(o) == -1)
}
// LTE checks if v is less than or equal to o.
func (v Version) LTE(o Version) bool {
return (v.Compare(o) <= 0)
}
// LE checks if v is less than or equal to o.
func (v Version) LE(o Version) bool {
return (v.Compare(o) <= 0)
}
// Compare compares Versions v to o:
// -1 == v is less than o
// 0 == v is equal to o
// 1 == v is greater than o
func (v Version) Compare(o Version) int {
if v.Major != o.Major {
if v.Major > o.Major {
return 1
}
return -1
}
if v.Minor != o.Minor {
if v.Minor > o.Minor {
return 1
}
return -1
}
if v.Patch != o.Patch {
if v.Patch > o.Patch {
return 1
}
return -1
}
// Quick comparison if a version has no prerelease versions
if len(v.Pre) == 0 && len(o.Pre) == 0 {
return 0
} else if len(v.Pre) == 0 && len(o.Pre) > 0 {
return 1
} else if len(v.Pre) > 0 && len(o.Pre) == 0 {
return -1
}
i := 0
for ; i < len(v.Pre) && i < len(o.Pre); i++ {
if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 {
continue
} else if comp == 1 {
return 1
} else {
return -1
}
}
// If all pr versions are the equal but one has further prversion, this one greater
if i == len(v.Pre) && i == len(o.Pre) {
return 0
} else if i == len(v.Pre) && i < len(o.Pre) {
return -1
} else {
return 1
}
}
// Validate validates v and returns error in case
func (v Version) Validate() error {
// Major, Minor, Patch already validated using uint64
for _, pre := range v.Pre {
if !pre.IsNum { //Numeric prerelease versions already uint64
if len(pre.VersionStr) == 0 {
return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr)
}
if !containsOnly(pre.VersionStr, alphanum) {
return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr)
}
}
}
for _, build := range v.Build {
if len(build) == 0 {
return fmt.Errorf("Build meta data can not be empty %q", build)
}
if !containsOnly(build, alphanum) {
return fmt.Errorf("Invalid character(s) found in build meta data %q", build)
}
}
return nil
}
// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
func New(s string) (vp *Version, err error) {
v, err := Parse(s)
vp = &v
return
}
// Make is an alias for Parse, parses version string and returns a validated Version or error
func Make(s string) (Version, error) {
return Parse(s)
}
// ParseTolerant allows for certain version specifications that do not strictly adhere to semver
// specs to be parsed by this library. It does so by normalizing versions before passing them to
// Parse(). It currently trims spaces, removes a "v" prefix, and adds a 0 patch number to versions
// with only major and minor components specified
func ParseTolerant(s string) (Version, error) {
s = strings.TrimSpace(s)
s = strings.TrimPrefix(s, "v")
// Split into major.minor.(patch+pr+meta)
parts := strings.SplitN(s, ".", 3)
if len(parts) < 3 {
if strings.ContainsAny(parts[len(parts)-1], "+-") {
return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data")
}
for len(parts) < 3 {
parts = append(parts, "0")
}
s = strings.Join(parts, ".")
}
return Parse(s)
}
// Parse parses version string and returns a validated Version or error
func Parse(s string) (Version, error) {
if len(s) == 0 {
return Version{}, errors.New("Version string empty")
}
// Split into major.minor.(patch+pr+meta)
parts := strings.SplitN(s, ".", 3)
if len(parts) != 3 {
return Version{}, errors.New("No Major.Minor.Patch elements found")
}
// Major
if !containsOnly(parts[0], numbers) {
return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0])
}
if hasLeadingZeroes(parts[0]) {
return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0])
}
major, err := strconv.ParseUint(parts[0], 10, 64)
if err != nil {
return Version{}, err
}
// Minor
if !containsOnly(parts[1], numbers) {
return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1])
}
if hasLeadingZeroes(parts[1]) {
return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1])
}
minor, err := strconv.ParseUint(parts[1], 10, 64)
if err != nil {
return Version{}, err
}
v := Version{}
v.Major = major
v.Minor = minor
var build, prerelease []string
patchStr := parts[2]
if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 {
build = strings.Split(patchStr[buildIndex+1:], ".")
patchStr = patchStr[:buildIndex]
}
if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 {
prerelease = strings.Split(patchStr[preIndex+1:], ".")
patchStr = patchStr[:preIndex]
}
if !containsOnly(patchStr, numbers) {
return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr)
}
if hasLeadingZeroes(patchStr) {
return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr)
}
patch, err := strconv.ParseUint(patchStr, 10, 64)
if err != nil {
return Version{}, err
}
v.Patch = patch
// Prerelease
for _, prstr := range prerelease {
parsedPR, err := NewPRVersion(prstr)
if err != nil {
return Version{}, err
}
v.Pre = append(v.Pre, parsedPR)
}
// Build meta data
for _, str := range build {
if len(str) == 0 {
return Version{}, errors.New("Build meta data is empty")
}
if !containsOnly(str, alphanum) {
return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str)
}
v.Build = append(v.Build, str)
}
return v, nil
}
// MustParse is like Parse but panics if the version cannot be parsed.
func MustParse(s string) Version {
v, err := Parse(s)
if err != nil {
panic(`semver: Parse(` + s + `): ` + err.Error())
}
return v
}
// PRVersion represents a PreRelease Version
type PRVersion struct {
VersionStr string
VersionNum uint64
IsNum bool
}
// NewPRVersion creates a new valid prerelease version
func NewPRVersion(s string) (PRVersion, error) {
if len(s) == 0 {
return PRVersion{}, errors.New("Prerelease is empty")
}
v := PRVersion{}
if containsOnly(s, numbers) {
if hasLeadingZeroes(s) {
return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s)
}
num, err := strconv.ParseUint(s, 10, 64)
// Might never be hit, but just in case
if err != nil {
return PRVersion{}, err
}
v.VersionNum = num
v.IsNum = true
} else if containsOnly(s, alphanum) {
v.VersionStr = s
v.IsNum = false
} else {
return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s)
}
return v, nil
}
// IsNumeric checks if prerelease-version is numeric
func (v PRVersion) IsNumeric() bool {
return v.IsNum
}
// Compare compares two PreRelease Versions v and o:
// -1 == v is less than o
// 0 == v is equal to o
// 1 == v is greater than o
func (v PRVersion) Compare(o PRVersion) int {
if v.IsNum && !o.IsNum {
return -1
} else if !v.IsNum && o.IsNum {
return 1
} else if v.IsNum && o.IsNum {
if v.VersionNum == o.VersionNum {
return 0
} else if v.VersionNum > o.VersionNum {
return 1
} else {
return -1
}
} else { // both are Alphas
if v.VersionStr == o.VersionStr {
return 0
} else if v.VersionStr > o.VersionStr {
return 1
} else {
return -1
}
}
}
// PreRelease version to string
func (v PRVersion) String() string {
if v.IsNum {
return strconv.FormatUint(v.VersionNum, 10)
}
return v.VersionStr
}
func containsOnly(s string, set string) bool {
return strings.IndexFunc(s, func(r rune) bool {
return !strings.ContainsRune(set, r)
}) == -1
}
func hasLeadingZeroes(s string) bool {
return len(s) > 1 && s[0] == '0'
}
// NewBuildVersion creates a new valid build version
func NewBuildVersion(s string) (string, error) {
if len(s) == 0 {
return "", errors.New("Buildversion is empty")
}
if !containsOnly(s, alphanum) {
return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s)
}
return s, nil
}

28
vendor/github.com/blang/semver/sort.go generated vendored Normal file
View File

@ -0,0 +1,28 @@
package semver
import (
"sort"
)
// Versions represents multiple versions.
type Versions []Version
// Len returns length of version collection
func (s Versions) Len() int {
return len(s)
}
// Swap swaps two versions inside the collection by its indices
func (s Versions) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Less checks if version at index i is less than version at index j
func (s Versions) Less(i, j int) bool {
return s[i].LT(s[j])
}
// Sort sorts a slice of versions
func Sort(versions []Version) {
sort.Sort(Versions(versions))
}

30
vendor/github.com/blang/semver/sql.go generated vendored Normal file
View File

@ -0,0 +1,30 @@
package semver
import (
"database/sql/driver"
"fmt"
)
// Scan implements the database/sql.Scanner interface.
func (v *Version) Scan(src interface{}) (err error) {
var str string
switch src := src.(type) {
case string:
str = src
case []byte:
str = string(src)
default:
return fmt.Errorf("Version.Scan: cannot convert %T to string.", src)
}
if t, err := Parse(str); err == nil {
*v = t
}
return
}
// Value implements the database/sql/driver.Valuer interface.
func (v Version) Value() (driver.Value, error) {
return v.String(), nil
}

View File

@ -0,0 +1,10 @@
package apis
import (
"github.com/che-incubator/kubernetes-image-puller-operator/pkg/apis/che/v1alpha1"
)
func init() {
// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme)
}

View File

@ -0,0 +1,13 @@
package apis
import (
"k8s.io/apimachinery/pkg/runtime"
)
// AddToSchemes may be used to add all resources defined in the project to a Scheme
var AddToSchemes runtime.SchemeBuilder
// AddToScheme adds all Resources to the Scheme
func AddToScheme(s *runtime.Scheme) error {
return AddToSchemes.AddToScheme(s)
}

View File

@ -0,0 +1,4 @@
// Package v1alpha1 contains API Schema definitions for the che v1alpha1 API group
// +k8s:deepcopy-gen=package,register
// +groupName=che.eclipse.org
package v1alpha1

View File

@ -0,0 +1,64 @@
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// KubernetesImagePullerSpec defines the desired state of KubernetesImagePuller
type KubernetesImagePullerSpec struct {
ConfigMapName string `json:"configMapName,omitempty"`
DaemonsetName string `json:"daemonsetName,omitempty"`
DeploymentName string `json:"deploymentName,omitempty"`
Images string `json:"images,omitempty"`
CachingIntervalHours string `json:"cachingIntervalHours,omitempty"`
CachingMemoryRequest string `json:"cachingMemoryRequest,omitempty"`
CachingMemoryLimit string `json:"cachingMemoryLimit,omitempty"`
CachingCpuRequest string `json:"cachingCPURequest,omitempty"`
CachingCpuLimit string `json:"cachingCPULimit,omitempty"`
NodeSelector string `json:"nodeSelector,omitempty"`
}
// KubernetesImagePullerStatus defines the observed state of KubernetesImagePuller
type KubernetesImagePullerStatus struct {
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// KubernetesImagePuller is the Schema for the kubernetesimagepullers API
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=kubernetesimagepullers,scope=Namespaced
type KubernetesImagePuller struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec KubernetesImagePullerSpec `json:"spec,omitempty"`
Status KubernetesImagePullerStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// KubernetesImagePullerList contains a list of KubernetesImagePuller
type KubernetesImagePullerList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []KubernetesImagePuller `json:"items"`
}
func init() {
SchemeBuilder.Register(&KubernetesImagePuller{}, &KubernetesImagePullerList{})
}
type KubernetesImagePullerConfig struct {
configMap *corev1.ConfigMap
}
func (config *KubernetesImagePullerConfig) WithDaemonsetName(name string) *KubernetesImagePullerConfig {
config.configMap.Data["DAEMONSET_NAME"] = name
return &KubernetesImagePullerConfig{
configMap: config.configMap,
}
}

View File

@ -0,0 +1,21 @@
// NOTE: Boilerplate only. Ignore this file.
// Package v1alpha1 contains API Schema definitions for the che v1alpha1 API group
// +k8s:deepcopy-gen=package,register
// +groupName=che.eclipse.org
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: "che.eclipse.org", Version: "v1alpha1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
AddToScheme = SchemeBuilder.AddToScheme
)

View File

@ -0,0 +1,124 @@
// +build !ignore_autogenerated
// Code generated by operator-sdk. DO NOT EDIT.
package v1alpha1
import (
v1 "k8s.io/api/core/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubernetesImagePuller) DeepCopyInto(out *KubernetesImagePuller) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
out.Status = in.Status
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesImagePuller.
func (in *KubernetesImagePuller) DeepCopy() *KubernetesImagePuller {
if in == nil {
return nil
}
out := new(KubernetesImagePuller)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *KubernetesImagePuller) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubernetesImagePullerConfig) DeepCopyInto(out *KubernetesImagePullerConfig) {
*out = *in
if in.configMap != nil {
in, out := &in.configMap, &out.configMap
*out = new(v1.ConfigMap)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesImagePullerConfig.
func (in *KubernetesImagePullerConfig) DeepCopy() *KubernetesImagePullerConfig {
if in == nil {
return nil
}
out := new(KubernetesImagePullerConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubernetesImagePullerList) DeepCopyInto(out *KubernetesImagePullerList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]KubernetesImagePuller, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesImagePullerList.
func (in *KubernetesImagePullerList) DeepCopy() *KubernetesImagePullerList {
if in == nil {
return nil
}
out := new(KubernetesImagePullerList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *KubernetesImagePullerList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubernetesImagePullerSpec) DeepCopyInto(out *KubernetesImagePullerSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesImagePullerSpec.
func (in *KubernetesImagePullerSpec) DeepCopy() *KubernetesImagePullerSpec {
if in == nil {
return nil
}
out := new(KubernetesImagePullerSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubernetesImagePullerStatus) DeepCopyInto(out *KubernetesImagePullerStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesImagePullerStatus.
func (in *KubernetesImagePullerStatus) DeepCopy() *KubernetesImagePullerStatus {
if in == nil {
return nil
}
out := new(KubernetesImagePullerStatus)
in.DeepCopyInto(out)
return out
}

201
vendor/github.com/operator-framework/api/LICENSE generated vendored Normal file
View File

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

View File

@ -0,0 +1,67 @@
package version
import (
"encoding/json"
"github.com/blang/semver"
)
// +k8s:openapi-gen=true
// OperatorVersion is a wrapper around semver.Version which supports correct
// marshaling to YAML and JSON.
// +kubebuilder:validation:Type=string
type OperatorVersion struct {
semver.Version `json:"-"`
}
// DeepCopyInto creates a deep-copy of the Version value.
func (v *OperatorVersion) DeepCopyInto(out *OperatorVersion) {
out.Major = v.Major
out.Minor = v.Minor
out.Patch = v.Patch
if v.Pre != nil {
pre := make([]semver.PRVersion, len(v.Pre))
copy(pre, v.Pre)
out.Pre = pre
}
if v.Build != nil {
build := make([]string, len(v.Build))
copy(build, v.Build)
out.Build = build
}
}
// MarshalJSON implements the encoding/json.Marshaler interface.
func (v OperatorVersion) MarshalJSON() ([]byte, error) {
return json.Marshal(v.String())
}
// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
func (v *OperatorVersion) UnmarshalJSON(data []byte) (err error) {
var versionString string
if err = json.Unmarshal(data, &versionString); err != nil {
return
}
version := semver.Version{}
version, err = semver.ParseTolerant(versionString)
if err != nil {
return err
}
v.Version = version
return
}
// OpenAPISchemaType is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
//
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
func (_ OperatorVersion) OpenAPISchemaType() []string { return []string{"string"} }
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
// "semver" is not a standard openapi format but tooling may use the value regardless
func (_ OperatorVersion) OpenAPISchemaFormat() string { return "semver" }

View File

@ -0,0 +1,4 @@
// +kubebuilder:skip
// Package operators contains all resource types of the operators.coreos.com API group.
package operators

View File

@ -0,0 +1,31 @@
package operators
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
// GroupName is the group name used in this package.
GroupName = "operators.coreos.com"
// GroupVersion is the group version used in this package.
GroupVersion = runtime.APIVersionInternal
// LEGACY: Exported kind names, remove after major version bump
// ClusterServiceVersionKind is the kind name for ClusterServiceVersion resources.
ClusterServiceVersionKind = "ClusterServiceVersion"
// CatalogSourceKind is the kind name for CatalogSource resources.
CatalogSourceKind = "CatalogSource"
// InstallPlanKind is the kind name for InstallPlan resources.
InstallPlanKind = "InstallPlan"
// SubscriptionKind is the kind name for Subscription resources.
SubscriptionKind = "Subscription"
// OperatorKind is the kind name for Operator resources.
OperatorKind = "Operator"
// OperatorGroupKind is the kind name for OperatorGroup resources.
OperatorGroupKind = "OperatorGroup"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}

View File

@ -0,0 +1,4 @@
// +groupName=operators.coreos.com
// Package v1 contains resources types for version v1 of the operators.coreos.com API group.
package v1

View File

@ -0,0 +1,28 @@
// +kubebuilder:object:generate=true
// Package v1 contains API Schema definitions for the operator v1 API group.
package v1
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: "operators.coreos.com", Version: "v1"}
// SchemeGroupVersion is required for compatibility with client generation.
SchemeGroupVersion = GroupVersion
// 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
)
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return GroupVersion.WithResource(resource).GroupResource()
}

View File

@ -0,0 +1,88 @@
package v1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// OperatorSpec defines the desired state of Operator
type OperatorSpec struct{}
// OperatorStatus defines the observed state of an Operator and its components
type OperatorStatus struct {
// Components describes resources that compose the operator.
// +optional
Components *Components `json:"components,omitempty"`
}
// ConditionType codifies a condition's type.
type ConditionType string
// Condition represent the latest available observations of an component's state.
type Condition struct {
// Type of condition.
Type ConditionType `json:"type"`
// Status of the condition, one of True, False, Unknown.
Status corev1.ConditionStatus `json:"status"`
// The reason for the condition's last transition.
// +optional
Reason string `json:"reason,omitempty"`
// A human readable message indicating details about the transition.
// +optional
Message string `json:"message,omitempty"`
// Last time the condition was probed
// +optional
LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"`
// Last time the condition transitioned from one status to another.
// +optional
LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"`
}
// Components tracks the resources that compose an operator.
type Components struct {
// LabelSelector is a label query over a set of resources used to select the operator's components
LabelSelector *metav1.LabelSelector `json:"labelSelector"`
// Refs are a set of references to the operator's component resources, selected with LabelSelector.
// +optional
Refs []RichReference `json:"refs,omitempty"`
}
// RichReference is a reference to a resource, enriched with its status conditions.
type RichReference struct {
*corev1.ObjectReference `json:",inline"`
// Conditions represents the latest state of the component.
// +optional
// +patchMergeKey=type
// +patchStrategy=merge
Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
}
// +genclient
// +genclient:nonNamespaced
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:resource:categories=olm,scope=Cluster
// +kubebuilder:subresource:status
// Operator represents a cluster operator.
type Operator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec OperatorSpec `json:"spec,omitempty"`
Status OperatorStatus `json:"status,omitempty"`
}
// +genclient:nonNamespaced
// +kubebuilder:object:root=true
// OperatorList contains a list of Operators.
type OperatorList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Operator `json:"items"`
}
func init() {
SchemeBuilder.Register(&Operator{}, &OperatorList{})
}

View File

@ -0,0 +1,150 @@
package v1
import (
"fmt"
"sort"
"strings"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
OperatorGroupAnnotationKey = "olm.operatorGroup"
OperatorGroupNamespaceAnnotationKey = "olm.operatorNamespace"
OperatorGroupTargetsAnnotationKey = "olm.targetNamespaces"
OperatorGroupProvidedAPIsAnnotationKey = "olm.providedAPIs"
OperatorGroupKind = "OperatorGroup"
OperatorGroupLabelPrefix = "olm.operatorgroup.uid/"
OperatorGroupLabelTemplate = OperatorGroupLabelPrefix + "%s"
)
// OperatorGroupSpec is the spec for an OperatorGroup resource.
type OperatorGroupSpec struct {
// Selector selects the OperatorGroup's target namespaces.
// +optional
Selector *metav1.LabelSelector `json:"selector,omitempty"`
// TargetNamespaces is an explicit set of namespaces to target.
// If it is set, Selector is ignored.
// +optional
// +listType=set
TargetNamespaces []string `json:"targetNamespaces,omitempty"`
// ServiceAccountName is the admin specified service account which will be
// used to deploy operator(s) in this operator group.
ServiceAccountName string `json:"serviceAccountName,omitempty"`
// Static tells OLM not to update the OperatorGroup's providedAPIs annotation
// +optional
StaticProvidedAPIs bool `json:"staticProvidedAPIs,omitempty"`
}
// OperatorGroupStatus is the status for an OperatorGroupResource.
type OperatorGroupStatus struct {
// Namespaces is the set of target namespaces for the OperatorGroup.
// +listType=set
Namespaces []string `json:"namespaces,omitempty"`
// ServiceAccountRef references the service account object specified.
ServiceAccountRef *corev1.ObjectReference `json:"serviceAccountRef,omitempty"`
// LastUpdated is a timestamp of the last time the OperatorGroup's status was Updated.
LastUpdated *metav1.Time `json:"lastUpdated"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// +kubebuilder:storageversion
// +kubebuilder:resource:shortName=og,categories=olm
// +kubebuilder:subresource:status
// OperatorGroup is the unit of multitenancy for OLM managed operators.
// It constrains the installation of operators in its namespace to a specified set of target namespaces.
type OperatorGroup struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
// +optional
Spec OperatorGroupSpec `json:"spec"`
Status OperatorGroupStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// OperatorGroupList is a list of OperatorGroup resources.
type OperatorGroupList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
// +listType=set
Items []OperatorGroup `json:"items"`
}
// BuildTargetNamespaces returns the set of target namespaces as a sorted, comma-delimited string
func (o *OperatorGroup) BuildTargetNamespaces() string {
ns := make([]string, len(o.Status.Namespaces))
copy(ns, o.Status.Namespaces)
sort.Strings(ns)
return strings.Join(ns, ",")
}
// IsServiceAccountSpecified returns true if the spec has a service account name specified.
func (o *OperatorGroup) IsServiceAccountSpecified() bool {
if o.Spec.ServiceAccountName == "" {
return false
}
return true
}
// HasServiceAccountSynced returns true if the service account specified has been synced.
func (o *OperatorGroup) HasServiceAccountSynced() bool {
if o.IsServiceAccountSpecified() && o.Status.ServiceAccountRef != nil {
return true
}
return false
}
// OGLabelKeyAndValue returns a key and value that should be applied to namespaces listed in the OperatorGroup.
// If the UID is not set an error is returned.
func (o *OperatorGroup) OGLabelKeyAndValue() (string, string, error) {
if string(o.GetUID()) == "" {
return "", "", fmt.Errorf("Missing UID")
}
return fmt.Sprintf(OperatorGroupLabelTemplate, o.GetUID()), "", nil
}
// NamespaceLabelSelector provides a selector that can be used to filter namespaces that belong to the OperatorGroup.
func (o *OperatorGroup) NamespaceLabelSelector() (*metav1.LabelSelector, error) {
if len(o.Spec.TargetNamespaces) == 0 {
// If no target namespaces are set, check if a selector exists.
if o.Spec.Selector != nil {
return o.Spec.Selector, nil
}
// No selector exists, return nil which should be used to select EVERYTHING.
return nil, nil
}
// Return a label that should be present on all namespaces defined in the OperatorGroup.Spec.TargetNamespaces field.
ogKey, ogValue, err := o.OGLabelKeyAndValue()
if err != nil {
return nil, err
}
return &metav1.LabelSelector{
MatchLabels: map[string]string{
ogKey: ogValue,
},
}, nil
}
// IsOperatorGroupLabel returns true if the label is an OperatorGroup label.
func IsOperatorGroupLabel(label string) bool {
return strings.HasPrefix(label, OperatorGroupLabelPrefix)
}
func init() {
SchemeBuilder.Register(&OperatorGroup{}, &OperatorGroupList{})
}

View File

@ -0,0 +1,310 @@
// +build !ignore_autogenerated
/*
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 controller-gen. DO NOT EDIT.
package v1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Components) DeepCopyInto(out *Components) {
*out = *in
if in.LabelSelector != nil {
in, out := &in.LabelSelector, &out.LabelSelector
*out = new(metav1.LabelSelector)
(*in).DeepCopyInto(*out)
}
if in.Refs != nil {
in, out := &in.Refs, &out.Refs
*out = make([]RichReference, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Components.
func (in *Components) DeepCopy() *Components {
if in == nil {
return nil
}
out := new(Components)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Condition) DeepCopyInto(out *Condition) {
*out = *in
if in.LastUpdateTime != nil {
in, out := &in.LastUpdateTime, &out.LastUpdateTime
*out = (*in).DeepCopy()
}
if in.LastTransitionTime != nil {
in, out := &in.LastTransitionTime, &out.LastTransitionTime
*out = (*in).DeepCopy()
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition.
func (in *Condition) DeepCopy() *Condition {
if in == nil {
return nil
}
out := new(Condition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Operator) DeepCopyInto(out *Operator) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Operator.
func (in *Operator) DeepCopy() *Operator {
if in == nil {
return nil
}
out := new(Operator)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Operator) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OperatorGroup) DeepCopyInto(out *OperatorGroup) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorGroup.
func (in *OperatorGroup) DeepCopy() *OperatorGroup {
if in == nil {
return nil
}
out := new(OperatorGroup)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *OperatorGroup) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OperatorGroupList) DeepCopyInto(out *OperatorGroupList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]OperatorGroup, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorGroupList.
func (in *OperatorGroupList) DeepCopy() *OperatorGroupList {
if in == nil {
return nil
}
out := new(OperatorGroupList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *OperatorGroupList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OperatorGroupSpec) DeepCopyInto(out *OperatorGroupSpec) {
*out = *in
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = new(metav1.LabelSelector)
(*in).DeepCopyInto(*out)
}
if in.TargetNamespaces != nil {
in, out := &in.TargetNamespaces, &out.TargetNamespaces
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorGroupSpec.
func (in *OperatorGroupSpec) DeepCopy() *OperatorGroupSpec {
if in == nil {
return nil
}
out := new(OperatorGroupSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OperatorGroupStatus) DeepCopyInto(out *OperatorGroupStatus) {
*out = *in
if in.Namespaces != nil {
in, out := &in.Namespaces, &out.Namespaces
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ServiceAccountRef != nil {
in, out := &in.ServiceAccountRef, &out.ServiceAccountRef
*out = new(corev1.ObjectReference)
**out = **in
}
if in.LastUpdated != nil {
in, out := &in.LastUpdated, &out.LastUpdated
*out = (*in).DeepCopy()
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorGroupStatus.
func (in *OperatorGroupStatus) DeepCopy() *OperatorGroupStatus {
if in == nil {
return nil
}
out := new(OperatorGroupStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OperatorList) DeepCopyInto(out *OperatorList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Operator, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorList.
func (in *OperatorList) DeepCopy() *OperatorList {
if in == nil {
return nil
}
out := new(OperatorList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *OperatorList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OperatorSpec) DeepCopyInto(out *OperatorSpec) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorSpec.
func (in *OperatorSpec) DeepCopy() *OperatorSpec {
if in == nil {
return nil
}
out := new(OperatorSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OperatorStatus) DeepCopyInto(out *OperatorStatus) {
*out = *in
if in.Components != nil {
in, out := &in.Components, &out.Components
*out = new(Components)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorStatus.
func (in *OperatorStatus) DeepCopy() *OperatorStatus {
if in == nil {
return nil
}
out := new(OperatorStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RichReference) DeepCopyInto(out *RichReference) {
*out = *in
if in.ObjectReference != nil {
in, out := &in.ObjectReference, &out.ObjectReference
*out = new(corev1.ObjectReference)
**out = **in
}
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RichReference.
func (in *RichReference) DeepCopy() *RichReference {
if in == nil {
return nil
}
out := new(RichReference)
in.DeepCopyInto(out)
return out
}

View File

@ -0,0 +1,243 @@
package v1alpha1
import (
"fmt"
"time"
"github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
const (
CatalogSourceCRDAPIVersion = GroupName + "/" + GroupVersion
CatalogSourceKind = "CatalogSource"
)
// SourceType indicates the type of backing store for a CatalogSource
type SourceType string
const (
// SourceTypeInternal (deprecated) specifies a CatalogSource of type SourceTypeConfigmap
SourceTypeInternal SourceType = "internal"
// SourceTypeConfigmap specifies a CatalogSource that generates a configmap-server registry
SourceTypeConfigmap SourceType = "configmap"
// SourceTypeGrpc specifies a CatalogSource that can use an operator registry image to generate a
// registry-server or connect to a pre-existing registry at an address.
SourceTypeGrpc SourceType = "grpc"
)
const (
// CatalogSourceSpecInvalidError denotes when fields on the spec of the CatalogSource are not valid.
CatalogSourceSpecInvalidError ConditionReason = "SpecInvalidError"
// CatalogSourceConfigMapError denotes when there is an issue extracting manifests from the specified ConfigMap.
CatalogSourceConfigMapError ConditionReason = "ConfigMapError"
// CatalogSourceRegistryServerError denotes when there is an issue querying the specified registry server.
CatalogSourceRegistryServerError ConditionReason = "RegistryServerError"
)
type CatalogSourceSpec struct {
// SourceType is the type of source
SourceType SourceType `json:"sourceType"`
// Priority field assigns a weight to the catalog source to prioritize them so that it can be consumed by the dependency resolver.
// Usage:
// Higher weight indicates that this catalog source is preferred over lower weighted catalog sources during dependency resolution.
// The range of the priority value can go from positive to negative in the range of int32.
// The default value to a catalog source with unassigned priority would be 0.
// The catalog source with the same priority values will be ranked lexicographically based on its name.
// +Optional
Priority int `json:"priority,omitempty"`
// ConfigMap is the name of the ConfigMap to be used to back a configmap-server registry.
// Only used when SourceType = SourceTypeConfigmap or SourceTypeInternal.
// +Optional
ConfigMap string `json:"configMap,omitempty"`
// Address is a host that OLM can use to connect to a pre-existing registry.
// Format: <registry-host or ip>:<port>
// Only used when SourceType = SourceTypeGrpc.
// Ignored when the Image field is set.
// +Optional
Address string `json:"address,omitempty"`
// Image is an operator-registry container image to instantiate a registry-server with.
// Only used when SourceType = SourceTypeGrpc.
// If present, the address field is ignored.
// +Optional
Image string `json:"image,omitempty"`
// UpdateStrategy defines how updated catalog source images can be discovered
// Consists of an interval that defines polling duration and an embedded strategy type
// +Optional
UpdateStrategy *UpdateStrategy `json:"updateStrategy,omitempty"`
// Secrets represent set of secrets that can be used to access the contents of the catalog.
// It is best to keep this list small, since each will need to be tried for every catalog entry.
// +Optional
Secrets []string `json:"secrets,omitempty"`
// Metadata
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
Publisher string `json:"publisher,omitempty"`
Icon Icon `json:"icon,omitempty"`
}
// UpdateStrategy holds all the different types of catalog source update strategies
// Currently only registry polling strategy is implemented
type UpdateStrategy struct {
*RegistryPoll `json:"registryPoll,omitempty"`
}
type RegistryPoll struct {
// Interval is used to determine the time interval between checks of the latest catalog source version.
// The catalog operator polls to see if a new version of the catalog source is available.
// If available, the latest image is pulled and gRPC traffic is directed to the latest catalog source.
Interval *metav1.Duration `json:"interval,omitempty"`
}
type RegistryServiceStatus struct {
Protocol string `json:"protocol,omitempty"`
ServiceName string `json:"serviceName,omitempty"`
ServiceNamespace string `json:"serviceNamespace,omitempty"`
Port string `json:"port,omitempty"`
CreatedAt metav1.Time `json:"createdAt,omitempty"`
}
func (s *RegistryServiceStatus) Address() string {
return fmt.Sprintf("%s.%s.svc:%s", s.ServiceName, s.ServiceNamespace, s.Port)
}
type GRPCConnectionState struct {
Address string `json:"address,omitempty"`
LastObservedState string `json:"lastObservedState"`
LastConnectTime metav1.Time `json:"lastConnect,omitempty"`
}
type CatalogSourceStatus struct {
// A human readable message indicating details about why the CatalogSource is in this condition.
// +optional
Message string `json:"message,omitempty"`
// Reason is the reason the CatalogSource was transitioned to its current state.
// +optional
Reason ConditionReason `json:"reason,omitempty"`
// The last time the CatalogSource image registry has been polled to ensure the image is up-to-date
LatestImageRegistryPoll *metav1.Time `json:"latestImageRegistryPoll,omitempty"`
ConfigMapResource *ConfigMapResourceReference `json:"configMapReference,omitempty"`
RegistryServiceStatus *RegistryServiceStatus `json:"registryService,omitempty"`
GRPCConnectionState *GRPCConnectionState `json:"connectionState,omitempty"`
}
type ConfigMapResourceReference struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
UID types.UID `json:"uid,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty"`
LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`
}
func (r *ConfigMapResourceReference) IsAMatch(object *metav1.ObjectMeta) bool {
return r.UID == object.GetUID() && r.ResourceVersion == object.GetResourceVersion()
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// +kubebuilder:resource:shortName=catsrc,categories=olm
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Display",type=string,JSONPath=`.spec.displayName`,description="The pretty name of the catalog"
// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.sourceType`,description="The type of the catalog"
// +kubebuilder:printcolumn:name="Publisher",type=string,JSONPath=`.spec.publisher`,description="The publisher of the catalog"
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// CatalogSource is a repository of CSVs, CRDs, and operator packages.
type CatalogSource struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec CatalogSourceSpec `json:"spec"`
// +optional
Status CatalogSourceStatus `json:"status"`
}
func (c *CatalogSource) Address() string {
if c.Spec.Address != "" {
return c.Spec.Address
}
return c.Status.RegistryServiceStatus.Address()
}
func (c *CatalogSource) SetError(reason ConditionReason, err error) {
c.Status.Reason = reason
c.Status.Message = ""
if err != nil {
c.Status.Message = err.Error()
}
}
func (c *CatalogSource) SetLastUpdateTime() {
now := metav1.Now()
c.Status.LatestImageRegistryPoll = &now
}
// Check if it is time to update based on polling setting
func (c *CatalogSource) Update() bool {
if !c.Poll() {
return false
}
interval := c.Spec.UpdateStrategy.Interval.Duration
latest := c.Status.LatestImageRegistryPoll
if latest == nil {
logrus.WithField("CatalogSource", c.Name).Debugf("latest poll %v", latest)
} else {
logrus.WithField("CatalogSource", c.Name).Debugf("latest poll %v", *c.Status.LatestImageRegistryPoll)
}
if c.Status.LatestImageRegistryPoll.IsZero() {
logrus.WithField("CatalogSource", c.Name).Debugf("creation timestamp plus interval before now %t", c.CreationTimestamp.Add(interval).Before(time.Now()))
if c.CreationTimestamp.Add(interval).Before(time.Now()) {
return true
}
} else {
logrus.WithField("CatalogSource", c.Name).Debugf("latest poll plus interval before now %t", c.Status.LatestImageRegistryPoll.Add(interval).Before(time.Now()))
if c.Status.LatestImageRegistryPoll.Add(interval).Before(time.Now()) {
return true
}
}
return false
}
// Poll determines whether the polling feature is enabled on the particular catalog source
func (c *CatalogSource) Poll() bool {
if c.Spec.UpdateStrategy == nil {
return false
}
// if polling interval is zero polling will not be done
if c.Spec.UpdateStrategy.RegistryPoll == nil {
return false
}
// if catalog source is not backed by an image polling will not be done
if c.Spec.Image == "" {
return false
}
// if image is not type gRPC polling will not be done
if c.Spec.SourceType != SourceTypeGrpc {
return false
}
return true
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// CatalogSourceList is a repository of CSVs, CRDs, and operator packages.
type CatalogSourceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []CatalogSource `json:"items"`
}

View File

@ -0,0 +1,208 @@
package v1alpha1
import (
"fmt"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/record"
)
const (
CopiedLabelKey = "olm.copiedFrom"
// ConditionsLengthLimit is the maximum length of Status.Conditions of a
// given ClusterServiceVersion object. The oldest condition(s) are removed
// from the list as it grows over time to keep it at limit.
ConditionsLengthLimit = 20
)
// obsoleteReasons are the set of reasons that mean a CSV should no longer be processed as active
var obsoleteReasons = map[ConditionReason]struct{}{
CSVReasonReplaced: {},
CSVReasonBeingReplaced: {},
}
// uncopiableReasons are the set of reasons that should prevent a CSV from being copied to target namespaces
var uncopiableReasons = map[ConditionReason]struct{}{
CSVReasonCopied: {},
CSVReasonInvalidInstallModes: {},
CSVReasonNoTargetNamespaces: {},
CSVReasonUnsupportedOperatorGroup: {},
CSVReasonNoOperatorGroup: {},
CSVReasonTooManyOperatorGroups: {},
CSVReasonInterOperatorGroupOwnerConflict: {},
CSVReasonCannotModifyStaticOperatorGroupProvidedAPIs: {},
}
// safeToAnnotateOperatorGroupReasons are the set of reasons that it's safe to attempt to update the operatorgroup
// annotations
var safeToAnnotateOperatorGroupReasons = map[ConditionReason]struct{}{
CSVReasonOwnerConflict: {},
CSVReasonInstallSuccessful: {},
CSVReasonInvalidInstallModes: {},
CSVReasonNoTargetNamespaces: {},
CSVReasonUnsupportedOperatorGroup: {},
CSVReasonNoOperatorGroup: {},
CSVReasonTooManyOperatorGroups: {},
CSVReasonInterOperatorGroupOwnerConflict: {},
CSVReasonCannotModifyStaticOperatorGroupProvidedAPIs: {},
}
// SetPhaseWithEventIfChanged emits a Kubernetes event with details of a phase change and sets the current phase if phase, reason, or message would changed
func (c *ClusterServiceVersion) SetPhaseWithEventIfChanged(phase ClusterServiceVersionPhase, reason ConditionReason, message string, now *metav1.Time, recorder record.EventRecorder) {
if c.Status.Phase == phase && c.Status.Reason == reason && c.Status.Message == message {
return
}
c.SetPhaseWithEvent(phase, reason, message, now, recorder)
}
// SetPhaseWithEvent generates a Kubernetes event with details about the phase change and sets the current phase
func (c *ClusterServiceVersion) SetPhaseWithEvent(phase ClusterServiceVersionPhase, reason ConditionReason, message string, now *metav1.Time, recorder record.EventRecorder) {
var eventtype string
if phase == CSVPhaseFailed {
eventtype = v1.EventTypeWarning
} else {
eventtype = v1.EventTypeNormal
}
go recorder.Event(c, eventtype, string(reason), message)
c.SetPhase(phase, reason, message, now)
}
// SetPhase sets the current phase and adds a condition if necessary
func (c *ClusterServiceVersion) SetPhase(phase ClusterServiceVersionPhase, reason ConditionReason, message string, now *metav1.Time) {
newCondition := func() ClusterServiceVersionCondition {
return ClusterServiceVersionCondition{
Phase: c.Status.Phase,
LastTransitionTime: c.Status.LastTransitionTime,
LastUpdateTime: c.Status.LastUpdateTime,
Message: message,
Reason: reason,
}
}
defer c.TrimConditionsIfLimitExceeded()
c.Status.LastUpdateTime = now
if c.Status.Phase != phase {
c.Status.Phase = phase
c.Status.LastTransitionTime = now
}
c.Status.Message = message
c.Status.Reason = reason
if len(c.Status.Conditions) == 0 {
c.Status.Conditions = append(c.Status.Conditions, newCondition())
return
}
previousCondition := c.Status.Conditions[len(c.Status.Conditions)-1]
if previousCondition.Phase != c.Status.Phase || previousCondition.Reason != c.Status.Reason {
c.Status.Conditions = append(c.Status.Conditions, newCondition())
}
}
// SetRequirementStatus adds the status of all requirements to the CSV status
func (c *ClusterServiceVersion) SetRequirementStatus(statuses []RequirementStatus) {
c.Status.RequirementStatus = statuses
}
// IsObsolete returns if this CSV is being replaced or is marked for deletion
func (c *ClusterServiceVersion) IsObsolete() bool {
for _, condition := range c.Status.Conditions {
_, ok := obsoleteReasons[condition.Reason]
if ok {
return true
}
}
return false
}
// IsCopied returns true if the CSV has been copied and false otherwise.
func (c *ClusterServiceVersion) IsCopied() bool {
operatorNamespace, ok := c.GetAnnotations()[OperatorGroupNamespaceAnnotationKey]
if c.Status.Reason == CSVReasonCopied || ok && c.GetNamespace() != operatorNamespace {
return true
}
if labels := c.GetLabels(); labels != nil {
if _, ok := labels[CopiedLabelKey]; ok {
return true
}
}
return false
}
func (c *ClusterServiceVersion) IsUncopiable() bool {
if c.Status.Phase == CSVPhaseNone {
return true
}
_, ok := uncopiableReasons[c.Status.Reason]
return ok
}
func (c *ClusterServiceVersion) IsSafeToUpdateOperatorGroupAnnotations() bool {
_, ok := safeToAnnotateOperatorGroupReasons[c.Status.Reason]
return ok
}
// NewInstallModeSet returns an InstallModeSet instantiated from the given list of InstallModes.
// If the given list is not a set, an error is returned.
func NewInstallModeSet(modes []InstallMode) (InstallModeSet, error) {
set := InstallModeSet{}
for _, mode := range modes {
if _, exists := set[mode.Type]; exists {
return nil, fmt.Errorf("InstallMode list contains duplicates, cannot make set: %v", modes)
}
set[mode.Type] = mode.Supported
}
return set, nil
}
// Supports returns an error if the InstallModeSet does not support configuration for
// the given operatorNamespace and list of target namespaces.
func (set InstallModeSet) Supports(operatorNamespace string, namespaces []string) error {
numNamespaces := len(namespaces)
switch {
case numNamespaces == 0:
return fmt.Errorf("operatorgroup has invalid selected namespaces, cannot configure to watch zero namespaces")
case numNamespaces == 1:
switch namespaces[0] {
case operatorNamespace:
if !set[InstallModeTypeOwnNamespace] {
return fmt.Errorf("%s InstallModeType not supported, cannot configure to watch own namespace", InstallModeTypeOwnNamespace)
}
case v1.NamespaceAll:
if !set[InstallModeTypeAllNamespaces] {
return fmt.Errorf("%s InstallModeType not supported, cannot configure to watch all namespaces", InstallModeTypeAllNamespaces)
}
default:
if !set[InstallModeTypeSingleNamespace] {
return fmt.Errorf("%s InstallModeType not supported, cannot configure to watch one namespace", InstallModeTypeSingleNamespace)
}
}
case numNamespaces > 1 && !set[InstallModeTypeMultiNamespace]:
return fmt.Errorf("%s InstallModeType not supported, cannot configure to watch %d namespaces", InstallModeTypeMultiNamespace, numNamespaces)
case numNamespaces > 1:
for _, namespace := range namespaces {
if namespace == operatorNamespace && !set[InstallModeTypeOwnNamespace] {
return fmt.Errorf("%s InstallModeType not supported, cannot configure to watch own namespace", InstallModeTypeOwnNamespace)
}
if namespace == v1.NamespaceAll {
return fmt.Errorf("operatorgroup has invalid selected namespaces, NamespaceAll found when |selected namespaces| > 1")
}
}
}
return nil
}
func (c *ClusterServiceVersion) TrimConditionsIfLimitExceeded() {
if len(c.Status.Conditions) <= ConditionsLengthLimit {
return
}
firstIndex := len(c.Status.Conditions) - ConditionsLengthLimit
c.Status.Conditions = c.Status.Conditions[firstIndex:len(c.Status.Conditions)]
}

View File

@ -0,0 +1,645 @@
package v1alpha1
import (
"encoding/json"
"fmt"
"sort"
"strings"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
appsv1 "k8s.io/api/apps/v1"
rbac "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/intstr"
"github.com/operator-framework/api/pkg/lib/version"
)
const (
ClusterServiceVersionAPIVersion = GroupName + "/" + GroupVersion
ClusterServiceVersionKind = "ClusterServiceVersion"
OperatorGroupNamespaceAnnotationKey = "olm.operatorNamespace"
InstallStrategyNameDeployment = "deployment"
)
// InstallModeType is a supported type of install mode for CSV installation
type InstallModeType string
const (
// InstallModeTypeOwnNamespace indicates that the operator can be a member of an `OperatorGroup` that selects its own namespace.
InstallModeTypeOwnNamespace InstallModeType = "OwnNamespace"
// InstallModeTypeSingleNamespace indicates that the operator can be a member of an `OperatorGroup` that selects one namespace.
InstallModeTypeSingleNamespace InstallModeType = "SingleNamespace"
// InstallModeTypeMultiNamespace indicates that the operator can be a member of an `OperatorGroup` that selects more than one namespace.
InstallModeTypeMultiNamespace InstallModeType = "MultiNamespace"
// InstallModeTypeAllNamespaces indicates that the operator can be a member of an `OperatorGroup` that selects all namespaces (target namespace set is the empty string "").
InstallModeTypeAllNamespaces InstallModeType = "AllNamespaces"
)
// InstallMode associates an InstallModeType with a flag representing if the CSV supports it
// +k8s:openapi-gen=true
type InstallMode struct {
Type InstallModeType `json:"type"`
Supported bool `json:"supported"`
}
// InstallModeSet is a mapping of unique InstallModeTypes to whether they are supported.
type InstallModeSet map[InstallModeType]bool
// NamedInstallStrategy represents the block of an ClusterServiceVersion resource
// where the install strategy is specified.
type NamedInstallStrategy struct {
StrategyName string `json:"strategy"`
StrategySpec StrategyDetailsDeployment `json:"spec,omitempty"`
}
// StrategyDeploymentPermissions describe the rbac rules and service account needed by the install strategy
type StrategyDeploymentPermissions struct {
ServiceAccountName string `json:"serviceAccountName"`
Rules []rbac.PolicyRule `json:"rules"`
}
// StrategyDeploymentSpec contains the name, spec and labels for the deployment ALM should create
type StrategyDeploymentSpec struct {
Name string `json:"name"`
Spec appsv1.DeploymentSpec `json:"spec"`
Label labels.Set `json:"label,omitempty"`
}
// StrategyDetailsDeployment represents the parsed details of a Deployment
// InstallStrategy.
type StrategyDetailsDeployment struct {
DeploymentSpecs []StrategyDeploymentSpec `json:"deployments"`
Permissions []StrategyDeploymentPermissions `json:"permissions,omitempty"`
ClusterPermissions []StrategyDeploymentPermissions `json:"clusterPermissions,omitempty"`
}
func (d *StrategyDetailsDeployment) GetStrategyName() string {
return InstallStrategyNameDeployment
}
// StatusDescriptor describes a field in a status block of a CRD so that OLM can consume it
// +k8s:openapi-gen=true
type StatusDescriptor struct {
Path string `json:"path"`
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
XDescriptors []string `json:"x-descriptors,omitempty"`
Value json.RawMessage `json:"value,omitempty"`
}
// SpecDescriptor describes a field in a spec block of a CRD so that OLM can consume it
// +k8s:openapi-gen=true
type SpecDescriptor struct {
Path string `json:"path"`
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
XDescriptors []string `json:"x-descriptors,omitempty"`
Value json.RawMessage `json:"value,omitempty"`
}
// ActionDescriptor describes a declarative action that can be performed on a custom resource instance
// +k8s:openapi-gen=true
type ActionDescriptor struct {
Path string `json:"path"`
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
XDescriptors []string `json:"x-descriptors,omitempty"`
Value json.RawMessage `json:"value,omitempty"`
}
// CRDDescription provides details to OLM about the CRDs
// +k8s:openapi-gen=true
type CRDDescription struct {
Name string `json:"name"`
Version string `json:"version"`
Kind string `json:"kind"`
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
Resources []APIResourceReference `json:"resources,omitempty"`
StatusDescriptors []StatusDescriptor `json:"statusDescriptors,omitempty"`
SpecDescriptors []SpecDescriptor `json:"specDescriptors,omitempty"`
ActionDescriptor []ActionDescriptor `json:"actionDescriptors,omitempty"`
}
// APIServiceDescription provides details to OLM about apis provided via aggregation
// +k8s:openapi-gen=true
type APIServiceDescription struct {
Name string `json:"name"`
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
DeploymentName string `json:"deploymentName,omitempty"`
ContainerPort int32 `json:"containerPort,omitempty"`
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
Resources []APIResourceReference `json:"resources,omitempty"`
StatusDescriptors []StatusDescriptor `json:"statusDescriptors,omitempty"`
SpecDescriptors []SpecDescriptor `json:"specDescriptors,omitempty"`
ActionDescriptor []ActionDescriptor `json:"actionDescriptors,omitempty"`
}
// APIResourceReference is a Kubernetes resource type used by a custom resource
// +k8s:openapi-gen=true
type APIResourceReference struct {
Name string `json:"name"`
Kind string `json:"kind"`
Version string `json:"version"`
}
// GetName returns the name of an APIService as derived from its group and version.
func (d APIServiceDescription) GetName() string {
return fmt.Sprintf("%s.%s", d.Version, d.Group)
}
// WebhookAdmissionType is the type of admission webhooks supported by OLM
type WebhookAdmissionType string
const (
// ValidatingAdmissionWebhook is for validating admission webhooks
ValidatingAdmissionWebhook WebhookAdmissionType = "ValidatingAdmissionWebhook"
// MutatingAdmissionWebhook is for mutating admission webhooks
MutatingAdmissionWebhook WebhookAdmissionType = "MutatingAdmissionWebhook"
// ConversionWebhook is for conversion webhooks
ConversionWebhook WebhookAdmissionType = "ConversionWebhook"
)
// WebhookDescription provides details to OLM about required webhooks
// +k8s:openapi-gen=true
type WebhookDescription struct {
GenerateName string `json:"generateName"`
// +kubebuilder:validation:Enum=ValidatingAdmissionWebhook;MutatingAdmissionWebhook;ConversionWebhook
Type WebhookAdmissionType `json:"type"`
DeploymentName string `json:"deploymentName,omitempty"`
// +kubebuilder:validation:Maximum=65535
// +kubebuilder:validation:Minimum=1
// +kubebuilder:default=443
ContainerPort int32 `json:"containerPort,omitempty"`
TargetPort *intstr.IntOrString `json:"targetPort,omitempty"`
Rules []admissionregistrationv1.RuleWithOperations `json:"rules,omitempty"`
FailurePolicy *admissionregistrationv1.FailurePolicyType `json:"failurePolicy,omitempty"`
MatchPolicy *admissionregistrationv1.MatchPolicyType `json:"matchPolicy,omitempty"`
ObjectSelector *metav1.LabelSelector `json:"objectSelector,omitempty"`
SideEffects *admissionregistrationv1.SideEffectClass `json:"sideEffects"`
TimeoutSeconds *int32 `json:"timeoutSeconds,omitempty"`
AdmissionReviewVersions []string `json:"admissionReviewVersions"`
ReinvocationPolicy *admissionregistrationv1.ReinvocationPolicyType `json:"reinvocationPolicy,omitempty"`
WebhookPath *string `json:"webhookPath,omitempty"`
ConversionCRDs []string `json:"conversionCRDs,omitempty"`
}
// GetValidatingWebhook returns a ValidatingWebhook generated from the WebhookDescription
func (w *WebhookDescription) GetValidatingWebhook(namespace string, namespaceSelector *metav1.LabelSelector, caBundle []byte) admissionregistrationv1.ValidatingWebhook {
return admissionregistrationv1.ValidatingWebhook{
Name: w.GenerateName,
Rules: w.Rules,
FailurePolicy: w.FailurePolicy,
MatchPolicy: w.MatchPolicy,
NamespaceSelector: namespaceSelector,
ObjectSelector: w.ObjectSelector,
SideEffects: w.SideEffects,
TimeoutSeconds: w.TimeoutSeconds,
AdmissionReviewVersions: w.AdmissionReviewVersions,
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: w.DomainName() + "-service",
Namespace: namespace,
Path: w.WebhookPath,
Port: &w.ContainerPort,
},
CABundle: caBundle,
},
}
}
// GetMutatingWebhook returns a MutatingWebhook generated from the WebhookDescription
func (w *WebhookDescription) GetMutatingWebhook(namespace string, namespaceSelector *metav1.LabelSelector, caBundle []byte) admissionregistrationv1.MutatingWebhook {
return admissionregistrationv1.MutatingWebhook{
Name: w.GenerateName,
Rules: w.Rules,
FailurePolicy: w.FailurePolicy,
MatchPolicy: w.MatchPolicy,
NamespaceSelector: namespaceSelector,
ObjectSelector: w.ObjectSelector,
SideEffects: w.SideEffects,
TimeoutSeconds: w.TimeoutSeconds,
AdmissionReviewVersions: w.AdmissionReviewVersions,
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: w.DomainName() + "-service",
Namespace: namespace,
Path: w.WebhookPath,
Port: &w.ContainerPort,
},
CABundle: caBundle,
},
ReinvocationPolicy: w.ReinvocationPolicy,
}
}
// DomainName returns the result of replacing all periods in the given Webhook name with hyphens
func (w *WebhookDescription) DomainName() string {
// Replace all '.'s with "-"s to convert to a DNS-1035 label
return strings.Replace(w.DeploymentName, ".", "-", -1)
}
// CustomResourceDefinitions declares all of the CRDs managed or required by
// an operator being ran by ClusterServiceVersion.
//
// If the CRD is present in the Owned list, it is implicitly required.
// +k8s:openapi-gen=true
type CustomResourceDefinitions struct {
Owned []CRDDescription `json:"owned,omitempty"`
Required []CRDDescription `json:"required,omitempty"`
}
// APIServiceDefinitions declares all of the extension apis managed or required by
// an operator being ran by ClusterServiceVersion.
// +k8s:openapi-gen=true
type APIServiceDefinitions struct {
Owned []APIServiceDescription `json:"owned,omitempty"`
Required []APIServiceDescription `json:"required,omitempty"`
}
// ClusterServiceVersionSpec declarations tell OLM how to install an operator
// that can manage apps for a given version.
type ClusterServiceVersionSpec struct {
InstallStrategy NamedInstallStrategy `json:"install"`
Version version.OperatorVersion `json:"version,omitempty"`
Maturity string `json:"maturity,omitempty"`
CustomResourceDefinitions CustomResourceDefinitions `json:"customresourcedefinitions,omitempty"`
APIServiceDefinitions APIServiceDefinitions `json:"apiservicedefinitions,omitempty"`
WebhookDefinitions []WebhookDescription `json:"webhookdefinitions,omitempty"`
NativeAPIs []metav1.GroupVersionKind `json:"nativeAPIs,omitempty"`
MinKubeVersion string `json:"minKubeVersion,omitempty"`
DisplayName string `json:"displayName"`
Description string `json:"description,omitempty"`
Keywords []string `json:"keywords,omitempty"`
Maintainers []Maintainer `json:"maintainers,omitempty"`
Provider AppLink `json:"provider,omitempty"`
Links []AppLink `json:"links,omitempty"`
Icon []Icon `json:"icon,omitempty"`
// InstallModes specify supported installation types
// +optional
InstallModes []InstallMode `json:"installModes,omitempty"`
// The name of a CSV this one replaces. Should match the `metadata.Name` field of the old CSV.
// +optional
Replaces string `json:"replaces,omitempty"`
// Map of string keys and values that can be used to organize and categorize
// (scope and select) objects.
// +optional
Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"`
// Annotations is an unstructured key value map stored with a resource that may be
// set by external tools to store and retrieve arbitrary metadata.
// +optional
Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`
// Label selector for related resources.
// +optional
Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,2,opt,name=selector"`
}
type Maintainer struct {
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
}
type AppLink struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
}
type Icon struct {
Data string `json:"base64data"`
MediaType string `json:"mediatype"`
}
// ClusterServiceVersionPhase is a label for the condition of a ClusterServiceVersion at the current time.
type ClusterServiceVersionPhase string
// These are the valid phases of ClusterServiceVersion
const (
CSVPhaseNone = ""
// CSVPhasePending means the csv has been accepted by the system, but the install strategy has not been attempted.
// This is likely because there are unmet requirements.
CSVPhasePending ClusterServiceVersionPhase = "Pending"
// CSVPhaseInstallReady means that the requirements are met but the install strategy has not been run.
CSVPhaseInstallReady ClusterServiceVersionPhase = "InstallReady"
// CSVPhaseInstalling means that the install strategy has been initiated but not completed.
CSVPhaseInstalling ClusterServiceVersionPhase = "Installing"
// CSVPhaseSucceeded means that the resources in the CSV were created successfully.
CSVPhaseSucceeded ClusterServiceVersionPhase = "Succeeded"
// CSVPhaseFailed means that the install strategy could not be successfully completed.
CSVPhaseFailed ClusterServiceVersionPhase = "Failed"
// CSVPhaseUnknown means that for some reason the state of the csv could not be obtained.
CSVPhaseUnknown ClusterServiceVersionPhase = "Unknown"
// CSVPhaseReplacing means that a newer CSV has been created and the csv's resources will be transitioned to a new owner.
CSVPhaseReplacing ClusterServiceVersionPhase = "Replacing"
// CSVPhaseDeleting means that a CSV has been replaced by a new one and will be checked for safety before being deleted
CSVPhaseDeleting ClusterServiceVersionPhase = "Deleting"
// CSVPhaseAny matches all other phases in CSV queries
CSVPhaseAny ClusterServiceVersionPhase = ""
)
// ConditionReason is a camelcased reason for the state transition
type ConditionReason string
const (
CSVReasonRequirementsUnknown ConditionReason = "RequirementsUnknown"
CSVReasonRequirementsNotMet ConditionReason = "RequirementsNotMet"
CSVReasonRequirementsMet ConditionReason = "AllRequirementsMet"
CSVReasonOwnerConflict ConditionReason = "OwnerConflict"
CSVReasonComponentFailed ConditionReason = "InstallComponentFailed"
CSVReasonComponentFailedNoRetry ConditionReason = "InstallComponentFailedNoRetry"
CSVReasonInvalidStrategy ConditionReason = "InvalidInstallStrategy"
CSVReasonWaiting ConditionReason = "InstallWaiting"
CSVReasonInstallSuccessful ConditionReason = "InstallSucceeded"
CSVReasonInstallCheckFailed ConditionReason = "InstallCheckFailed"
CSVReasonComponentUnhealthy ConditionReason = "ComponentUnhealthy"
CSVReasonBeingReplaced ConditionReason = "BeingReplaced"
CSVReasonReplaced ConditionReason = "Replaced"
CSVReasonNeedsReinstall ConditionReason = "NeedsReinstall"
CSVReasonNeedsCertRotation ConditionReason = "NeedsCertRotation"
CSVReasonAPIServiceResourceIssue ConditionReason = "APIServiceResourceIssue"
CSVReasonAPIServiceResourcesNeedReinstall ConditionReason = "APIServiceResourcesNeedReinstall"
CSVReasonAPIServiceInstallFailed ConditionReason = "APIServiceInstallFailed"
CSVReasonCopied ConditionReason = "Copied"
CSVReasonInvalidInstallModes ConditionReason = "InvalidInstallModes"
CSVReasonNoTargetNamespaces ConditionReason = "NoTargetNamespaces"
CSVReasonUnsupportedOperatorGroup ConditionReason = "UnsupportedOperatorGroup"
CSVReasonNoOperatorGroup ConditionReason = "NoOperatorGroup"
CSVReasonTooManyOperatorGroups ConditionReason = "TooManyOperatorGroups"
CSVReasonInterOperatorGroupOwnerConflict ConditionReason = "InterOperatorGroupOwnerConflict"
CSVReasonCannotModifyStaticOperatorGroupProvidedAPIs ConditionReason = "CannotModifyStaticOperatorGroupProvidedAPIs"
CSVReasonDetectedClusterChange ConditionReason = "DetectedClusterChange"
CSVReasonInvalidWebhookDescription ConditionReason = "InvalidWebhookDescription"
)
// HasCaResources returns true if the CSV has owned APIServices or Webhooks.
func (c *ClusterServiceVersion) HasCAResources() bool {
// Return early if there are no owned APIServices
if len(c.Spec.APIServiceDefinitions.Owned)+len(c.Spec.WebhookDefinitions) == 0 {
return false
}
return true
}
// Conditions appear in the status as a record of state transitions on the ClusterServiceVersion
type ClusterServiceVersionCondition struct {
// Condition of the ClusterServiceVersion
Phase ClusterServiceVersionPhase `json:"phase,omitempty"`
// A human readable message indicating details about why the ClusterServiceVersion is in this condition.
// +optional
Message string `json:"message,omitempty"`
// A brief CamelCase message indicating details about why the ClusterServiceVersion is in this state.
// e.g. 'RequirementsNotMet'
// +optional
Reason ConditionReason `json:"reason,omitempty"`
// Last time we updated the status
// +optional
LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"`
// Last time the status transitioned from one status to another.
// +optional
LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"`
}
// OwnsCRD determines whether the current CSV owns a particular CRD.
func (csv ClusterServiceVersion) OwnsCRD(name string) bool {
for _, desc := range csv.Spec.CustomResourceDefinitions.Owned {
if desc.Name == name {
return true
}
}
return false
}
// OwnsAPIService determines whether the current CSV owns a particular APIService.
func (csv ClusterServiceVersion) OwnsAPIService(name string) bool {
for _, desc := range csv.Spec.APIServiceDefinitions.Owned {
apiServiceName := fmt.Sprintf("%s.%s", desc.Version, desc.Group)
if apiServiceName == name {
return true
}
}
return false
}
// StatusReason is a camelcased reason for the status of a RequirementStatus or DependentStatus
type StatusReason string
const (
RequirementStatusReasonPresent StatusReason = "Present"
RequirementStatusReasonNotPresent StatusReason = "NotPresent"
RequirementStatusReasonPresentNotSatisfied StatusReason = "PresentNotSatisfied"
// The CRD is present but the Established condition is False (not available)
RequirementStatusReasonNotAvailable StatusReason = "PresentNotAvailable"
DependentStatusReasonSatisfied StatusReason = "Satisfied"
DependentStatusReasonNotSatisfied StatusReason = "NotSatisfied"
)
// DependentStatus is the status for a dependent requirement (to prevent infinite nesting)
type DependentStatus struct {
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
Status StatusReason `json:"status"`
UUID string `json:"uuid,omitempty"`
Message string `json:"message,omitempty"`
}
type RequirementStatus struct {
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
Name string `json:"name"`
Status StatusReason `json:"status"`
Message string `json:"message"`
UUID string `json:"uuid,omitempty"`
Dependents []DependentStatus `json:"dependents,omitempty"`
}
// ClusterServiceVersionStatus represents information about the status of a pod. Status may trail the actual
// state of a system.
type ClusterServiceVersionStatus struct {
// Current condition of the ClusterServiceVersion
Phase ClusterServiceVersionPhase `json:"phase,omitempty"`
// A human readable message indicating details about why the ClusterServiceVersion is in this condition.
// +optional
Message string `json:"message,omitempty"`
// A brief CamelCase message indicating details about why the ClusterServiceVersion is in this state.
// e.g. 'RequirementsNotMet'
// +optional
Reason ConditionReason `json:"reason,omitempty"`
// Last time we updated the status
// +optional
LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"`
// Last time the status transitioned from one status to another.
// +optional
LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"`
// List of conditions, a history of state transitions
Conditions []ClusterServiceVersionCondition `json:"conditions,omitempty"`
// The status of each requirement for this CSV
RequirementStatus []RequirementStatus `json:"requirementStatus,omitempty"`
// Last time the owned APIService certs were updated
// +optional
CertsLastUpdated *metav1.Time `json:"certsLastUpdated,omitempty"`
// Time the owned APIService certs will rotate next
// +optional
CertsRotateAt *metav1.Time `json:"certsRotateAt,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// +kubebuilder:storageversion
// +kubebuilder:resource:shortName={csv, csvs},categories=olm
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Display",type=string,JSONPath=`.spec.displayName`,description="The name of the CSV"
// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.version`,description="The version of the CSV"
// +kubebuilder:printcolumn:name="Replaces",type=string,JSONPath=`.spec.replaces`,description="The name of a CSV that this one replaces"
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
// ClusterServiceVersion is a Custom Resource of type `ClusterServiceVersionSpec`.
type ClusterServiceVersion struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec ClusterServiceVersionSpec `json:"spec"`
// +optional
Status ClusterServiceVersionStatus `json:"status"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ClusterServiceVersionList represents a list of ClusterServiceVersions.
type ClusterServiceVersionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []ClusterServiceVersion `json:"items"`
}
// GetAllCRDDescriptions returns a deduplicated set of CRDDescriptions that is
// the union of the owned and required CRDDescriptions.
//
// Descriptions with the same name prefer the value in Owned.
// Descriptions are returned in alphabetical order.
func (csv ClusterServiceVersion) GetAllCRDDescriptions() []CRDDescription {
set := make(map[string]CRDDescription)
for _, required := range csv.Spec.CustomResourceDefinitions.Required {
set[required.Name] = required
}
for _, owned := range csv.Spec.CustomResourceDefinitions.Owned {
set[owned.Name] = owned
}
keys := make([]string, 0)
for key := range set {
keys = append(keys, key)
}
sort.StringSlice(keys).Sort()
descs := make([]CRDDescription, 0)
for _, key := range keys {
descs = append(descs, set[key])
}
return descs
}
// GetAllAPIServiceDescriptions returns a deduplicated set of APIServiceDescriptions that is
// the union of the owned and required APIServiceDescriptions.
//
// Descriptions with the same name prefer the value in Owned.
// Descriptions are returned in alphabetical order.
func (csv ClusterServiceVersion) GetAllAPIServiceDescriptions() []APIServiceDescription {
set := make(map[string]APIServiceDescription)
for _, required := range csv.Spec.APIServiceDefinitions.Required {
name := fmt.Sprintf("%s.%s", required.Version, required.Group)
set[name] = required
}
for _, owned := range csv.Spec.APIServiceDefinitions.Owned {
name := fmt.Sprintf("%s.%s", owned.Version, owned.Group)
set[name] = owned
}
keys := make([]string, 0)
for key := range set {
keys = append(keys, key)
}
sort.StringSlice(keys).Sort()
descs := make([]APIServiceDescription, 0)
for _, key := range keys {
descs = append(descs, set[key])
}
return descs
}
// GetRequiredAPIServiceDescriptions returns a deduplicated set of required APIServiceDescriptions
// with the intersection of required and owned removed
// Equivalent to the set subtraction required - owned
//
// Descriptions are returned in alphabetical order.
func (csv ClusterServiceVersion) GetRequiredAPIServiceDescriptions() []APIServiceDescription {
set := make(map[string]APIServiceDescription)
for _, required := range csv.Spec.APIServiceDefinitions.Required {
name := fmt.Sprintf("%s.%s", required.Version, required.Group)
set[name] = required
}
// Remove any shared owned from the set
for _, owned := range csv.Spec.APIServiceDefinitions.Owned {
name := fmt.Sprintf("%s.%s", owned.Version, owned.Group)
if _, ok := set[name]; ok {
delete(set, name)
}
}
keys := make([]string, 0)
for key := range set {
keys = append(keys, key)
}
sort.StringSlice(keys).Sort()
descs := make([]APIServiceDescription, 0)
for _, key := range keys {
descs = append(descs, set[key])
}
return descs
}
// GetOwnedAPIServiceDescriptions returns a deduplicated set of owned APIServiceDescriptions
//
// Descriptions are returned in alphabetical order.
func (csv ClusterServiceVersion) GetOwnedAPIServiceDescriptions() []APIServiceDescription {
set := make(map[string]APIServiceDescription)
for _, owned := range csv.Spec.APIServiceDefinitions.Owned {
name := owned.GetName()
set[name] = owned
}
keys := make([]string, 0)
for key := range set {
keys = append(keys, key)
}
sort.StringSlice(keys).Sort()
descs := make([]APIServiceDescription, 0)
for _, key := range keys {
descs = append(descs, set[key])
}
return descs
}

View File

@ -0,0 +1,6 @@
// +groupName=operators.coreos.com
// +k8s:deepcopy-gen=package
// +k8s:conversion-gen=github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators
// Package v1alpha1 contains resources types for version v1alpha1 of the operators.coreos.com API group.
package v1alpha1

View File

@ -0,0 +1,373 @@
package v1alpha1
import (
"errors"
"fmt"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
InstallPlanKind = "InstallPlan"
InstallPlanAPIVersion = GroupName + "/" + GroupVersion
)
// Approval is the user approval policy for an InstallPlan.
// It must be one of "Automatic" or "Manual".
type Approval string
const (
ApprovalAutomatic Approval = "Automatic"
ApprovalManual Approval = "Manual"
)
// InstallPlanSpec defines a set of Application resources to be installed
type InstallPlanSpec struct {
CatalogSource string `json:"source,omitempty"`
CatalogSourceNamespace string `json:"sourceNamespace,omitempty"`
ClusterServiceVersionNames []string `json:"clusterServiceVersionNames"`
Approval Approval `json:"approval"`
Approved bool `json:"approved"`
Generation int `json:"generation,omitempty"`
}
// InstallPlanPhase is the current status of a InstallPlan as a whole.
type InstallPlanPhase string
const (
InstallPlanPhaseNone InstallPlanPhase = ""
InstallPlanPhasePlanning InstallPlanPhase = "Planning"
InstallPlanPhaseRequiresApproval InstallPlanPhase = "RequiresApproval"
InstallPlanPhaseInstalling InstallPlanPhase = "Installing"
InstallPlanPhaseComplete InstallPlanPhase = "Complete"
InstallPlanPhaseFailed InstallPlanPhase = "Failed"
)
// InstallPlanConditionType describes the state of an InstallPlan at a certain point as a whole.
type InstallPlanConditionType string
const (
InstallPlanResolved InstallPlanConditionType = "Resolved"
InstallPlanInstalled InstallPlanConditionType = "Installed"
)
// ConditionReason is a camelcased reason for the state transition.
type InstallPlanConditionReason string
const (
InstallPlanReasonPlanUnknown InstallPlanConditionReason = "PlanUnknown"
InstallPlanReasonInstallCheckFailed InstallPlanConditionReason = "InstallCheckFailed"
InstallPlanReasonDependencyConflict InstallPlanConditionReason = "DependenciesConflict"
InstallPlanReasonComponentFailed InstallPlanConditionReason = "InstallComponentFailed"
)
// StepStatus is the current status of a particular resource an in
// InstallPlan
type StepStatus string
const (
StepStatusUnknown StepStatus = "Unknown"
StepStatusNotPresent StepStatus = "NotPresent"
StepStatusPresent StepStatus = "Present"
StepStatusCreated StepStatus = "Created"
StepStatusWaitingForAPI StepStatus = "WaitingForApi"
StepStatusUnsupportedResource StepStatus = "UnsupportedResource"
)
// ErrInvalidInstallPlan is the error returned by functions that operate on
// InstallPlans when the InstallPlan does not contain totally valid data.
var ErrInvalidInstallPlan = errors.New("the InstallPlan contains invalid data")
// InstallPlanStatus represents the information about the status of
// steps required to complete installation.
//
// Status may trail the actual state of a system.
type InstallPlanStatus struct {
Phase InstallPlanPhase `json:"phase"`
Conditions []InstallPlanCondition `json:"conditions,omitempty"`
CatalogSources []string `json:"catalogSources"`
Plan []*Step `json:"plan,omitempty"`
// BundleLookups is the set of in-progress requests to pull and unpackage bundle content to the cluster.
// +optional
BundleLookups []BundleLookup `json:"bundleLookups,omitempty"`
// AttenuatedServiceAccountRef references the service account that is used
// to do scoped operator install.
AttenuatedServiceAccountRef *corev1.ObjectReference `json:"attenuatedServiceAccountRef,omitempty"`
}
// InstallPlanCondition represents the overall status of the execution of
// an InstallPlan.
type InstallPlanCondition struct {
Type InstallPlanConditionType `json:"type,omitempty"`
Status corev1.ConditionStatus `json:"status,omitempty"` // True, False, or Unknown
LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"`
LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"`
Reason InstallPlanConditionReason `json:"reason,omitempty"`
Message string `json:"message,omitempty"`
}
// allow overwriting `now` function for deterministic tests
var now = metav1.Now
// GetCondition returns the InstallPlanCondition of the given type if it exists in the InstallPlanStatus' Conditions.
// Returns a condition of the given type with a ConditionStatus of "Unknown" if not found.
func (s InstallPlanStatus) GetCondition(conditionType InstallPlanConditionType) InstallPlanCondition {
for _, cond := range s.Conditions {
if cond.Type == conditionType {
return cond
}
}
return InstallPlanCondition{
Type: conditionType,
Status: corev1.ConditionUnknown,
}
}
// SetCondition adds or updates a condition, using `Type` as merge key.
func (s *InstallPlanStatus) SetCondition(cond InstallPlanCondition) InstallPlanCondition {
for i, existing := range s.Conditions {
if existing.Type != cond.Type {
continue
}
if existing.Status == cond.Status {
cond.LastTransitionTime = existing.LastTransitionTime
}
s.Conditions[i] = cond
return cond
}
s.Conditions = append(s.Conditions, cond)
return cond
}
func OrderSteps(steps []*Step) []*Step {
// CSVs must be applied first
csvList := []*Step{}
// CRDs must be applied second
crdList := []*Step{}
// Other resources may be applied in any order
remainingResources := []*Step{}
for _, step := range steps {
switch step.Resource.Kind {
case crdKind:
crdList = append(crdList, step)
case ClusterServiceVersionKind:
csvList = append(csvList, step)
default:
remainingResources = append(remainingResources, step)
}
}
result := make([]*Step, len(steps))
i := 0
for j := range csvList {
result[i] = csvList[j]
i++
}
for j := range crdList {
result[i] = crdList[j]
i++
}
for j := range remainingResources {
result[i] = remainingResources[j]
i++
}
return result
}
func (s InstallPlanStatus) NeedsRequeue() bool {
for _, step := range s.Plan {
switch step.Status {
case StepStatusWaitingForAPI:
return true
}
}
return false
}
func ConditionFailed(cond InstallPlanConditionType, reason InstallPlanConditionReason, message string, now *metav1.Time) InstallPlanCondition {
return InstallPlanCondition{
Type: cond,
Status: corev1.ConditionFalse,
Reason: reason,
Message: message,
LastUpdateTime: now,
LastTransitionTime: now,
}
}
func ConditionMet(cond InstallPlanConditionType, now *metav1.Time) InstallPlanCondition {
return InstallPlanCondition{
Type: cond,
Status: corev1.ConditionTrue,
LastUpdateTime: now,
LastTransitionTime: now,
}
}
// Step represents the status of an individual step in an InstallPlan.
type Step struct {
Resolving string `json:"resolving"`
Resource StepResource `json:"resource"`
Status StepStatus `json:"status"`
}
// BundleLookupConditionType is a category of the overall state of a BundleLookup.
type BundleLookupConditionType string
const (
// BundleLookupPending describes BundleLookups that are not complete.
BundleLookupPending BundleLookupConditionType = "BundleLookupPending"
crdKind = "CustomResourceDefinition"
)
type BundleLookupCondition struct {
// Type of condition.
Type BundleLookupConditionType `json:"type"`
// Status of the condition, one of True, False, Unknown.
Status corev1.ConditionStatus `json:"status"`
// The reason for the condition's last transition.
// +optional
Reason string `json:"reason,omitempty"`
// A human readable message indicating details about the transition.
// +optional
Message string `json:"message,omitempty"`
// Last time the condition was probed.
// +optional
LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"`
// Last time the condition transitioned from one status to another.
// +optional
LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"`
}
// BundleLookup is a request to pull and unpackage the content of a bundle to the cluster.
type BundleLookup struct {
// Path refers to the location of a bundle to pull.
// It's typically an image reference.
Path string `json:"path"`
// Identifier is the catalog-unique name of the operator (the name of the CSV for bundles that contain CSVs)
Identifier string `json:"identifier"`
// Replaces is the name of the bundle to replace with the one found at Path.
Replaces string `json:"replaces"`
// CatalogSourceRef is a reference to the CatalogSource the bundle path was resolved from.
CatalogSourceRef *corev1.ObjectReference `json:"catalogSourceRef"`
// Conditions represents the overall state of a BundleLookup.
// +optional
Conditions []BundleLookupCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// The effective properties of the unpacked bundle.
// +optional
Properties string `json:"properties,omitempty"`
}
// GetCondition returns the BundleLookupCondition of the given type if it exists in the BundleLookup's Conditions.
// Returns a condition of the given type with a ConditionStatus of "Unknown" if not found.
func (b BundleLookup) GetCondition(conditionType BundleLookupConditionType) BundleLookupCondition {
for _, cond := range b.Conditions {
if cond.Type == conditionType {
return cond
}
}
return BundleLookupCondition{
Type: conditionType,
Status: corev1.ConditionUnknown,
}
}
// RemoveCondition removes the BundleLookupCondition of the given type from the BundleLookup's Conditions if it exists.
func (b *BundleLookup) RemoveCondition(conditionType BundleLookupConditionType) {
for i, cond := range b.Conditions {
if cond.Type == conditionType {
b.Conditions = append(b.Conditions[:i], b.Conditions[i+1:]...)
if len(b.Conditions) == 0 {
b.Conditions = nil
}
return
}
}
}
// SetCondition replaces the existing BundleLookupCondition of the same type, or adds it if it was not found.
func (b *BundleLookup) SetCondition(cond BundleLookupCondition) BundleLookupCondition {
for i, existing := range b.Conditions {
if existing.Type != cond.Type {
continue
}
if existing.Status == cond.Status {
cond.LastTransitionTime = existing.LastTransitionTime
}
b.Conditions[i] = cond
return cond
}
b.Conditions = append(b.Conditions, cond)
return cond
}
func (s *Step) String() string {
return fmt.Sprintf("%s: %s (%s)", s.Resolving, s.Resource, s.Status)
}
// StepResource represents the status of a resource to be tracked by an
// InstallPlan.
type StepResource struct {
CatalogSource string `json:"sourceName"`
CatalogSourceNamespace string `json:"sourceNamespace"`
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
Name string `json:"name"`
Manifest string `json:"manifest,omitempty"`
}
func (r StepResource) String() string {
return fmt.Sprintf("%s[%s/%s/%s (%s/%s)]", r.Name, r.Group, r.Version, r.Kind, r.CatalogSource, r.CatalogSourceNamespace)
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// +kubebuilder:resource:shortName=ip,categories=olm
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="CSV",type=string,JSONPath=`.spec.clusterServiceVersionNames[0]`,description="The first CSV in the list of clusterServiceVersionNames"
// +kubebuilder:printcolumn:name="Approval",type=string,JSONPath=`.spec.approval`,description="The approval mode"
// +kubebuilder:printcolumn:name="Approved",type=boolean,JSONPath=`.spec.approved`
// InstallPlan defines the installation of a set of operators.
type InstallPlan struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec InstallPlanSpec `json:"spec"`
// +optional
Status InstallPlanStatus `json:"status"`
}
// EnsureCatalogSource ensures that a CatalogSource is present in the Status
// block of an InstallPlan.
func (p *InstallPlan) EnsureCatalogSource(sourceName string) {
for _, srcName := range p.Status.CatalogSources {
if srcName == sourceName {
return
}
}
p.Status.CatalogSources = append(p.Status.CatalogSources, sourceName)
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// InstallPlanList is a list of InstallPlan resources.
type InstallPlanList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []InstallPlan `json:"items"`
}

View File

@ -0,0 +1,55 @@
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/operator-framework/api/pkg/operators"
)
const (
// GroupName is the group name used in this package.
GroupName = operators.GroupName
// GroupVersion is the group version used in this package.
GroupVersion = "v1alpha1"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: GroupVersion}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
// SchemeBuilder initializes a scheme builder
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
// AddToScheme is a global function that registers this API group & version to a scheme
AddToScheme = SchemeBuilder.AddToScheme
// localSchemeBuilder is expected by generated conversion functions
localSchemeBuilder = &SchemeBuilder
)
// addKnownTypes adds the list of known types to Scheme
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&CatalogSource{},
&CatalogSourceList{},
&InstallPlan{},
&InstallPlanList{},
&Subscription{},
&SubscriptionList{},
&ClusterServiceVersion{},
&ClusterServiceVersionList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View File

@ -0,0 +1,325 @@
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
const (
SubscriptionKind = "Subscription"
SubscriptionCRDAPIVersion = GroupName + "/" + GroupVersion
)
// SubscriptionState tracks when updates are available, installing, or service is up to date
type SubscriptionState string
const (
SubscriptionStateNone = ""
SubscriptionStateFailed = "UpgradeFailed"
SubscriptionStateUpgradeAvailable = "UpgradeAvailable"
SubscriptionStateUpgradePending = "UpgradePending"
SubscriptionStateAtLatest = "AtLatestKnown"
)
const (
SubscriptionReasonInvalidCatalog ConditionReason = "InvalidCatalog"
SubscriptionReasonUpgradeSucceeded ConditionReason = "UpgradeSucceeded"
)
// SubscriptionSpec defines an Application that can be installed
type SubscriptionSpec struct {
CatalogSource string `json:"source"`
CatalogSourceNamespace string `json:"sourceNamespace"`
Package string `json:"name"`
Channel string `json:"channel,omitempty"`
StartingCSV string `json:"startingCSV,omitempty"`
InstallPlanApproval Approval `json:"installPlanApproval,omitempty"`
Config SubscriptionConfig `json:"config,omitempty"`
}
// SubscriptionConfig contains configuration specified for a subscription.
type SubscriptionConfig struct {
// Selector is the label selector for pods to be configured.
// Existing ReplicaSets whose pods are
// selected by this will be the ones affected by this deployment.
// It must match the pod template's labels.
Selector *metav1.LabelSelector `json:"selector,omitempty"`
// NodeSelector is a selector which must be true for the pod to fit on a node.
// Selector which must match a node's labels for the pod to be scheduled on that node.
// More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
// +optional
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// Tolerations are the pod's tolerations.
// +optional
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
// Resources represents compute resources required by this container.
// Immutable.
// More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/
// +optional
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
// EnvFrom is a list of sources to populate environment variables in the container.
// The keys defined within a source must be a C_IDENTIFIER. All invalid keys
// will be reported as an event when the container is starting. When a key exists in multiple
// sources, the value associated with the last source will take precedence.
// Values defined by an Env with a duplicate key will take precedence.
// Immutable.
// +optional
EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"`
// Env is a list of environment variables to set in the container.
// Cannot be updated.
// +patchMergeKey=name
// +patchStrategy=merge
// +optional
Env []corev1.EnvVar `json:"env,omitempty" patchMergeKey:"name" patchStrategy:"merge"`
// List of Volumes to set in the podSpec.
// +optional
Volumes []corev1.Volume `json:"volumes,omitempty"`
// List of VolumeMounts to set in the container.
// +optional
VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`
}
// SubscriptionConditionType indicates an explicit state condition about a Subscription in "abnormal-true"
// polarity form (see https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties).
type SubscriptionConditionType string
const (
// SubscriptionCatalogSourcesUnhealthy indicates that some or all of the CatalogSources to be used in resolution are unhealthy.
SubscriptionCatalogSourcesUnhealthy SubscriptionConditionType = "CatalogSourcesUnhealthy"
// SubscriptionInstallPlanMissing indicates that a Subscription's InstallPlan is missing.
SubscriptionInstallPlanMissing SubscriptionConditionType = "InstallPlanMissing"
// SubscriptionInstallPlanPending indicates that a Subscription's InstallPlan is pending installation.
SubscriptionInstallPlanPending SubscriptionConditionType = "InstallPlanPending"
// SubscriptionInstallPlanFailed indicates that the installation of a Subscription's InstallPlan has failed.
SubscriptionInstallPlanFailed SubscriptionConditionType = "InstallPlanFailed"
)
const (
// NoCatalogSourcesFound is a reason string for Subscriptions with unhealthy CatalogSources due to none being available.
NoCatalogSourcesFound = "NoCatalogSourcesFound"
// AllCatalogSourcesHealthy is a reason string for Subscriptions that transitioned due to all CatalogSources being healthy.
AllCatalogSourcesHealthy = "AllCatalogSourcesHealthy"
// CatalogSourcesAdded is a reason string for Subscriptions that transitioned due to CatalogSources being added.
CatalogSourcesAdded = "CatalogSourcesAdded"
// CatalogSourcesUpdated is a reason string for Subscriptions that transitioned due to CatalogSource being updated.
CatalogSourcesUpdated = "CatalogSourcesUpdated"
// CatalogSourcesDeleted is a reason string for Subscriptions that transitioned due to CatalogSources being removed.
CatalogSourcesDeleted = "CatalogSourcesDeleted"
// UnhealthyCatalogSourceFound is a reason string for Subscriptions that transitioned because an unhealthy CatalogSource was found.
UnhealthyCatalogSourceFound = "UnhealthyCatalogSourceFound"
// ReferencedInstallPlanNotFound is a reason string for Subscriptions that transitioned due to a referenced InstallPlan not being found.
ReferencedInstallPlanNotFound = "ReferencedInstallPlanNotFound"
// InstallPlanNotYetReconciled is a reason string for Subscriptions that transitioned due to a referenced InstallPlan not being reconciled yet.
InstallPlanNotYetReconciled = "InstallPlanNotYetReconciled"
// InstallPlanFailed is a reason string for Subscriptions that transitioned due to a referenced InstallPlan failing without setting an explicit failure condition.
InstallPlanFailed = "InstallPlanFailed"
)
// SubscriptionCondition represents the latest available observations of a Subscription's state.
type SubscriptionCondition struct {
// Type is the type of Subscription condition.
Type SubscriptionConditionType `json:"type" description:"type of Subscription condition"`
// Status is the status of the condition, one of True, False, Unknown.
Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"`
// Reason is a one-word CamelCase reason for the condition's last transition.
// +optional
Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"`
// Message is a human-readable message indicating details about last transition.
// +optional
Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"`
// LastHeartbeatTime is the last time we got an update on a given condition
// +optional
LastHeartbeatTime *metav1.Time `json:"lastHeartbeatTime,omitempty" description:"last time we got an update on a given condition"`
// LastTransitionTime is the last time the condition transit from one status to another
// +optional
LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty" description:"last time the condition transit from one status to another" hash:"ignore"`
}
// Equals returns true if a SubscriptionCondition equals the one given, false otherwise.
// Equality is determined by the equality of the type, status, reason, and message fields ONLY.
func (s SubscriptionCondition) Equals(condition SubscriptionCondition) bool {
return s.Type == condition.Type && s.Status == condition.Status && s.Reason == condition.Reason && s.Message == condition.Message
}
type SubscriptionStatus struct {
// CurrentCSV is the CSV the Subscription is progressing to.
// +optional
CurrentCSV string `json:"currentCSV,omitempty"`
// InstalledCSV is the CSV currently installed by the Subscription.
// +optional
InstalledCSV string `json:"installedCSV,omitempty"`
// Install is a reference to the latest InstallPlan generated for the Subscription.
// DEPRECATED: InstallPlanRef
// +optional
Install *InstallPlanReference `json:"installplan,omitempty"`
// State represents the current state of the Subscription
// +optional
State SubscriptionState `json:"state,omitempty"`
// Reason is the reason the Subscription was transitioned to its current state.
// +optional
Reason ConditionReason `json:"reason,omitempty"`
// InstallPlanGeneration is the current generation of the installplan
// +optional
InstallPlanGeneration int `json:"installPlanGeneration,omitempty"`
// InstallPlanRef is a reference to the latest InstallPlan that contains the Subscription's current CSV.
// +optional
InstallPlanRef *corev1.ObjectReference `json:"installPlanRef,omitempty"`
// CatalogHealth contains the Subscription's view of its relevant CatalogSources' status.
// It is used to determine SubscriptionStatusConditions related to CatalogSources.
// +optional
CatalogHealth []SubscriptionCatalogHealth `json:"catalogHealth,omitempty"`
// Conditions is a list of the latest available observations about a Subscription's current state.
// +optional
Conditions []SubscriptionCondition `json:"conditions,omitempty" hash:"set"`
// LastUpdated represents the last time that the Subscription status was updated.
LastUpdated metav1.Time `json:"lastUpdated"`
}
// GetCondition returns the SubscriptionCondition of the given type if it exists in the SubscriptionStatus' Conditions.
// Returns a condition of the given type with a ConditionStatus of "Unknown" if not found.
func (s SubscriptionStatus) GetCondition(conditionType SubscriptionConditionType) SubscriptionCondition {
for _, cond := range s.Conditions {
if cond.Type == conditionType {
return cond
}
}
return SubscriptionCondition{
Type: conditionType,
Status: corev1.ConditionUnknown,
}
}
// SetCondition sets the given SubscriptionCondition in the SubscriptionStatus' Conditions.
func (s *SubscriptionStatus) SetCondition(condition SubscriptionCondition) {
for i, cond := range s.Conditions {
if cond.Type == condition.Type {
s.Conditions[i] = condition
return
}
}
s.Conditions = append(s.Conditions, condition)
}
// RemoveConditions removes any conditions of the given types from the SubscriptionStatus' Conditions.
func (s *SubscriptionStatus) RemoveConditions(remove ...SubscriptionConditionType) {
exclusions := map[SubscriptionConditionType]struct{}{}
for _, r := range remove {
exclusions[r] = struct{}{}
}
var filtered []SubscriptionCondition
for _, cond := range s.Conditions {
if _, ok := exclusions[cond.Type]; ok {
// Skip excluded condition types
continue
}
filtered = append(filtered, cond)
}
s.Conditions = filtered
}
type InstallPlanReference struct {
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
Name string `json:"name"`
UID types.UID `json:"uuid"`
}
// SubscriptionCatalogHealth describes the health of a CatalogSource the Subscription knows about.
type SubscriptionCatalogHealth struct {
// CatalogSourceRef is a reference to a CatalogSource.
CatalogSourceRef *corev1.ObjectReference `json:"catalogSourceRef"`
// LastUpdated represents the last time that the CatalogSourceHealth changed
LastUpdated *metav1.Time `json:"lastUpdated"`
// Healthy is true if the CatalogSource is healthy; false otherwise.
Healthy bool `json:"healthy"`
}
// Equals returns true if a SubscriptionCatalogHealth equals the one given, false otherwise.
// Equality is based SOLEY on health and UID.
func (s SubscriptionCatalogHealth) Equals(health SubscriptionCatalogHealth) bool {
return s.Healthy == health.Healthy && s.CatalogSourceRef.UID == health.CatalogSourceRef.UID
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// +kubebuilder:resource:shortName={sub, subs},categories=olm
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Package",type=string,JSONPath=`.spec.name`,description="The package subscribed to"
// +kubebuilder:printcolumn:name="Source",type=string,JSONPath=`.spec.source`,description="The catalog source for the specified package"
// +kubebuilder:printcolumn:name="Channel",type=string,JSONPath=`.spec.channel`,description="The channel of updates to subscribe to"
// Subscription keeps operators up to date by tracking changes to Catalogs.
type Subscription struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec *SubscriptionSpec `json:"spec"`
// +optional
Status SubscriptionStatus `json:"status"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// SubscriptionList is a list of Subscription resources.
type SubscriptionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Subscription `json:"items"`
}
// GetInstallPlanApproval gets the configured install plan approval or the default
func (s *Subscription) GetInstallPlanApproval() Approval {
if s.Spec.InstallPlanApproval == ApprovalManual {
return ApprovalManual
}
return ApprovalAutomatic
}
// NewInstallPlanReference returns an InstallPlanReference for the given ObjectReference.
func NewInstallPlanReference(ref *corev1.ObjectReference) *InstallPlanReference {
return &InstallPlanReference{
APIVersion: ref.APIVersion,
Kind: ref.Kind,
Name: ref.Name,
UID: ref.UID,
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,123 @@
package operators
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
// CatalogSourceKind is the PascalCase name of a CatalogSource's kind.
const CatalogSourceKind = "CatalogSource"
// SourceType indicates the type of backing store for a CatalogSource
type SourceType string
const (
// SourceTypeInternal (deprecated) specifies a CatalogSource of type SourceTypeConfigmap
SourceTypeInternal SourceType = "internal"
// SourceTypeConfigmap specifies a CatalogSource that generates a configmap-server registry
SourceTypeConfigmap SourceType = "configmap"
// SourceTypeGrpc specifies a CatalogSource that can use an operator registry image to generate a
// registry-server or connect to a pre-existing registry at an address.
SourceTypeGrpc SourceType = "grpc"
)
type CatalogSourceSpec struct {
// SourceType is the type of source
SourceType SourceType
// ConfigMap is the name of the ConfigMap to be used to back a configmap-server registry.
// Only used when SourceType = SourceTypeConfigmap or SourceTypeInternal.
// +Optional
ConfigMap string
// Address is a host that OLM can use to connect to a pre-existing registry.
// Format: <registry-host or ip>:<port>
// Only used when SourceType = SourceTypeGrpc.
// Ignored when the Image field is set.
// +Optional
Address string
// Image is an operator-registry container image to instantiate a registry-server with.
// Only used when SourceType = SourceTypeGrpc.
// If present, the address field is ignored.
// +Optional
Image string
// Secrets represent set of secrets that can be used to access the contents of the catalog.
// It is best to keep this list small, since each will need to be tried for every catalog entry.
// +Optional
Secrets []string
// Metadata
DisplayName string
Description string
Publisher string
Icon Icon
}
type RegistryServiceStatus struct {
Protocol string
ServiceName string
ServiceNamespace string
Port string
CreatedAt metav1.Time
}
type GRPCConnectionState struct {
Address string
LastObservedState string
LastConnectTime metav1.Time
}
func (s *RegistryServiceStatus) Address() string {
return fmt.Sprintf("%s.%s.svc:%s", s.ServiceName, s.ServiceNamespace, s.Port)
}
type CatalogSourceStatus struct {
Message string `json:"message,omitempty"`
Reason ConditionReason `json:"reason,omitempty"`
ConfigMapResource *ConfigMapResourceReference
RegistryServiceStatus *RegistryServiceStatus
GRPCConnectionState *GRPCConnectionState
}
type ConfigMapResourceReference struct {
Name string
Namespace string
UID types.UID
ResourceVersion string
LastUpdateTime metav1.Time
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// CatalogSource is a repository of CSVs, CRDs, and operator packages.
type CatalogSource struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec CatalogSourceSpec
Status CatalogSourceStatus
}
func (c *CatalogSource) Address() string {
if c.Spec.Address != "" {
return c.Spec.Address
}
return c.Status.RegistryServiceStatus.Address()
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// CatalogSourceList is a list of CatalogSource resources.
type CatalogSourceList struct {
metav1.TypeMeta
metav1.ListMeta
Items []CatalogSource
}

View File

@ -0,0 +1,488 @@
package operators
import (
"encoding/json"
"fmt"
"sort"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version"
)
// ClusterServiceVersionKind is the PascalCase name of a CSV's kind.
const ClusterServiceVersionKind = "ClusterServiceVersion"
// InstallModeType is a supported type of install mode for CSV installation
type InstallModeType string
const (
// InstallModeTypeOwnNamespace indicates that the operator can be a member of an `OperatorGroup` that selects its own namespace.
InstallModeTypeOwnNamespace InstallModeType = "OwnNamespace"
// InstallModeTypeSingleNamespace indicates that the operator can be a member of an `OperatorGroup` that selects one namespace.
InstallModeTypeSingleNamespace InstallModeType = "SingleNamespace"
// InstallModeTypeMultiNamespace indicates that the operator can be a member of an `OperatorGroup` that selects more than one namespace.
InstallModeTypeMultiNamespace InstallModeType = "MultiNamespace"
// InstallModeTypeAllNamespaces indicates that the operator can be a member of an `OperatorGroup` that selects all namespaces (target namespace set is the empty string "").
InstallModeTypeAllNamespaces InstallModeType = "AllNamespaces"
)
// InstallMode associates an InstallModeType with a flag representing if the CSV supports it
type InstallMode struct {
Type InstallModeType
Supported bool
}
// InstallModeSet is a mapping of unique InstallModeTypes to whether they are supported.
type InstallModeSet map[InstallModeType]bool
// NamedInstallStrategy represents the block of an ClusterServiceVersion resource
// where the install strategy is specified.
type NamedInstallStrategy struct {
StrategyName string
StrategySpecRaw json.RawMessage
}
// StatusDescriptor describes a field in a status block of a CRD so that OLM can consume it
type StatusDescriptor struct {
Path string
DisplayName string
Description string
XDescriptors []string
Value *json.RawMessage
}
// SpecDescriptor describes a field in a spec block of a CRD so that OLM can consume it
type SpecDescriptor struct {
Path string
DisplayName string
Description string
XDescriptors []string
Value *json.RawMessage
}
// ActionDescriptor describes a declarative action that can be performed on a custom resource instance
type ActionDescriptor struct {
Path string
DisplayName string
Description string
XDescriptors []string
Value *json.RawMessage
}
// CRDDescription provides details to OLM about the CRDs
type CRDDescription struct {
Name string
Version string
Kind string
DisplayName string
Description string
Resources []APIResourceReference
StatusDescriptors []StatusDescriptor
SpecDescriptors []SpecDescriptor
ActionDescriptor []ActionDescriptor
}
// APIServiceDescription provides details to OLM about apis provided via aggregation
type APIServiceDescription struct {
Name string
Group string
Version string
Kind string
DeploymentName string
ContainerPort int32
DisplayName string
Description string
Resources []APIResourceReference
StatusDescriptors []StatusDescriptor
SpecDescriptors []SpecDescriptor
ActionDescriptor []ActionDescriptor
}
// APIResourceReference is a Kubernetes resource type used by a custom resource
type APIResourceReference struct {
Name string
Kind string
Version string
}
// GetName returns the name of an APIService as derived from its group and version.
func (d APIServiceDescription) GetName() string {
return fmt.Sprintf("%s.%s", d.Version, d.Group)
}
// CustomResourceDefinitions declares all of the CRDs managed or required by
// an operator being ran by ClusterServiceVersion.
//
// If the CRD is present in the Owned list, it is implicitly required.
type CustomResourceDefinitions struct {
Owned []CRDDescription
Required []CRDDescription
}
// APIServiceDefinitions declares all of the extension apis managed or required by
// an operator being ran by ClusterServiceVersion.
type APIServiceDefinitions struct {
Owned []APIServiceDescription
Required []APIServiceDescription
}
// ClusterServiceVersionSpec declarations tell OLM how to install an operator
// that can manage apps for a given version.
type ClusterServiceVersionSpec struct {
InstallStrategy NamedInstallStrategy
Version version.OperatorVersion
Maturity string
CustomResourceDefinitions CustomResourceDefinitions
APIServiceDefinitions APIServiceDefinitions
NativeAPIs []metav1.GroupVersionKind
MinKubeVersion string
DisplayName string
Description string
Keywords []string
Maintainers []Maintainer
Provider AppLink
Links []AppLink
Icon []Icon
// InstallModes specify supported installation types
// +optional
InstallModes []InstallMode
// The name of a CSV this one replaces. Should match the `metadata.Name` field of the old CSV.
// +optional
Replaces string
// Map of string keys and values that can be used to organize and categorize
// (scope and select) objects.
// +optional
Labels map[string]string
// Annotations is an unstructured key value map stored with a resource that may be
// set by external tools to store and retrieve arbitrary metadata.
// +optional
Annotations map[string]string
// Label selector for related resources.
// +optional
Selector *metav1.LabelSelector
}
type Maintainer struct {
Name string
Email string
}
type AppLink struct {
Name string
URL string
}
type Icon struct {
Data string
MediaType string
}
// ClusterServiceVersionPhase is a label for the condition of a ClusterServiceVersion at the current time.
type ClusterServiceVersionPhase string
// These are the valid phases of ClusterServiceVersion
const (
CSVPhaseNone = ""
// CSVPhasePending means the csv has been accepted by the system, but the install strategy has not been attempted.
// This is likely because there are unmet requirements.
CSVPhasePending ClusterServiceVersionPhase = "Pending"
// CSVPhaseInstallReady means that the requirements are met but the install strategy has not been run.
CSVPhaseInstallReady ClusterServiceVersionPhase = "InstallReady"
// CSVPhaseInstalling means that the install strategy has been initiated but not completed.
CSVPhaseInstalling ClusterServiceVersionPhase = "Installing"
// CSVPhaseSucceeded means that the resources in the CSV were created successfully.
CSVPhaseSucceeded ClusterServiceVersionPhase = "Succeeded"
// CSVPhaseFailed means that the install strategy could not be successfully completed.
CSVPhaseFailed ClusterServiceVersionPhase = "Failed"
// CSVPhaseUnknown means that for some reason the state of the csv could not be obtained.
CSVPhaseUnknown ClusterServiceVersionPhase = "Unknown"
// CSVPhaseReplacing means that a newer CSV has been created and the csv's resources will be transitioned to a new owner.
CSVPhaseReplacing ClusterServiceVersionPhase = "Replacing"
// CSVPhaseDeleting means that a CSV has been replaced by a new one and will be checked for safety before being deleted
CSVPhaseDeleting ClusterServiceVersionPhase = "Deleting"
// CSVPhaseAny matches all other phases in CSV queries
CSVPhaseAny ClusterServiceVersionPhase = ""
)
// ConditionReason is a camelcased reason for the state transition
type ConditionReason string
const (
CSVReasonRequirementsUnknown ConditionReason = "RequirementsUnknown"
CSVReasonRequirementsNotMet ConditionReason = "RequirementsNotMet"
CSVReasonRequirementsMet ConditionReason = "AllRequirementsMet"
CSVReasonOwnerConflict ConditionReason = "OwnerConflict"
CSVReasonComponentFailed ConditionReason = "InstallComponentFailed"
CSVReasonInvalidStrategy ConditionReason = "InvalidInstallStrategy"
CSVReasonWaiting ConditionReason = "InstallWaiting"
CSVReasonInstallSuccessful ConditionReason = "InstallSucceeded"
CSVReasonInstallCheckFailed ConditionReason = "InstallCheckFailed"
CSVReasonComponentUnhealthy ConditionReason = "ComponentUnhealthy"
CSVReasonBeingReplaced ConditionReason = "BeingReplaced"
CSVReasonReplaced ConditionReason = "Replaced"
CSVReasonNeedsReinstall ConditionReason = "NeedsReinstall"
CSVReasonNeedsCertRotation ConditionReason = "NeedsCertRotation"
CSVReasonAPIServiceResourceIssue ConditionReason = "APIServiceResourceIssue"
CSVReasonAPIServiceResourcesNeedReinstall ConditionReason = "APIServiceResourcesNeedReinstall"
CSVReasonAPIServiceInstallFailed ConditionReason = "APIServiceInstallFailed"
CSVReasonCopied ConditionReason = "Copied"
CSVReasonInvalidInstallModes ConditionReason = "InvalidInstallModes"
CSVReasonNoTargetNamespaces ConditionReason = "NoTargetNamespaces"
CSVReasonUnsupportedOperatorGroup ConditionReason = "UnsupportedOperatorGroup"
CSVReasonNoOperatorGroup ConditionReason = "NoOperatorGroup"
CSVReasonTooManyOperatorGroups ConditionReason = "TooManyOperatorGroups"
CSVReasonInterOperatorGroupOwnerConflict ConditionReason = "InterOperatorGroupOwnerConflict"
CSVReasonCannotModifyStaticOperatorGroupProvidedAPIs ConditionReason = "CannotModifyStaticOperatorGroupProvidedAPIs"
)
// Conditions appear in the status as a record of state transitions on the ClusterServiceVersion
type ClusterServiceVersionCondition struct {
// Condition of the ClusterServiceVersion
Phase ClusterServiceVersionPhase
// A human readable message indicating details about why the ClusterServiceVersion is in this condition.
// +optional
Message string
// A brief CamelCase message indicating details about why the ClusterServiceVersion is in this state.
// e.g. 'RequirementsNotMet'
// +optional
Reason ConditionReason
// Last time we updated the status
// +optional
LastUpdateTime metav1.Time
// Last time the status transitioned from one status to another.
// +optional
LastTransitionTime metav1.Time
}
// OwnsCRD determines whether the current CSV owns a paritcular CRD.
func (csv ClusterServiceVersion) OwnsCRD(name string) bool {
for _, desc := range csv.Spec.CustomResourceDefinitions.Owned {
if desc.Name == name {
return true
}
}
return false
}
// OwnsAPIService determines whether the current CSV owns a paritcular APIService.
func (csv ClusterServiceVersion) OwnsAPIService(name string) bool {
for _, desc := range csv.Spec.APIServiceDefinitions.Owned {
apiServiceName := fmt.Sprintf("%s.%s", desc.Version, desc.Group)
if apiServiceName == name {
return true
}
}
return false
}
// StatusReason is a camelcased reason for the status of a RequirementStatus or DependentStatus
type StatusReason string
const (
RequirementStatusReasonPresent StatusReason = "Present"
RequirementStatusReasonNotPresent StatusReason = "NotPresent"
RequirementStatusReasonPresentNotSatisfied StatusReason = "PresentNotSatisfied"
// The CRD is present but the Established condition is False (not available)
RequirementStatusReasonNotAvailable StatusReason = "PresentNotAvailable"
DependentStatusReasonSatisfied StatusReason = "Satisfied"
DependentStatusReasonNotSatisfied StatusReason = "NotSatisfied"
)
// DependentStatus is the status for a dependent requirement (to prevent infinite nesting)
type DependentStatus struct {
Group string
Version string
Kind string
Status StatusReason
UUID string
Message string
}
type RequirementStatus struct {
Group string
Version string
Kind string
Name string
Status StatusReason
Message string
UUID string
Dependents []DependentStatus
}
// ClusterServiceVersionStatus represents information about the status of a pod. Status may trail the actual
// state of a system.
type ClusterServiceVersionStatus struct {
// Current condition of the ClusterServiceVersion
Phase ClusterServiceVersionPhase
// A human readable message indicating details about why the ClusterServiceVersion is in this condition.
// +optional
Message string
// A brief CamelCase message indicating details about why the ClusterServiceVersion is in this state.
// e.g. 'RequirementsNotMet'
// +optional
Reason ConditionReason
// Last time we updated the status
// +optional
LastUpdateTime metav1.Time
// Last time the status transitioned from one status to another.
// +optional
LastTransitionTime metav1.Time
// List of conditions, a history of state transitions
Conditions []ClusterServiceVersionCondition
// The status of each requirement for this CSV
RequirementStatus []RequirementStatus
// Last time the owned APIService certs were updated
// +optional
CertsLastUpdated metav1.Time
// Time the owned APIService certs will rotate next
// +optional
CertsRotateAt metav1.Time
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// ClusterServiceVersion is a Custom Resource of type `ClusterServiceVersionSpec`.
type ClusterServiceVersion struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec ClusterServiceVersionSpec
Status ClusterServiceVersionStatus
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ClusterServiceVersionList represents a list of ClusterServiceVersions.
type ClusterServiceVersionList struct {
metav1.TypeMeta
metav1.ListMeta
Items []ClusterServiceVersion
}
// GetAllCRDDescriptions returns a deduplicated set of CRDDescriptions that is
// the union of the owned and required CRDDescriptions.
//
// Descriptions with the same name prefer the value in Owned.
// Descriptions are returned in alphabetical order.
func (csv ClusterServiceVersion) GetAllCRDDescriptions() []CRDDescription {
set := make(map[string]CRDDescription)
for _, required := range csv.Spec.CustomResourceDefinitions.Required {
set[required.Name] = required
}
for _, owned := range csv.Spec.CustomResourceDefinitions.Owned {
set[owned.Name] = owned
}
keys := make([]string, 0)
for key := range set {
keys = append(keys, key)
}
sort.StringSlice(keys).Sort()
descs := make([]CRDDescription, 0)
for _, key := range keys {
descs = append(descs, set[key])
}
return descs
}
// GetAllAPIServiceDescriptions returns a deduplicated set of APIServiceDescriptions that is
// the union of the owned and required APIServiceDescriptions.
//
// Descriptions with the same name prefer the value in Owned.
// Descriptions are returned in alphabetical order.
func (csv ClusterServiceVersion) GetAllAPIServiceDescriptions() []APIServiceDescription {
set := make(map[string]APIServiceDescription)
for _, required := range csv.Spec.APIServiceDefinitions.Required {
name := fmt.Sprintf("%s.%s", required.Version, required.Group)
set[name] = required
}
for _, owned := range csv.Spec.APIServiceDefinitions.Owned {
name := fmt.Sprintf("%s.%s", owned.Version, owned.Group)
set[name] = owned
}
keys := make([]string, 0)
for key := range set {
keys = append(keys, key)
}
sort.StringSlice(keys).Sort()
descs := make([]APIServiceDescription, 0)
for _, key := range keys {
descs = append(descs, set[key])
}
return descs
}
// GetRequiredAPIServiceDescriptions returns a deduplicated set of required APIServiceDescriptions
// with the intersection of required and owned removed
// Equivalent to the set subtraction required - owned
//
// Descriptions are returned in alphabetical order.
func (csv ClusterServiceVersion) GetRequiredAPIServiceDescriptions() []APIServiceDescription {
set := make(map[string]APIServiceDescription)
for _, required := range csv.Spec.APIServiceDefinitions.Required {
name := fmt.Sprintf("%s.%s", required.Version, required.Group)
set[name] = required
}
// Remove any shared owned from the set
for _, owned := range csv.Spec.APIServiceDefinitions.Owned {
name := fmt.Sprintf("%s.%s", owned.Version, owned.Group)
if _, ok := set[name]; ok {
delete(set, name)
}
}
keys := make([]string, 0)
for key := range set {
keys = append(keys, key)
}
sort.StringSlice(keys).Sort()
descs := make([]APIServiceDescription, 0)
for _, key := range keys {
descs = append(descs, set[key])
}
return descs
}
// GetOwnedAPIServiceDescriptions returns a deduplicated set of owned APIServiceDescriptions
//
// Descriptions are returned in alphabetical order.
func (csv ClusterServiceVersion) GetOwnedAPIServiceDescriptions() []APIServiceDescription {
set := make(map[string]APIServiceDescription)
for _, owned := range csv.Spec.APIServiceDefinitions.Owned {
name := owned.GetName()
set[name] = owned
}
keys := make([]string, 0)
for key := range set {
keys = append(keys, key)
}
sort.StringSlice(keys).Sort()
descs := make([]APIServiceDescription, 0)
for _, key := range keys {
descs = append(descs, set[key])
}
return descs
}

View File

@ -0,0 +1,5 @@
// +k8s:deepcopy-gen=package
// +groupName=operators.coreos.com
// Package operators contains all resource types of the operators.coreos.com API group.
package operators

View File

@ -0,0 +1,249 @@
package operators
import (
"errors"
"fmt"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// InstallPlanKind is the PascalCase name of an InstallPlan's kind.
const InstallPlanKind = "InstallPlan"
// Approval is the user approval policy for an InstallPlan.
type Approval string
const (
ApprovalAutomatic Approval = "Automatic"
ApprovalManual Approval = "Manual"
)
// InstallPlanSpec defines a set of Application resources to be installed
type InstallPlanSpec struct {
CatalogSource string
CatalogSourceNamespace string
ClusterServiceVersionNames []string
Approval Approval
Approved bool
}
// InstallPlanPhase is the current status of a InstallPlan as a whole.
type InstallPlanPhase string
const (
InstallPlanPhaseNone InstallPlanPhase = ""
InstallPlanPhasePlanning InstallPlanPhase = "Planning"
InstallPlanPhaseRequiresApproval InstallPlanPhase = "RequiresApproval"
InstallPlanPhaseInstalling InstallPlanPhase = "Installing"
InstallPlanPhaseComplete InstallPlanPhase = "Complete"
InstallPlanPhaseFailed InstallPlanPhase = "Failed"
)
// InstallPlanConditionType describes the state of an InstallPlan at a certain point as a whole.
type InstallPlanConditionType string
const (
InstallPlanResolved InstallPlanConditionType = "Resolved"
InstallPlanInstalled InstallPlanConditionType = "Installed"
)
// ConditionReason is a camelcased reason for the state transition.
type InstallPlanConditionReason string
const (
InstallPlanReasonPlanUnknown InstallPlanConditionReason = "PlanUnknown"
InstallPlanReasonInstallCheckFailed InstallPlanConditionReason = "InstallCheckFailed"
InstallPlanReasonDependencyConflict InstallPlanConditionReason = "DependenciesConflict"
InstallPlanReasonComponentFailed InstallPlanConditionReason = "InstallComponentFailed"
)
// StepStatus is the current status of a particular resource an in
// InstallPlan
type StepStatus string
const (
StepStatusUnknown StepStatus = "Unknown"
StepStatusNotPresent StepStatus = "NotPresent"
StepStatusPresent StepStatus = "Present"
StepStatusCreated StepStatus = "Created"
)
// ErrInvalidInstallPlan is the error returned by functions that operate on
// InstallPlans when the InstallPlan does not contain totally valid data.
var ErrInvalidInstallPlan = errors.New("the InstallPlan contains invalid data")
// InstallPlanStatus represents the information about the status of
// steps required to complete installation.
//
// Status may trail the actual state of a system.
type InstallPlanStatus struct {
Phase InstallPlanPhase
Conditions []InstallPlanCondition
CatalogSources []string
Plan []*Step
AttenuatedServiceAccountRef *corev1.ObjectReference
}
// InstallPlanCondition represents the overall status of the execution of
// an InstallPlan.
type InstallPlanCondition struct {
Type InstallPlanConditionType
Status corev1.ConditionStatus // True, False, or Unknown
LastUpdateTime metav1.Time
LastTransitionTime metav1.Time
Reason InstallPlanConditionReason
Message string
}
// allow overwriting `now` function for deterministic tests
var now = metav1.Now
// GetCondition returns the InstallPlanCondition of the given type if it exists in the InstallPlanStatus' Conditions.
// Returns a condition of the given type with a ConditionStatus of "Unknown" if not found.
func (s InstallPlanStatus) GetCondition(conditionType InstallPlanConditionType) InstallPlanCondition {
for _, cond := range s.Conditions {
if cond.Type == conditionType {
return cond
}
}
return InstallPlanCondition{
Type: conditionType,
Status: corev1.ConditionUnknown,
}
}
// SetCondition adds or updates a condition, using `Type` as merge key.
func (s *InstallPlanStatus) SetCondition(cond InstallPlanCondition) InstallPlanCondition {
for i, existing := range s.Conditions {
if existing.Type != cond.Type {
continue
}
if existing.Status == cond.Status {
cond.LastTransitionTime = existing.LastTransitionTime
}
s.Conditions[i] = cond
return cond
}
s.Conditions = append(s.Conditions, cond)
return cond
}
func ConditionFailed(cond InstallPlanConditionType, reason InstallPlanConditionReason, message string, now *metav1.Time) InstallPlanCondition {
return InstallPlanCondition{
Type: cond,
Status: corev1.ConditionFalse,
Reason: reason,
Message: message,
LastUpdateTime: *now,
LastTransitionTime: *now,
}
}
func ConditionMet(cond InstallPlanConditionType, now *metav1.Time) InstallPlanCondition {
return InstallPlanCondition{
Type: cond,
Status: corev1.ConditionTrue,
LastUpdateTime: *now,
LastTransitionTime: *now,
}
}
// Step represents the status of an individual step in an InstallPlan.
type Step struct {
Resolving string
Resource StepResource
Status StepStatus
}
// ManifestsMatch returns true if the CSV manifests in the StepResources of the given list of steps
// matches those in the InstallPlanStatus.
func (s *InstallPlanStatus) CSVManifestsMatch(steps []*Step) bool {
if s.Plan == nil && steps == nil {
return true
}
if s.Plan == nil || steps == nil {
return false
}
manifests := make(map[string]struct{})
for _, step := range s.Plan {
resource := step.Resource
if resource.Kind != ClusterServiceVersionKind {
continue
}
manifests[resource.Manifest] = struct{}{}
}
for _, step := range steps {
resource := step.Resource
if resource.Kind != ClusterServiceVersionKind {
continue
}
if _, ok := manifests[resource.Manifest]; !ok {
return false
}
delete(manifests, resource.Manifest)
}
if len(manifests) == 0 {
return true
}
return false
}
func (s *Step) String() string {
return fmt.Sprintf("%s: %s (%s)", s.Resolving, s.Resource, s.Status)
}
// StepResource represents the status of a resource to be tracked by an
// InstallPlan.
type StepResource struct {
CatalogSource string
CatalogSourceNamespace string
Group string
Version string
Kind string
Name string
Manifest string
}
func (r StepResource) String() string {
return fmt.Sprintf("%s[%s/%s/%s (%s/%s)]", r.Name, r.Group, r.Version, r.Kind, r.CatalogSource, r.CatalogSourceNamespace)
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// InstallPlan defines the installation of a set of operators.
type InstallPlan struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec InstallPlanSpec
Status InstallPlanStatus
}
// EnsureCatalogSource ensures that a CatalogSource is present in the Status
// block of an InstallPlan.
func (p *InstallPlan) EnsureCatalogSource(sourceName string) {
for _, srcName := range p.Status.CatalogSources {
if srcName == sourceName {
return
}
}
p.Status.CatalogSources = append(p.Status.CatalogSources, sourceName)
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// InstallPlanList is a list of InstallPlan resources.
type InstallPlanList struct {
metav1.TypeMeta
metav1.ListMeta
Items []InstallPlan
}

View File

@ -0,0 +1,79 @@
package operators
import (
"sort"
"strings"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// OperatorGroupKind is the PascalCase name of an OperatorGroup's kind.
const OperatorGroupKind = "OperatorGroup"
const (
OperatorGroupAnnotationKey = "olm.operatorGroup"
OperatorGroupNamespaceAnnotationKey = "olm.operatorNamespace"
OperatorGroupTargetsAnnotationKey = "olm.targetNamespaces"
OperatorGroupProvidedAPIsAnnotationKey = "olm.providedAPIs"
)
// OperatorGroupSpec is the spec for an OperatorGroup resource.
type OperatorGroupSpec struct {
// Selector selects the OperatorGroup's target namespaces.
// +optional
Selector *metav1.LabelSelector
// TargetNamespaces is an explicit set of namespaces to target.
// If it is set, Selector is ignored.
// +optional
TargetNamespaces []string
// ServiceAccountName is the admin specified service account which will be
// used to deploy operator(s) in this operator group.
ServiceAccountName string
// Static tells OLM not to update the OperatorGroup's providedAPIs annotation
// +optional
StaticProvidedAPIs bool
}
// OperatorGroupStatus is the status for an OperatorGroupResource.
type OperatorGroupStatus struct {
// Namespaces is the set of target namespaces for the OperatorGroup.
Namespaces []string
// ServiceAccountRef references the service account object specified.
ServiceAccountRef *corev1.ObjectReference
// LastUpdated is a timestamp of the last time the OperatorGroup's status was Updated.
LastUpdated metav1.Time
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// OperatorGroup is the unit of multitenancy for OLM managed operators.
// It constrains the installation of operators in its namespace to a specified set of target namespaces.
type OperatorGroup struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec OperatorGroupSpec
Status OperatorGroupStatus
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// OperatorGroupList is a list of OperatorGroup resources.
type OperatorGroupList struct {
metav1.TypeMeta
metav1.ListMeta
Items []OperatorGroup
}
func (o *OperatorGroup) BuildTargetNamespaces() string {
sort.Strings(o.Status.Namespaces)
return strings.Join(o.Status.Namespaces, ",")
}

View File

@ -0,0 +1,50 @@
package operators
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
// GroupName is the group name used in this package.
GroupName = "operators.coreos.com"
// GroupVersion is the group version used in this package.
GroupVersion = runtime.APIVersionInternal
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
// SchemeBuilder initializes a scheme builder
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
// AddToScheme is a global function that registers this API group & version to a scheme
AddToScheme = SchemeBuilder.AddToScheme
)
// addKnownTypes adds the list of known types to Scheme
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&CatalogSource{},
&CatalogSourceList{},
&InstallPlan{},
&InstallPlanList{},
&Subscription{},
&SubscriptionList{},
&ClusterServiceVersion{},
&ClusterServiceVersionList{},
&OperatorGroup{},
&OperatorGroupList{},
)
return nil
}

View File

@ -0,0 +1,312 @@
package operators
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
// SubscriptionKind is the PascalCase name of a Subscription's kind.
const SubscriptionKind = "Subscription"
// SubscriptionState tracks when updates are available, installing, or service is up to date
type SubscriptionState string
const (
SubscriptionStateNone = ""
SubscriptionStateFailed = "UpgradeFailed"
SubscriptionStateUpgradeAvailable = "UpgradeAvailable"
SubscriptionStateUpgradePending = "UpgradePending"
SubscriptionStateAtLatest = "AtLatestKnown"
)
const (
SubscriptionReasonInvalidCatalog ConditionReason = "InvalidCatalog"
SubscriptionReasonUpgradeSucceeded ConditionReason = "UpgradeSucceeded"
)
// SubscriptionSpec defines an Application that can be installed
type SubscriptionSpec struct {
CatalogSource string
CatalogSourceNamespace string
Package string
Channel string
StartingCSV string
InstallPlanApproval Approval
Config SubscriptionConfig
}
// SubscriptionConfig contains configuration specified for a subscription.
type SubscriptionConfig struct {
// Label selector for pods. Existing ReplicaSets whose pods are
// selected by this will be the ones affected by this deployment.
// It must match the pod template's labels.
Selector *metav1.LabelSelector `json:"selector,omitempty"`
// NodeSelector is a selector which must be true for the pod to fit on a node.
// Selector which must match a node's labels for the pod to be scheduled on that node.
// More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
// +optional
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// If specified, the pod's tolerations.
// +optional
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
// Compute Resources required by this container.
// Cannot be updated.
// More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/
// +optional
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
// List of sources to populate environment variables in the container.
// The keys defined within a source must be a C_IDENTIFIER. All invalid keys
// will be reported as an event when the container is starting. When a key exists in multiple
// sources, the value associated with the last source will take precedence.
// Values defined by an Env with a duplicate key will take precedence.
// Cannot be updated.
// +optional
EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"`
// List of environment variables to set in the container.
// Cannot be updated.
// +optional
// +patchMergeKey=name
// +patchStrategy=merge
Env []corev1.EnvVar `json:"env,omitempty"`
// List of Volumes to set in the podSpec.
// +optional
Volumes []corev1.Volume `json:"volumes,omitempty"`
// List of VolumeMounts to set in the container.
// +optional
VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`
}
// SubscriptionConditionType indicates an explicit state condition about a Subscription in "abnormal-true"
// polarity form (see https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties).
type SubscriptionConditionType string
const (
// SubscriptionCatalogSourcesUnhealthy indicates that some or all of the CatalogSources to be used in resolution are unhealthy.
SubscriptionCatalogSourcesUnhealthy SubscriptionConditionType = "CatalogSourcesUnhealthy"
// SubscriptionInstallPlanMissing indicates that a Subscription's InstallPlan is missing.
SubscriptionInstallPlanMissing SubscriptionConditionType = "InstallPlanMissing"
// SubscriptionInstallPlanPending indicates that a Subscription's InstallPlan is pending installation.
SubscriptionInstallPlanPending SubscriptionConditionType = "InstallPlanPending"
// SubscriptionInstallPlanFailed indicates that the installation of a Subscription's InstallPlan has failed.
SubscriptionInstallPlanFailed SubscriptionConditionType = "InstallPlanFailed"
)
const (
// NoCatalogSourcesFound is a reason string for Subscriptions with unhealthy CatalogSources due to none being available.
NoCatalogSourcesFound = "NoCatalogSourcesFound"
// AllCatalogSourcesHealthy is a reason string for Subscriptions that transitioned due to all CatalogSources being healthy.
AllCatalogSourcesHealthy = "AllCatalogSourcesHealthy"
// CatalogSourcesAdded is a reason string for Subscriptions that transitioned due to CatalogSources being added.
CatalogSourcesAdded = "CatalogSourcesAdded"
// CatalogSourcesUpdated is a reason string for Subscriptions that transitioned due to CatalogSource being updated.
CatalogSourcesUpdated = "CatalogSourcesUpdated"
// CatalogSourcesDeleted is a reason string for Subscriptions that transitioned due to CatalogSources being removed.
CatalogSourcesDeleted = "CatalogSourcesDeleted"
// UnhealthyCatalogSourceFound is a reason string for Subscriptions that transitioned because an unhealthy CatalogSource was found.
UnhealthyCatalogSourceFound = "UnhealthyCatalogSourceFound"
// ReferencedInstallPlanNotFound is a reason string for Subscriptions that transitioned due to a referenced InstallPlan not being found.
ReferencedInstallPlanNotFound = "ReferencedInstallPlanNotFound"
// InstallPlanNotYetReconciled is a reason string for Subscriptions that transitioned due to a referenced InstallPlan not being reconciled yet.
InstallPlanNotYetReconciled = "InstallPlanNotYetReconciled"
// InstallPlanFailed is a reason string for Subscriptions that transitioned due to a referenced InstallPlan failing without setting an explicit failure condition.
InstallPlanFailed = "InstallPlanFailed"
)
// SubscriptionCondition represents the latest available observations of a Subscription's state.
type SubscriptionCondition struct {
// Type is the type of Subscription condition.
Type SubscriptionConditionType
// Status is the status of the condition, one of True, False, Unknown.
Status corev1.ConditionStatus
// Reason is a one-word CamelCase reason for the condition's last transition.
// +optional
Reason string
// Message is a human-readable message indicating details about last transition.
// +optional
Message string
// LastHeartbeatTime is the last time we got an update on a given condition
// +optional
LastHeartbeatTime *metav1.Time
// LastTransitionTime is the last time the condition transit from one status to another
// +optional
LastTransitionTime *metav1.Time
}
// Equals returns true if a SubscriptionCondition equals the one given, false otherwise.
// Equality is determined by the equality of the type, status, reason, and message fields ONLY.
func (s SubscriptionCondition) Equals(condition SubscriptionCondition) bool {
return s.Type == condition.Type && s.Status == condition.Status && s.Reason == condition.Reason && s.Message == condition.Message
}
type SubscriptionStatus struct {
// CurrentCSV is the CSV the Subscription is progressing to.
// +optional
CurrentCSV string
// InstalledCSV is the CSV currently installed by the Subscription.
// +optional
InstalledCSV string
// Install is a reference to the latest InstallPlan generated for the Subscription.
// DEPRECATED: InstallPlanRef
// +optional
Install *InstallPlanReference
// State represents the current state of the Subscription
// +optional
State SubscriptionState
// Reason is the reason the Subscription was transitioned to its current state.
// +optional
Reason ConditionReason
// InstallPlanRef is a reference to the latest InstallPlan that contains the Subscription's current CSV.
// +optional
InstallPlanRef *corev1.ObjectReference
// CatalogHealth contains the Subscription's view of its relevant CatalogSources' status.
// It is used to determine SubscriptionStatusConditions related to CatalogSources.
// +optional
CatalogHealth []SubscriptionCatalogHealth
// Conditions is a list of the latest available observations about a Subscription's current state.
// +optional
Conditions []SubscriptionCondition `hash:"set"`
// LastUpdated represents the last time that the Subscription status was updated.
LastUpdated metav1.Time
}
// GetCondition returns the SubscriptionCondition of the given type if it exists in the SubscriptionStatus' Conditions.
// Returns a condition of the given type with a ConditionStatus of "Unknown" if not found.
func (s SubscriptionStatus) GetCondition(conditionType SubscriptionConditionType) SubscriptionCondition {
for _, cond := range s.Conditions {
if cond.Type == conditionType {
return cond
}
}
return SubscriptionCondition{
Type: conditionType,
Status: corev1.ConditionUnknown,
}
}
// SetCondition sets the given SubscriptionCondition in the SubscriptionStatus' Conditions.
func (s *SubscriptionStatus) SetCondition(condition SubscriptionCondition) {
for i, cond := range s.Conditions {
if cond.Type == condition.Type {
s.Conditions[i] = condition
return
}
}
s.Conditions = append(s.Conditions, condition)
}
// RemoveConditions removes any conditions of the given types from the SubscriptionStatus' Conditions.
func (s *SubscriptionStatus) RemoveConditions(remove ...SubscriptionConditionType) {
exclusions := map[SubscriptionConditionType]struct{}{}
for _, r := range remove {
exclusions[r] = struct{}{}
}
var filtered []SubscriptionCondition
for _, cond := range s.Conditions {
if _, ok := exclusions[cond.Type]; ok {
// Skip excluded condition types
continue
}
filtered = append(filtered, cond)
}
s.Conditions = filtered
}
type InstallPlanReference struct {
APIVersion string
Kind string
Name string
UID types.UID
}
// SubscriptionCatalogHealth describes the health of a CatalogSource the Subscription knows about.
type SubscriptionCatalogHealth struct {
// CatalogSourceRef is a reference to a CatalogSource.
CatalogSourceRef *corev1.ObjectReference
// LastUpdated represents the last time that the CatalogSourceHealth changed
LastUpdated *metav1.Time
// Healthy is true if the CatalogSource is healthy; false otherwise.
Healthy bool
}
// Equals returns true if a SubscriptionCatalogHealth equals the one given, false otherwise.
// Equality is based SOLEY on health and UID.
func (s SubscriptionCatalogHealth) Equals(health SubscriptionCatalogHealth) bool {
return s.Healthy == health.Healthy && s.CatalogSourceRef.UID == health.CatalogSourceRef.UID
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// Subscription keeps operators up to date by tracking changes to Catalogs.
type Subscription struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec *SubscriptionSpec
Status SubscriptionStatus
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// SubscriptionList is a list of Subscription resources.
type SubscriptionList struct {
metav1.TypeMeta
metav1.ListMeta
Items []Subscription
}
// GetInstallPlanApproval gets the configured install plan approval or the default
func (s *Subscription) GetInstallPlanApproval() Approval {
if s.Spec.InstallPlanApproval == ApprovalManual {
return ApprovalManual
}
return ApprovalAutomatic
}
// NewInstallPlanReference returns an InstallPlanReference for the given ObjectReference.
func NewInstallPlanReference(ref *corev1.ObjectReference) *InstallPlanReference {
return &InstallPlanReference{
APIVersion: ref.APIVersion,
Kind: ref.Kind,
Name: ref.Name,
UID: ref.UID,
}
}

View File

@ -0,0 +1,147 @@
package v1alpha1
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
const (
CatalogSourceCRDAPIVersion = GroupName + "/" + GroupVersion
CatalogSourceKind = "CatalogSource"
)
// SourceType indicates the type of backing store for a CatalogSource
type SourceType string
const (
// SourceTypeInternal (deprecated) specifies a CatalogSource of type SourceTypeConfigmap
SourceTypeInternal SourceType = "internal"
// SourceTypeConfigmap specifies a CatalogSource that generates a configmap-server registry
SourceTypeConfigmap SourceType = "configmap"
// SourceTypeGrpc specifies a CatalogSource that can use an operator registry image to generate a
// registry-server or connect to a pre-existing registry at an address.
SourceTypeGrpc SourceType = "grpc"
)
const (
CatalogSourceConfigMapError ConditionReason = "ConfigMapError"
CatalogSourceRegistryServerError ConditionReason = "RegistryServerError"
)
type CatalogSourceSpec struct {
// SourceType is the type of source
SourceType SourceType `json:"sourceType"`
// ConfigMap is the name of the ConfigMap to be used to back a configmap-server registry.
// Only used when SourceType = SourceTypeConfigmap or SourceTypeInternal.
// +Optional
ConfigMap string `json:"configMap,omitempty"`
// Address is a host that OLM can use to connect to a pre-existing registry.
// Format: <registry-host or ip>:<port>
// Only used when SourceType = SourceTypeGrpc.
// Ignored when the Image field is set.
// +Optional
Address string `json:"address,omitempty"`
// Image is an operator-registry container image to instantiate a registry-server with.
// Only used when SourceType = SourceTypeGrpc.
// If present, the address field is ignored.
// +Optional
Image string `json:"image,omitempty"`
// Secrets represent set of secrets that can be used to access the contents of the catalog.
// It is best to keep this list small, since each will need to be tried for every catalog entry.
// +Optional
Secrets []string `json:"secrets,omitempty"`
// Metadata
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
Publisher string `json:"publisher,omitempty"`
Icon Icon `json:"icon,omitempty"`
}
type RegistryServiceStatus struct {
Protocol string `json:"protocol,omitempty"`
ServiceName string `json:"serviceName,omitempty"`
ServiceNamespace string `json:"serviceNamespace,omitempty"`
Port string `json:"port,omitempty"`
CreatedAt metav1.Time `json:"createdAt,omitempty"`
}
func (s *RegistryServiceStatus) Address() string {
return fmt.Sprintf("%s.%s.svc:%s", s.ServiceName, s.ServiceNamespace, s.Port)
}
type GRPCConnectionState struct {
Address string `json:"address,omitempty"`
LastObservedState string `json:"lastObservedState"`
LastConnectTime metav1.Time `json:"lastConnect,omitempty"`
}
type CatalogSourceStatus struct {
// A human readable message indicating details about why the ClusterServiceVersion is in this condition.
// +optional
Message string `json:"message,omitempty"`
// Reason is the reason the Subscription was transitioned to its current state.
// +optional
Reason ConditionReason `json:"reason,omitempty"`
ConfigMapResource *ConfigMapResourceReference `json:"configMapReference,omitempty"`
RegistryServiceStatus *RegistryServiceStatus `json:"registryService,omitempty"`
GRPCConnectionState *GRPCConnectionState `json:"connectionState,omitempty"`
}
type ConfigMapResourceReference struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
UID types.UID `json:"uid,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty"`
LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`
}
func (r *ConfigMapResourceReference) IsAMatch(object *metav1.ObjectMeta) bool {
return r.UID == object.GetUID() && r.ResourceVersion == object.GetResourceVersion()
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// CatalogSource is a repository of CSVs, CRDs, and operator packages.
type CatalogSource struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec CatalogSourceSpec `json:"spec"`
Status CatalogSourceStatus `json:"status"`
}
func (c *CatalogSource) Address() string {
if c.Spec.Address != "" {
return c.Spec.Address
}
return c.Status.RegistryServiceStatus.Address()
}
func (c *CatalogSource) SetError(reason ConditionReason, err error) {
c.Status.Reason = reason
c.Status.Message = ""
if err != nil {
c.Status.Message = err.Error()
}
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// CatalogSourceList is a repository of CSVs, CRDs, and operator packages.
type CatalogSourceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []CatalogSource `json:"items"`
}

View File

@ -0,0 +1,208 @@
package v1alpha1
import (
"fmt"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/record"
)
const (
CopiedLabelKey = "olm.copiedFrom"
// ConditionsLengthLimit is the maximum length of Status.Conditions of a
// given ClusterServiceVersion object. The oldest condition(s) are removed
// from the list as it grows over time to keep it at limit.
ConditionsLengthLimit = 20
)
// obsoleteReasons are the set of reasons that mean a CSV should no longer be processed as active
var obsoleteReasons = map[ConditionReason]struct{}{
CSVReasonReplaced: {},
CSVReasonBeingReplaced: {},
}
// uncopiableReasons are the set of reasons that should prevent a CSV from being copied to target namespaces
var uncopiableReasons = map[ConditionReason]struct{}{
CSVReasonCopied: {},
CSVReasonInvalidInstallModes: {},
CSVReasonNoTargetNamespaces: {},
CSVReasonUnsupportedOperatorGroup: {},
CSVReasonNoOperatorGroup: {},
CSVReasonTooManyOperatorGroups: {},
CSVReasonInterOperatorGroupOwnerConflict: {},
CSVReasonCannotModifyStaticOperatorGroupProvidedAPIs: {},
}
// safeToAnnotateOperatorGroupReasons are the set of reasons that it's safe to attempt to update the operatorgroup
// annotations
var safeToAnnotateOperatorGroupReasons = map[ConditionReason]struct{}{
CSVReasonOwnerConflict: {},
CSVReasonInstallSuccessful: {},
CSVReasonInvalidInstallModes: {},
CSVReasonNoTargetNamespaces: {},
CSVReasonUnsupportedOperatorGroup: {},
CSVReasonNoOperatorGroup: {},
CSVReasonTooManyOperatorGroups: {},
CSVReasonInterOperatorGroupOwnerConflict: {},
CSVReasonCannotModifyStaticOperatorGroupProvidedAPIs: {},
}
// SetPhaseWithEventIfChanged emits a Kubernetes event with details of a phase change and sets the current phase if phase, reason, or message would changed
func (c *ClusterServiceVersion) SetPhaseWithEventIfChanged(phase ClusterServiceVersionPhase, reason ConditionReason, message string, now metav1.Time, recorder record.EventRecorder) {
if c.Status.Phase == phase && c.Status.Reason == reason && c.Status.Message == message {
return
}
c.SetPhaseWithEvent(phase, reason, message, now, recorder)
}
// SetPhaseWithEvent generates a Kubernetes event with details about the phase change and sets the current phase
func (c *ClusterServiceVersion) SetPhaseWithEvent(phase ClusterServiceVersionPhase, reason ConditionReason, message string, now metav1.Time, recorder record.EventRecorder) {
var eventtype string
if phase == CSVPhaseFailed {
eventtype = v1.EventTypeWarning
} else {
eventtype = v1.EventTypeNormal
}
go recorder.Event(c, eventtype, string(reason), message)
c.SetPhase(phase, reason, message, now)
}
// SetPhase sets the current phase and adds a condition if necessary
func (c *ClusterServiceVersion) SetPhase(phase ClusterServiceVersionPhase, reason ConditionReason, message string, now metav1.Time) {
newCondition := func() ClusterServiceVersionCondition {
return ClusterServiceVersionCondition{
Phase: c.Status.Phase,
LastTransitionTime: c.Status.LastTransitionTime,
LastUpdateTime: c.Status.LastUpdateTime,
Message: message,
Reason: reason,
}
}
defer c.TrimConditionsIfLimitExceeded()
c.Status.LastUpdateTime = now
if c.Status.Phase != phase {
c.Status.Phase = phase
c.Status.LastTransitionTime = now
}
c.Status.Message = message
c.Status.Reason = reason
if len(c.Status.Conditions) == 0 {
c.Status.Conditions = append(c.Status.Conditions, newCondition())
return
}
previousCondition := c.Status.Conditions[len(c.Status.Conditions)-1]
if previousCondition.Phase != c.Status.Phase || previousCondition.Reason != c.Status.Reason {
c.Status.Conditions = append(c.Status.Conditions, newCondition())
}
}
// SetRequirementStatus adds the status of all requirements to the CSV status
func (c *ClusterServiceVersion) SetRequirementStatus(statuses []RequirementStatus) {
c.Status.RequirementStatus = statuses
}
// IsObsolete returns if this CSV is being replaced or is marked for deletion
func (c *ClusterServiceVersion) IsObsolete() bool {
for _, condition := range c.Status.Conditions {
_, ok := obsoleteReasons[condition.Reason]
if ok {
return true
}
}
return false
}
// IsCopied returns true if the CSV has been copied and false otherwise.
func (c *ClusterServiceVersion) IsCopied() bool {
operatorNamespace, ok := c.GetAnnotations()[OperatorGroupNamespaceAnnotationKey]
if c.Status.Reason == CSVReasonCopied || ok && c.GetNamespace() != operatorNamespace {
return true
}
if labels := c.GetLabels(); labels != nil {
if _, ok := labels[CopiedLabelKey]; ok {
return true
}
}
return false
}
func (c *ClusterServiceVersion) IsUncopiable() bool {
if c.Status.Phase == CSVPhaseNone {
return true
}
_, ok := uncopiableReasons[c.Status.Reason]
return ok
}
func (c *ClusterServiceVersion) IsSafeToUpdateOperatorGroupAnnotations() bool {
_, ok := safeToAnnotateOperatorGroupReasons[c.Status.Reason]
return ok
}
// NewInstallModeSet returns an InstallModeSet instantiated from the given list of InstallModes.
// If the given list is not a set, an error is returned.
func NewInstallModeSet(modes []InstallMode) (InstallModeSet, error) {
set := InstallModeSet{}
for _, mode := range modes {
if _, exists := set[mode.Type]; exists {
return nil, fmt.Errorf("InstallMode list contains duplicates, cannot make set: %v", modes)
}
set[mode.Type] = mode.Supported
}
return set, nil
}
// Supports returns an error if the InstallModeSet does not support configuration for
// the given operatorNamespace and list of target namespaces.
func (set InstallModeSet) Supports(operatorNamespace string, namespaces []string) error {
numNamespaces := len(namespaces)
switch {
case numNamespaces == 0:
return fmt.Errorf("operatorgroup has invalid selected namespaces, cannot configure to watch zero namespaces")
case numNamespaces == 1:
switch namespaces[0] {
case operatorNamespace:
if !set[InstallModeTypeOwnNamespace] {
return fmt.Errorf("%s InstallModeType not supported, cannot configure to watch own namespace", InstallModeTypeOwnNamespace)
}
case v1.NamespaceAll:
if !set[InstallModeTypeAllNamespaces] {
return fmt.Errorf("%s InstallModeType not supported, cannot configure to watch all namespaces", InstallModeTypeAllNamespaces)
}
default:
if !set[InstallModeTypeSingleNamespace] {
return fmt.Errorf("%s InstallModeType not supported, cannot configure to watch one namespace", InstallModeTypeSingleNamespace)
}
}
case numNamespaces > 1 && !set[InstallModeTypeMultiNamespace]:
return fmt.Errorf("%s InstallModeType not supported, cannot configure to watch %d namespaces", InstallModeTypeMultiNamespace, numNamespaces)
case numNamespaces > 1:
for _, namespace := range namespaces {
if namespace == operatorNamespace && !set[InstallModeTypeOwnNamespace] {
return fmt.Errorf("%s InstallModeType not supported, cannot configure to watch own namespace", InstallModeTypeOwnNamespace)
}
if namespace == v1.NamespaceAll {
return fmt.Errorf("operatorgroup has invalid selected namespaces, NamespaceAll found when |selected namespaces| > 1")
}
}
}
return nil
}
func (c *ClusterServiceVersion) TrimConditionsIfLimitExceeded() {
if len(c.Status.Conditions) <= ConditionsLengthLimit {
return
}
firstIndex := len(c.Status.Conditions) - ConditionsLengthLimit
c.Status.Conditions = c.Status.Conditions[firstIndex:len(c.Status.Conditions)]
}

View File

@ -0,0 +1,502 @@
package v1alpha1
import (
"encoding/json"
"fmt"
"sort"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version"
)
const (
ClusterServiceVersionAPIVersion = GroupName + "/" + GroupVersion
ClusterServiceVersionKind = "ClusterServiceVersion"
OperatorGroupNamespaceAnnotationKey = "olm.operatorNamespace"
)
// InstallModeType is a supported type of install mode for CSV installation
type InstallModeType string
const (
// InstallModeTypeOwnNamespace indicates that the operator can be a member of an `OperatorGroup` that selects its own namespace.
InstallModeTypeOwnNamespace InstallModeType = "OwnNamespace"
// InstallModeTypeSingleNamespace indicates that the operator can be a member of an `OperatorGroup` that selects one namespace.
InstallModeTypeSingleNamespace InstallModeType = "SingleNamespace"
// InstallModeTypeMultiNamespace indicates that the operator can be a member of an `OperatorGroup` that selects more than one namespace.
InstallModeTypeMultiNamespace InstallModeType = "MultiNamespace"
// InstallModeTypeAllNamespaces indicates that the operator can be a member of an `OperatorGroup` that selects all namespaces (target namespace set is the empty string "").
InstallModeTypeAllNamespaces InstallModeType = "AllNamespaces"
)
// InstallMode associates an InstallModeType with a flag representing if the CSV supports it
// +k8s:openapi-gen=true
type InstallMode struct {
Type InstallModeType `json:"type"`
Supported bool `json:"supported"`
}
// InstallModeSet is a mapping of unique InstallModeTypes to whether they are supported.
type InstallModeSet map[InstallModeType]bool
// NamedInstallStrategy represents the block of an ClusterServiceVersion resource
// where the install strategy is specified.
type NamedInstallStrategy struct {
StrategyName string `json:"strategy"`
StrategySpecRaw json.RawMessage `json:"spec,omitempty"`
}
// StatusDescriptor describes a field in a status block of a CRD so that OLM can consume it
// +k8s:openapi-gen=true
type StatusDescriptor struct {
Path string `json:"path"`
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
XDescriptors []string `json:"x-descriptors,omitempty"`
Value *json.RawMessage `json:"value,omitempty"`
}
// SpecDescriptor describes a field in a spec block of a CRD so that OLM can consume it
// +k8s:openapi-gen=true
type SpecDescriptor struct {
Path string `json:"path"`
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
XDescriptors []string `json:"x-descriptors,omitempty"`
Value *json.RawMessage `json:"value,omitempty"`
}
// ActionDescriptor describes a declarative action that can be performed on a custom resource instance
// +k8s:openapi-gen=true
type ActionDescriptor struct {
Path string `json:"path"`
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
XDescriptors []string `json:"x-descriptors,omitempty"`
Value *json.RawMessage `json:"value,omitempty"`
}
// CRDDescription provides details to OLM about the CRDs
// +k8s:openapi-gen=true
type CRDDescription struct {
Name string `json:"name"`
Version string `json:"version"`
Kind string `json:"kind"`
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
Resources []APIResourceReference `json:"resources,omitempty"`
StatusDescriptors []StatusDescriptor `json:"statusDescriptors,omitempty"`
SpecDescriptors []SpecDescriptor `json:"specDescriptors,omitempty"`
ActionDescriptor []ActionDescriptor `json:"actionDescriptors,omitempty"`
}
// APIServiceDescription provides details to OLM about apis provided via aggregation
// +k8s:openapi-gen=true
type APIServiceDescription struct {
Name string `json:"name"`
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
DeploymentName string `json:"deploymentName,omitempty"`
ContainerPort int32 `json:"containerPort,omitempty"`
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
Resources []APIResourceReference `json:"resources,omitempty"`
StatusDescriptors []StatusDescriptor `json:"statusDescriptors,omitempty"`
SpecDescriptors []SpecDescriptor `json:"specDescriptors,omitempty"`
ActionDescriptor []ActionDescriptor `json:"actionDescriptors,omitempty"`
}
// APIResourceReference is a Kubernetes resource type used by a custom resource
// +k8s:openapi-gen=true
type APIResourceReference struct {
Name string `json:"name"`
Kind string `json:"kind"`
Version string `json:"version"`
}
// GetName returns the name of an APIService as derived from its group and version.
func (d APIServiceDescription) GetName() string {
return fmt.Sprintf("%s.%s", d.Version, d.Group)
}
// CustomResourceDefinitions declares all of the CRDs managed or required by
// an operator being ran by ClusterServiceVersion.
//
// If the CRD is present in the Owned list, it is implicitly required.
// +k8s:openapi-gen=true
type CustomResourceDefinitions struct {
Owned []CRDDescription `json:"owned,omitempty"`
Required []CRDDescription `json:"required,omitempty"`
}
// APIServiceDefinitions declares all of the extension apis managed or required by
// an operator being ran by ClusterServiceVersion.
// +k8s:openapi-gen=true
type APIServiceDefinitions struct {
Owned []APIServiceDescription `json:"owned,omitempty"`
Required []APIServiceDescription `json:"required,omitempty"`
}
// ClusterServiceVersionSpec declarations tell OLM how to install an operator
// that can manage apps for a given version.
type ClusterServiceVersionSpec struct {
InstallStrategy NamedInstallStrategy `json:"install"`
Version version.OperatorVersion `json:"version,omitempty"`
Maturity string `json:"maturity,omitempty"`
CustomResourceDefinitions CustomResourceDefinitions `json:"customresourcedefinitions,omitempty"`
APIServiceDefinitions APIServiceDefinitions `json:"apiservicedefinitions,omitempty"`
NativeAPIs []metav1.GroupVersionKind `json:"nativeAPIs,omitempty"`
MinKubeVersion string `json:"minKubeVersion,omitempty"`
DisplayName string `json:"displayName"`
Description string `json:"description,omitempty"`
Keywords []string `json:"keywords,omitempty"`
Maintainers []Maintainer `json:"maintainers,omitempty"`
Provider AppLink `json:"provider,omitempty"`
Links []AppLink `json:"links,omitempty"`
Icon []Icon `json:"icon,omitempty"`
// InstallModes specify supported installation types
// +optional
InstallModes []InstallMode `json:"installModes,omitempty"`
// The name of a CSV this one replaces. Should match the `metadata.Name` field of the old CSV.
// +optional
Replaces string `json:"replaces,omitempty"`
// Map of string keys and values that can be used to organize and categorize
// (scope and select) objects.
// +optional
Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"`
// Annotations is an unstructured key value map stored with a resource that may be
// set by external tools to store and retrieve arbitrary metadata.
// +optional
Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`
// Label selector for related resources.
// +optional
Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,2,opt,name=selector"`
}
type Maintainer struct {
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
}
type AppLink struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
}
type Icon struct {
Data string `json:"base64data"`
MediaType string `json:"mediatype"`
}
// ClusterServiceVersionPhase is a label for the condition of a ClusterServiceVersion at the current time.
type ClusterServiceVersionPhase string
// These are the valid phases of ClusterServiceVersion
const (
CSVPhaseNone = ""
// CSVPhasePending means the csv has been accepted by the system, but the install strategy has not been attempted.
// This is likely because there are unmet requirements.
CSVPhasePending ClusterServiceVersionPhase = "Pending"
// CSVPhaseInstallReady means that the requirements are met but the install strategy has not been run.
CSVPhaseInstallReady ClusterServiceVersionPhase = "InstallReady"
// CSVPhaseInstalling means that the install strategy has been initiated but not completed.
CSVPhaseInstalling ClusterServiceVersionPhase = "Installing"
// CSVPhaseSucceeded means that the resources in the CSV were created successfully.
CSVPhaseSucceeded ClusterServiceVersionPhase = "Succeeded"
// CSVPhaseFailed means that the install strategy could not be successfully completed.
CSVPhaseFailed ClusterServiceVersionPhase = "Failed"
// CSVPhaseUnknown means that for some reason the state of the csv could not be obtained.
CSVPhaseUnknown ClusterServiceVersionPhase = "Unknown"
// CSVPhaseReplacing means that a newer CSV has been created and the csv's resources will be transitioned to a new owner.
CSVPhaseReplacing ClusterServiceVersionPhase = "Replacing"
// CSVPhaseDeleting means that a CSV has been replaced by a new one and will be checked for safety before being deleted
CSVPhaseDeleting ClusterServiceVersionPhase = "Deleting"
// CSVPhaseAny matches all other phases in CSV queries
CSVPhaseAny ClusterServiceVersionPhase = ""
)
// ConditionReason is a camelcased reason for the state transition
type ConditionReason string
const (
CSVReasonRequirementsUnknown ConditionReason = "RequirementsUnknown"
CSVReasonRequirementsNotMet ConditionReason = "RequirementsNotMet"
CSVReasonRequirementsMet ConditionReason = "AllRequirementsMet"
CSVReasonOwnerConflict ConditionReason = "OwnerConflict"
CSVReasonComponentFailed ConditionReason = "InstallComponentFailed"
CSVReasonComponentFailedNoRetry ConditionReason = "InstallComponentFailedNoRetry"
CSVReasonInvalidStrategy ConditionReason = "InvalidInstallStrategy"
CSVReasonWaiting ConditionReason = "InstallWaiting"
CSVReasonInstallSuccessful ConditionReason = "InstallSucceeded"
CSVReasonInstallCheckFailed ConditionReason = "InstallCheckFailed"
CSVReasonComponentUnhealthy ConditionReason = "ComponentUnhealthy"
CSVReasonBeingReplaced ConditionReason = "BeingReplaced"
CSVReasonReplaced ConditionReason = "Replaced"
CSVReasonNeedsReinstall ConditionReason = "NeedsReinstall"
CSVReasonNeedsCertRotation ConditionReason = "NeedsCertRotation"
CSVReasonAPIServiceResourceIssue ConditionReason = "APIServiceResourceIssue"
CSVReasonAPIServiceResourcesNeedReinstall ConditionReason = "APIServiceResourcesNeedReinstall"
CSVReasonAPIServiceInstallFailed ConditionReason = "APIServiceInstallFailed"
CSVReasonCopied ConditionReason = "Copied"
CSVReasonInvalidInstallModes ConditionReason = "InvalidInstallModes"
CSVReasonNoTargetNamespaces ConditionReason = "NoTargetNamespaces"
CSVReasonUnsupportedOperatorGroup ConditionReason = "UnsupportedOperatorGroup"
CSVReasonNoOperatorGroup ConditionReason = "NoOperatorGroup"
CSVReasonTooManyOperatorGroups ConditionReason = "TooManyOperatorGroups"
CSVReasonInterOperatorGroupOwnerConflict ConditionReason = "InterOperatorGroupOwnerConflict"
CSVReasonCannotModifyStaticOperatorGroupProvidedAPIs ConditionReason = "CannotModifyStaticOperatorGroupProvidedAPIs"
CSVReasonDetectedClusterChange ConditionReason = "DetectedClusterChange"
)
// Conditions appear in the status as a record of state transitions on the ClusterServiceVersion
type ClusterServiceVersionCondition struct {
// Condition of the ClusterServiceVersion
Phase ClusterServiceVersionPhase `json:"phase,omitempty"`
// A human readable message indicating details about why the ClusterServiceVersion is in this condition.
// +optional
Message string `json:"message,omitempty"`
// A brief CamelCase message indicating details about why the ClusterServiceVersion is in this state.
// e.g. 'RequirementsNotMet'
// +optional
Reason ConditionReason `json:"reason,omitempty"`
// Last time we updated the status
// +optional
LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`
// Last time the status transitioned from one status to another.
// +optional
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
}
// OwnsCRD determines whether the current CSV owns a particular CRD.
func (csv ClusterServiceVersion) OwnsCRD(name string) bool {
for _, desc := range csv.Spec.CustomResourceDefinitions.Owned {
if desc.Name == name {
return true
}
}
return false
}
// OwnsAPIService determines whether the current CSV owns a particular APIService.
func (csv ClusterServiceVersion) OwnsAPIService(name string) bool {
for _, desc := range csv.Spec.APIServiceDefinitions.Owned {
apiServiceName := fmt.Sprintf("%s.%s", desc.Version, desc.Group)
if apiServiceName == name {
return true
}
}
return false
}
// StatusReason is a camelcased reason for the status of a RequirementStatus or DependentStatus
type StatusReason string
const (
RequirementStatusReasonPresent StatusReason = "Present"
RequirementStatusReasonNotPresent StatusReason = "NotPresent"
RequirementStatusReasonPresentNotSatisfied StatusReason = "PresentNotSatisfied"
// The CRD is present but the Established condition is False (not available)
RequirementStatusReasonNotAvailable StatusReason = "PresentNotAvailable"
DependentStatusReasonSatisfied StatusReason = "Satisfied"
DependentStatusReasonNotSatisfied StatusReason = "NotSatisfied"
)
// DependentStatus is the status for a dependent requirement (to prevent infinite nesting)
type DependentStatus struct {
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
Status StatusReason `json:"status"`
UUID string `json:"uuid,omitempty"`
Message string `json:"message,omitempty"`
}
type RequirementStatus struct {
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
Name string `json:"name"`
Status StatusReason `json:"status"`
Message string `json:"message"`
UUID string `json:"uuid,omitempty"`
Dependents []DependentStatus `json:"dependents,omitempty"`
}
// ClusterServiceVersionStatus represents information about the status of a pod. Status may trail the actual
// state of a system.
type ClusterServiceVersionStatus struct {
// Current condition of the ClusterServiceVersion
Phase ClusterServiceVersionPhase `json:"phase,omitempty"`
// A human readable message indicating details about why the ClusterServiceVersion is in this condition.
// +optional
Message string `json:"message,omitempty"`
// A brief CamelCase message indicating details about why the ClusterServiceVersion is in this state.
// e.g. 'RequirementsNotMet'
// +optional
Reason ConditionReason `json:"reason,omitempty"`
// Last time we updated the status
// +optional
LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`
// Last time the status transitioned from one status to another.
// +optional
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
// List of conditions, a history of state transitions
Conditions []ClusterServiceVersionCondition `json:"conditions,omitempty"`
// The status of each requirement for this CSV
RequirementStatus []RequirementStatus `json:"requirementStatus,omitempty"`
// Last time the owned APIService certs were updated
// +optional
CertsLastUpdated metav1.Time `json:"certsLastUpdated,omitempty"`
// Time the owned APIService certs will rotate next
// +optional
CertsRotateAt metav1.Time `json:"certsRotateAt,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// ClusterServiceVersion is a Custom Resource of type `ClusterServiceVersionSpec`.
type ClusterServiceVersion struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec ClusterServiceVersionSpec `json:"spec"`
Status ClusterServiceVersionStatus `json:"status"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ClusterServiceVersionList represents a list of ClusterServiceVersions.
type ClusterServiceVersionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []ClusterServiceVersion `json:"items"`
}
// GetAllCRDDescriptions returns a deduplicated set of CRDDescriptions that is
// the union of the owned and required CRDDescriptions.
//
// Descriptions with the same name prefer the value in Owned.
// Descriptions are returned in alphabetical order.
func (csv ClusterServiceVersion) GetAllCRDDescriptions() []CRDDescription {
set := make(map[string]CRDDescription)
for _, required := range csv.Spec.CustomResourceDefinitions.Required {
set[required.Name] = required
}
for _, owned := range csv.Spec.CustomResourceDefinitions.Owned {
set[owned.Name] = owned
}
keys := make([]string, 0)
for key := range set {
keys = append(keys, key)
}
sort.StringSlice(keys).Sort()
descs := make([]CRDDescription, 0)
for _, key := range keys {
descs = append(descs, set[key])
}
return descs
}
// GetAllAPIServiceDescriptions returns a deduplicated set of APIServiceDescriptions that is
// the union of the owned and required APIServiceDescriptions.
//
// Descriptions with the same name prefer the value in Owned.
// Descriptions are returned in alphabetical order.
func (csv ClusterServiceVersion) GetAllAPIServiceDescriptions() []APIServiceDescription {
set := make(map[string]APIServiceDescription)
for _, required := range csv.Spec.APIServiceDefinitions.Required {
name := fmt.Sprintf("%s.%s", required.Version, required.Group)
set[name] = required
}
for _, owned := range csv.Spec.APIServiceDefinitions.Owned {
name := fmt.Sprintf("%s.%s", owned.Version, owned.Group)
set[name] = owned
}
keys := make([]string, 0)
for key := range set {
keys = append(keys, key)
}
sort.StringSlice(keys).Sort()
descs := make([]APIServiceDescription, 0)
for _, key := range keys {
descs = append(descs, set[key])
}
return descs
}
// GetRequiredAPIServiceDescriptions returns a deduplicated set of required APIServiceDescriptions
// with the intersection of required and owned removed
// Equivalent to the set subtraction required - owned
//
// Descriptions are returned in alphabetical order.
func (csv ClusterServiceVersion) GetRequiredAPIServiceDescriptions() []APIServiceDescription {
set := make(map[string]APIServiceDescription)
for _, required := range csv.Spec.APIServiceDefinitions.Required {
name := fmt.Sprintf("%s.%s", required.Version, required.Group)
set[name] = required
}
// Remove any shared owned from the set
for _, owned := range csv.Spec.APIServiceDefinitions.Owned {
name := fmt.Sprintf("%s.%s", owned.Version, owned.Group)
if _, ok := set[name]; ok {
delete(set, name)
}
}
keys := make([]string, 0)
for key := range set {
keys = append(keys, key)
}
sort.StringSlice(keys).Sort()
descs := make([]APIServiceDescription, 0)
for _, key := range keys {
descs = append(descs, set[key])
}
return descs
}
// GetOwnedAPIServiceDescriptions returns a deduplicated set of owned APIServiceDescriptions
//
// Descriptions are returned in alphabetical order.
func (csv ClusterServiceVersion) GetOwnedAPIServiceDescriptions() []APIServiceDescription {
set := make(map[string]APIServiceDescription)
for _, owned := range csv.Spec.APIServiceDefinitions.Owned {
name := owned.GetName()
set[name] = owned
}
keys := make([]string, 0)
for key := range set {
keys = append(keys, key)
}
sort.StringSlice(keys).Sort()
descs := make([]APIServiceDescription, 0)
for _, key := range keys {
descs = append(descs, set[key])
}
return descs
}

View File

@ -0,0 +1,6 @@
// +k8s:deepcopy-gen=package
// +k8s:conversion-gen=github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators
// +groupName=operators.coreos.com
// Package v1alpha1 contains resources types for version v1alpha1 of the operators.coreos.com API group.
package v1alpha1

View File

@ -0,0 +1,254 @@
package v1alpha1
import (
"errors"
"fmt"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
InstallPlanKind = "InstallPlan"
InstallPlanAPIVersion = GroupName + "/" + GroupVersion
)
// Approval is the user approval policy for an InstallPlan.
type Approval string
const (
ApprovalAutomatic Approval = "Automatic"
ApprovalManual Approval = "Manual"
)
// InstallPlanSpec defines a set of Application resources to be installed
type InstallPlanSpec struct {
CatalogSource string `json:"source,omitempty"`
CatalogSourceNamespace string `json:"sourceNamespace,omitempty"`
ClusterServiceVersionNames []string `json:"clusterServiceVersionNames"`
Approval Approval `json:"approval"`
Approved bool `json:"approved"`
}
// InstallPlanPhase is the current status of a InstallPlan as a whole.
type InstallPlanPhase string
const (
InstallPlanPhaseNone InstallPlanPhase = ""
InstallPlanPhasePlanning InstallPlanPhase = "Planning"
InstallPlanPhaseRequiresApproval InstallPlanPhase = "RequiresApproval"
InstallPlanPhaseInstalling InstallPlanPhase = "Installing"
InstallPlanPhaseComplete InstallPlanPhase = "Complete"
InstallPlanPhaseFailed InstallPlanPhase = "Failed"
)
// InstallPlanConditionType describes the state of an InstallPlan at a certain point as a whole.
type InstallPlanConditionType string
const (
InstallPlanResolved InstallPlanConditionType = "Resolved"
InstallPlanInstalled InstallPlanConditionType = "Installed"
)
// ConditionReason is a camelcased reason for the state transition.
type InstallPlanConditionReason string
const (
InstallPlanReasonPlanUnknown InstallPlanConditionReason = "PlanUnknown"
InstallPlanReasonInstallCheckFailed InstallPlanConditionReason = "InstallCheckFailed"
InstallPlanReasonDependencyConflict InstallPlanConditionReason = "DependenciesConflict"
InstallPlanReasonComponentFailed InstallPlanConditionReason = "InstallComponentFailed"
)
// StepStatus is the current status of a particular resource an in
// InstallPlan
type StepStatus string
const (
StepStatusUnknown StepStatus = "Unknown"
StepStatusNotPresent StepStatus = "NotPresent"
StepStatusPresent StepStatus = "Present"
StepStatusCreated StepStatus = "Created"
)
// ErrInvalidInstallPlan is the error returned by functions that operate on
// InstallPlans when the InstallPlan does not contain totally valid data.
var ErrInvalidInstallPlan = errors.New("the InstallPlan contains invalid data")
// InstallPlanStatus represents the information about the status of
// steps required to complete installation.
//
// Status may trail the actual state of a system.
type InstallPlanStatus struct {
Phase InstallPlanPhase `json:"phase"`
Conditions []InstallPlanCondition `json:"conditions,omitempty"`
CatalogSources []string `json:"catalogSources"`
Plan []*Step `json:"plan,omitempty"`
// AttenuatedServiceAccountRef references the service account that is used
// to do scoped operator install.
AttenuatedServiceAccountRef *corev1.ObjectReference `json:"attenuatedServiceAccountRef,omitempty"`
}
// InstallPlanCondition represents the overall status of the execution of
// an InstallPlan.
type InstallPlanCondition struct {
Type InstallPlanConditionType `json:"type,omitempty"`
Status corev1.ConditionStatus `json:"status,omitempty"` // True, False, or Unknown
LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
Reason InstallPlanConditionReason `json:"reason,omitempty"`
Message string `json:"message,omitempty"`
}
// allow overwriting `now` function for deterministic tests
var now = metav1.Now
// GetCondition returns the InstallPlanCondition of the given type if it exists in the InstallPlanStatus' Conditions.
// Returns a condition of the given type with a ConditionStatus of "Unknown" if not found.
func (s InstallPlanStatus) GetCondition(conditionType InstallPlanConditionType) InstallPlanCondition {
for _, cond := range s.Conditions {
if cond.Type == conditionType {
return cond
}
}
return InstallPlanCondition{
Type: conditionType,
Status: corev1.ConditionUnknown,
}
}
// SetCondition adds or updates a condition, using `Type` as merge key.
func (s *InstallPlanStatus) SetCondition(cond InstallPlanCondition) InstallPlanCondition {
for i, existing := range s.Conditions {
if existing.Type != cond.Type {
continue
}
if existing.Status == cond.Status {
cond.LastTransitionTime = existing.LastTransitionTime
}
s.Conditions[i] = cond
return cond
}
s.Conditions = append(s.Conditions, cond)
return cond
}
func ConditionFailed(cond InstallPlanConditionType, reason InstallPlanConditionReason, message string, now *metav1.Time) InstallPlanCondition {
return InstallPlanCondition{
Type: cond,
Status: corev1.ConditionFalse,
Reason: reason,
Message: message,
LastUpdateTime: *now,
LastTransitionTime: *now,
}
}
func ConditionMet(cond InstallPlanConditionType, now *metav1.Time) InstallPlanCondition {
return InstallPlanCondition{
Type: cond,
Status: corev1.ConditionTrue,
LastUpdateTime: *now,
LastTransitionTime: *now,
}
}
// Step represents the status of an individual step in an InstallPlan.
type Step struct {
Resolving string `json:"resolving"`
Resource StepResource `json:"resource"`
Status StepStatus `json:"status"`
}
// ManifestsMatch returns true if the CSV manifests in the StepResources of the given list of steps
// matches those in the InstallPlanStatus.
func (s *InstallPlanStatus) CSVManifestsMatch(steps []*Step) bool {
if s.Plan == nil && steps == nil {
return true
}
if s.Plan == nil || steps == nil {
return false
}
manifests := make(map[string]struct{})
for _, step := range s.Plan {
resource := step.Resource
if resource.Kind != ClusterServiceVersionKind {
continue
}
manifests[resource.Manifest] = struct{}{}
}
for _, step := range steps {
resource := step.Resource
if resource.Kind != ClusterServiceVersionKind {
continue
}
if _, ok := manifests[resource.Manifest]; !ok {
return false
}
delete(manifests, resource.Manifest)
}
if len(manifests) == 0 {
return true
}
return false
}
func (s *Step) String() string {
return fmt.Sprintf("%s: %s (%s)", s.Resolving, s.Resource, s.Status)
}
// StepResource represents the status of a resource to be tracked by an
// InstallPlan.
type StepResource struct {
CatalogSource string `json:"sourceName"`
CatalogSourceNamespace string `json:"sourceNamespace"`
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
Name string `json:"name"`
Manifest string `json:"manifest,omitempty"`
}
func (r StepResource) String() string {
return fmt.Sprintf("%s[%s/%s/%s (%s/%s)]", r.Name, r.Group, r.Version, r.Kind, r.CatalogSource, r.CatalogSourceNamespace)
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// InstallPlan defines the installation of a set of operators.
type InstallPlan struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec InstallPlanSpec `json:"spec"`
Status InstallPlanStatus `json:"status"`
}
// EnsureCatalogSource ensures that a CatalogSource is present in the Status
// block of an InstallPlan.
func (p *InstallPlan) EnsureCatalogSource(sourceName string) {
for _, srcName := range p.Status.CatalogSources {
if srcName == sourceName {
return
}
}
p.Status.CatalogSources = append(p.Status.CatalogSources, sourceName)
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// InstallPlanList is a list of InstallPlan resources.
type InstallPlanList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []InstallPlan `json:"items"`
}

View File

@ -0,0 +1,55 @@
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators"
)
const (
// GroupName is the group name used in this package.
GroupName = operators.GroupName
// GroupVersion is the group version used in this package.
GroupVersion = "v1alpha1"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: GroupVersion}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
// SchemeBuilder initializes a scheme builder
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
// AddToScheme is a global function that registers this API group & version to a scheme
AddToScheme = SchemeBuilder.AddToScheme
// localSchemeBuilder is expected by generated conversion functions
localSchemeBuilder = &SchemeBuilder
)
// addKnownTypes adds the list of known types to Scheme
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&CatalogSource{},
&CatalogSourceList{},
&InstallPlan{},
&InstallPlanList{},
&Subscription{},
&SubscriptionList{},
&ClusterServiceVersion{},
&ClusterServiceVersionList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View File

@ -0,0 +1,315 @@
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
const (
SubscriptionKind = "Subscription"
SubscriptionCRDAPIVersion = GroupName + "/" + GroupVersion
)
// SubscriptionState tracks when updates are available, installing, or service is up to date
type SubscriptionState string
const (
SubscriptionStateNone = ""
SubscriptionStateFailed = "UpgradeFailed"
SubscriptionStateUpgradeAvailable = "UpgradeAvailable"
SubscriptionStateUpgradePending = "UpgradePending"
SubscriptionStateAtLatest = "AtLatestKnown"
)
const (
SubscriptionReasonInvalidCatalog ConditionReason = "InvalidCatalog"
SubscriptionReasonUpgradeSucceeded ConditionReason = "UpgradeSucceeded"
)
// SubscriptionSpec defines an Application that can be installed
type SubscriptionSpec struct {
CatalogSource string `json:"source"`
CatalogSourceNamespace string `json:"sourceNamespace"`
Package string `json:"name"`
Channel string `json:"channel,omitempty"`
StartingCSV string `json:"startingCSV,omitempty"`
InstallPlanApproval Approval `json:"installPlanApproval,omitempty"`
Config SubscriptionConfig `json:"config,omitempty"`
}
// SubscriptionConfig contains configuration specified for a subscription.
type SubscriptionConfig struct {
// Selector is the label selector for pods to be configured.
// Existing ReplicaSets whose pods are
// selected by this will be the ones affected by this deployment.
// It must match the pod template's labels.
Selector *metav1.LabelSelector `json:"selector,omitempty"`
// NodeSelector is a selector which must be true for the pod to fit on a node.
// Selector which must match a node's labels for the pod to be scheduled on that node.
// More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
// +optional
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// Tolerations are the pod's tolerations.
// +optional
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
// Resources represents compute resources required by this container.
// Immutable.
// More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/
// +optional
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
// EnvFrom is a list of sources to populate environment variables in the container.
// The keys defined within a source must be a C_IDENTIFIER. All invalid keys
// will be reported as an event when the container is starting. When a key exists in multiple
// sources, the value associated with the last source will take precedence.
// Values defined by an Env with a duplicate key will take precedence.
// Immutable.
// +optional
EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"`
// Env is a list of environment variables to set in the container.
// Cannot be updated.
// +patchMergeKey=name
// +patchStrategy=merge
// +optional
Env []corev1.EnvVar `json:"env,omitempty" patchMergeKey:"name" patchStrategy:"merge"`
// List of Volumes to set in the podSpec.
// +optional
Volumes []corev1.Volume `json:"volumes,omitempty"`
// List of VolumeMounts to set in the container.
// +optional
VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`
}
// SubscriptionConditionType indicates an explicit state condition about a Subscription in "abnormal-true"
// polarity form (see https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties).
type SubscriptionConditionType string
const (
// SubscriptionCatalogSourcesUnhealthy indicates that some or all of the CatalogSources to be used in resolution are unhealthy.
SubscriptionCatalogSourcesUnhealthy SubscriptionConditionType = "CatalogSourcesUnhealthy"
// SubscriptionInstallPlanMissing indicates that a Subscription's InstallPlan is missing.
SubscriptionInstallPlanMissing SubscriptionConditionType = "InstallPlanMissing"
// SubscriptionInstallPlanPending indicates that a Subscription's InstallPlan is pending installation.
SubscriptionInstallPlanPending SubscriptionConditionType = "InstallPlanPending"
// SubscriptionInstallPlanFailed indicates that the installation of a Subscription's InstallPlan has failed.
SubscriptionInstallPlanFailed SubscriptionConditionType = "InstallPlanFailed"
)
const (
// NoCatalogSourcesFound is a reason string for Subscriptions with unhealthy CatalogSources due to none being available.
NoCatalogSourcesFound = "NoCatalogSourcesFound"
// AllCatalogSourcesHealthy is a reason string for Subscriptions that transitioned due to all CatalogSources being healthy.
AllCatalogSourcesHealthy = "AllCatalogSourcesHealthy"
// CatalogSourcesAdded is a reason string for Subscriptions that transitioned due to CatalogSources being added.
CatalogSourcesAdded = "CatalogSourcesAdded"
// CatalogSourcesUpdated is a reason string for Subscriptions that transitioned due to CatalogSource being updated.
CatalogSourcesUpdated = "CatalogSourcesUpdated"
// CatalogSourcesDeleted is a reason string for Subscriptions that transitioned due to CatalogSources being removed.
CatalogSourcesDeleted = "CatalogSourcesDeleted"
// UnhealthyCatalogSourceFound is a reason string for Subscriptions that transitioned because an unhealthy CatalogSource was found.
UnhealthyCatalogSourceFound = "UnhealthyCatalogSourceFound"
// ReferencedInstallPlanNotFound is a reason string for Subscriptions that transitioned due to a referenced InstallPlan not being found.
ReferencedInstallPlanNotFound = "ReferencedInstallPlanNotFound"
// InstallPlanNotYetReconciled is a reason string for Subscriptions that transitioned due to a referenced InstallPlan not being reconciled yet.
InstallPlanNotYetReconciled = "InstallPlanNotYetReconciled"
// InstallPlanFailed is a reason string for Subscriptions that transitioned due to a referenced InstallPlan failing without setting an explicit failure condition.
InstallPlanFailed = "InstallPlanFailed"
)
// SubscriptionCondition represents the latest available observations of a Subscription's state.
type SubscriptionCondition struct {
// Type is the type of Subscription condition.
Type SubscriptionConditionType `json:"type" description:"type of Subscription condition"`
// Status is the status of the condition, one of True, False, Unknown.
Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"`
// Reason is a one-word CamelCase reason for the condition's last transition.
// +optional
Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"`
// Message is a human-readable message indicating details about last transition.
// +optional
Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"`
// LastHeartbeatTime is the last time we got an update on a given condition
// +optional
LastHeartbeatTime *metav1.Time `json:"lastHeartbeatTime,omitempty" description:"last time we got an update on a given condition"`
// LastTransitionTime is the last time the condition transit from one status to another
// +optional
LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty" description:"last time the condition transit from one status to another" hash:"ignore"`
}
// Equals returns true if a SubscriptionCondition equals the one given, false otherwise.
// Equality is determined by the equality of the type, status, reason, and message fields ONLY.
func (s SubscriptionCondition) Equals(condition SubscriptionCondition) bool {
return s.Type == condition.Type && s.Status == condition.Status && s.Reason == condition.Reason && s.Message == condition.Message
}
type SubscriptionStatus struct {
// CurrentCSV is the CSV the Subscription is progressing to.
// +optional
CurrentCSV string `json:"currentCSV,omitempty"`
// InstalledCSV is the CSV currently installed by the Subscription.
// +optional
InstalledCSV string `json:"installedCSV,omitempty"`
// Install is a reference to the latest InstallPlan generated for the Subscription.
// DEPRECATED: InstallPlanRef
// +optional
Install *InstallPlanReference `json:"installplan,omitempty"`
// State represents the current state of the Subscription
// +optional
State SubscriptionState `json:"state,omitempty"`
// Reason is the reason the Subscription was transitioned to its current state.
// +optional
Reason ConditionReason `json:"reason,omitempty"`
// InstallPlanRef is a reference to the latest InstallPlan that contains the Subscription's current CSV.
// +optional
InstallPlanRef *corev1.ObjectReference `json:"installPlanRef,omitempty"`
// CatalogHealth contains the Subscription's view of its relevant CatalogSources' status.
// It is used to determine SubscriptionStatusConditions related to CatalogSources.
// +optional
CatalogHealth []SubscriptionCatalogHealth `json:"catalogHealth,omitempty"`
// Conditions is a list of the latest available observations about a Subscription's current state.
// +optional
Conditions []SubscriptionCondition `json:"conditions,omitempty" hash:"set"`
// LastUpdated represents the last time that the Subscription status was updated.
LastUpdated metav1.Time `json:"lastUpdated"`
}
// GetCondition returns the SubscriptionCondition of the given type if it exists in the SubscriptionStatus' Conditions.
// Returns a condition of the given type with a ConditionStatus of "Unknown" if not found.
func (s SubscriptionStatus) GetCondition(conditionType SubscriptionConditionType) SubscriptionCondition {
for _, cond := range s.Conditions {
if cond.Type == conditionType {
return cond
}
}
return SubscriptionCondition{
Type: conditionType,
Status: corev1.ConditionUnknown,
}
}
// SetCondition sets the given SubscriptionCondition in the SubscriptionStatus' Conditions.
func (s *SubscriptionStatus) SetCondition(condition SubscriptionCondition) {
for i, cond := range s.Conditions {
if cond.Type == condition.Type {
s.Conditions[i] = condition
return
}
}
s.Conditions = append(s.Conditions, condition)
}
// RemoveConditions removes any conditions of the given types from the SubscriptionStatus' Conditions.
func (s *SubscriptionStatus) RemoveConditions(remove ...SubscriptionConditionType) {
exclusions := map[SubscriptionConditionType]struct{}{}
for _, r := range remove {
exclusions[r] = struct{}{}
}
var filtered []SubscriptionCondition
for _, cond := range s.Conditions {
if _, ok := exclusions[cond.Type]; ok {
// Skip excluded condition types
continue
}
filtered = append(filtered, cond)
}
s.Conditions = filtered
}
type InstallPlanReference struct {
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
Name string `json:"name"`
UID types.UID `json:"uuid"`
}
// SubscriptionCatalogHealth describes the health of a CatalogSource the Subscription knows about.
type SubscriptionCatalogHealth struct {
// CatalogSourceRef is a reference to a CatalogSource.
CatalogSourceRef *corev1.ObjectReference `json:"catalogSourceRef"`
// LastUpdated represents the last time that the CatalogSourceHealth changed
LastUpdated *metav1.Time `json:"lastUpdated"`
// Healthy is true if the CatalogSource is healthy; false otherwise.
Healthy bool `json:"healthy"`
}
// Equals returns true if a SubscriptionCatalogHealth equals the one given, false otherwise.
// Equality is based SOLEY on health and UID.
func (s SubscriptionCatalogHealth) Equals(health SubscriptionCatalogHealth) bool {
return s.Healthy == health.Healthy && s.CatalogSourceRef.UID == health.CatalogSourceRef.UID
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// Subscription keeps operators up to date by tracking changes to Catalogs.
type Subscription struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec *SubscriptionSpec `json:"spec"`
Status SubscriptionStatus `json:"status"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// SubscriptionList is a list of Subscription resources.
type SubscriptionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Subscription `json:"items"`
}
// GetInstallPlanApproval gets the configured install plan approval or the default
func (s *Subscription) GetInstallPlanApproval() Approval {
if s.Spec.InstallPlanApproval == ApprovalManual {
return ApprovalManual
}
return ApprovalAutomatic
}
// NewInstallPlanReference returns an InstallPlanReference for the given ObjectReference.
func NewInstallPlanReference(ref *corev1.ObjectReference) *InstallPlanReference {
return &InstallPlanReference{
APIVersion: ref.APIVersion,
Kind: ref.Kind,
Name: ref.Name,
UID: ref.UID,
}
}

View File

@ -0,0 +1,66 @@
package version
import (
"encoding/json"
"github.com/blang/semver"
)
// +k8s:openapi-gen=true
// OperatorVersion is a wrapper around semver.Version which supports correct
// marshaling to YAML and JSON.
type OperatorVersion struct {
semver.Version
}
// DeepCopyInto creates a deep-copy of the Version value.
func (v *OperatorVersion) DeepCopyInto(out *OperatorVersion) {
out.Major = v.Major
out.Minor = v.Minor
out.Patch = v.Patch
if v.Pre != nil {
pre := make([]semver.PRVersion, len(v.Pre))
copy(pre, v.Pre)
out.Pre = pre
}
if v.Build != nil {
build := make([]string, len(v.Build))
copy(build, v.Build)
out.Build = build
}
}
// MarshalJSON implements the encoding/json.Marshaler interface.
func (v OperatorVersion) MarshalJSON() ([]byte, error) {
return json.Marshal(v.String())
}
// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
func (v *OperatorVersion) UnmarshalJSON(data []byte) (err error) {
var versionString string
if err = json.Unmarshal(data, &versionString); err != nil {
return
}
version := semver.Version{}
version, err = semver.ParseTolerant(versionString)
if err != nil {
return err
}
v.Version = version
return
}
// OpenAPISchemaType is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
//
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
func (_ OperatorVersion) OpenAPISchemaType() []string { return []string{"string"} }
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
// "semver" is not a standard openapi format but tooling may use the value regardless
func (_ OperatorVersion) OpenAPISchemaFormat() string { return "semver" }

View File

@ -0,0 +1,5 @@
// +k8s:deepcopy-gen=package
// Package operators is the internal version of the API.
// +groupName=operators.coreos.com
package operators

View File

@ -0,0 +1,71 @@
package operators
import operatorsv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
// CreateCSVDescription creates a CSVDescription from a given CSV
func CreateCSVDescription(csv *operatorsv1alpha1.ClusterServiceVersion) CSVDescription {
desc := CSVDescription{
DisplayName: csv.Spec.DisplayName,
Version: csv.Spec.Version,
Provider: AppLink{
Name: csv.Spec.Provider.Name,
URL: csv.Spec.Provider.URL,
},
Annotations: csv.GetAnnotations(),
LongDescription: csv.Spec.Description,
InstallModes: csv.Spec.InstallModes,
CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{
Owned: descriptionsForCRDs(csv.Spec.CustomResourceDefinitions.Owned),
Required: descriptionsForCRDs(csv.Spec.CustomResourceDefinitions.Required),
},
APIServiceDefinitions: operatorsv1alpha1.APIServiceDefinitions{
Owned: descriptionsForAPIServices(csv.Spec.APIServiceDefinitions.Owned),
Required: descriptionsForAPIServices(csv.Spec.APIServiceDefinitions.Required),
},
}
icons := make([]Icon, len(csv.Spec.Icon))
for i, icon := range csv.Spec.Icon {
icons[i] = Icon{
Base64Data: icon.Data,
Mediatype: icon.MediaType,
}
}
if len(icons) > 0 {
desc.Icon = icons
}
return desc
}
// descriptionsForCRDs filters certain fields from provided API descriptions to reduce response size.
func descriptionsForCRDs(crds []operatorsv1alpha1.CRDDescription) []operatorsv1alpha1.CRDDescription {
descriptions := []operatorsv1alpha1.CRDDescription{}
for _, crd := range crds {
descriptions = append(descriptions, operatorsv1alpha1.CRDDescription{
Name: crd.Name,
Version: crd.Version,
Kind: crd.Kind,
DisplayName: crd.DisplayName,
Description: crd.Description,
})
}
return descriptions
}
// descriptionsForAPIServices filters certain fields from provided API descriptions to reduce response size.
func descriptionsForAPIServices(apis []operatorsv1alpha1.APIServiceDescription) []operatorsv1alpha1.APIServiceDescription {
descriptions := []operatorsv1alpha1.APIServiceDescription{}
for _, api := range apis {
descriptions = append(descriptions, operatorsv1alpha1.APIServiceDescription{
Name: api.Name,
Group: api.Group,
Version: api.Version,
Kind: api.Kind,
DisplayName: api.DisplayName,
Description: api.Description,
})
}
return descriptions
}

View File

@ -0,0 +1,125 @@
package operators
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
operatorv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version"
)
// PackageManifestList is a list of PackageManifest objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type PackageManifestList struct {
metav1.TypeMeta
metav1.ListMeta
Items []PackageManifest
}
// PackageManifest holds information about a package, which is a reference to one (or more)
// channels under a single package.
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type PackageManifest struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec PackageManifestSpec
Status PackageManifestStatus
}
// PackageManifestSpec defines the desired state of PackageManifest
type PackageManifestSpec struct{}
// PackageManifestStatus represents the current status of the PackageManifest
type PackageManifestStatus struct {
// CatalogSource is the name of the CatalogSource this package belongs to
CatalogSource string
CatalogSourceDisplayName string
CatalogSourcePublisher string
// CatalogSourceNamespace is the namespace of the owning CatalogSource
CatalogSourceNamespace string
// Provider is the provider of the PackageManifest's default CSV
Provider AppLink
// PackageName is the name of the overall package, ala .
PackageName string
// Channels are the declared channels for the package, ala .
Channels []PackageChannel
// DefaultChannel is, if specified, the name of the default channel for the package. The
// default channel will be installed if no other channel is explicitly given. If the package
// has a single channel, then that channel is implicitly the default.
DefaultChannel string
}
// GetDefaultChannel gets the default channel or returns the only one if there's only one. returns empty string if it
// can't determine the default
func (m PackageManifest) GetDefaultChannel() string {
if m.Status.DefaultChannel != "" {
return m.Status.DefaultChannel
}
if len(m.Status.Channels) == 1 {
return m.Status.Channels[0].Name
}
return ""
}
// PackageChannel defines a single channel under a package, pointing to a version of that
// package.
type PackageChannel struct {
// Name is the name of the channel, e.g.
Name string
// CurrentCSV defines a reference to the CSV holding the version of this package currently
// for the channel.
CurrentCSV string
// CurrentCSVSpec holds the spec of the current CSV
CurrentCSVDesc CSVDescription
}
// CSVDescription defines a description of a CSV
type CSVDescription struct {
// DisplayName is the CSV's display name
DisplayName string
// Icon is the CSV's base64 encoded icon
Icon []Icon
// Version is the CSV's semantic version
Version version.OperatorVersion
// Provider is the CSV's provider
Provider AppLink
Annotations map[string]string
// LongDescription is the CSV's description
LongDescription string
// InstallModes specify supported installation types
InstallModes []operatorv1alpha1.InstallMode
CustomResourceDefinitions operatorv1alpha1.CustomResourceDefinitions
APIServiceDefinitions operatorv1alpha1.APIServiceDefinitions
}
// AppLink defines a link to an application
type AppLink struct {
Name string
URL string
}
// Icon defines a base64 encoded icon and media type
type Icon struct {
Base64Data string
Mediatype string
}
// IsDefaultChannel returns true if the PackageChannel is the default for the PackageManifest
func (pc PackageChannel) IsDefaultChannel(pm PackageManifest) bool {
return pc.Name == pm.Status.DefaultChannel || len(pm.Status.Channels) == 1
}

View File

@ -0,0 +1,36 @@
package operators
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
var (
Group = "packages.operators.coreos.com"
// SchemeGroupVersion is the GroupVersion used to register this object
SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: runtime.APIVersionInternal}
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Kind takes an unqualified kind and returns the group-qualified kind.
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns the group-qualified resource.
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
// addKnownTypes adds the set of types defined in this package to the supplied scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
// Add types for each GroupVersion
scheme.AddKnownTypes(SchemeGroupVersion,
&PackageManifest{},
&PackageManifestList{},
)
return nil
}

View File

@ -0,0 +1,7 @@
// +k8s:deepcopy-gen=package
// +k8s:conversion-gen=github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators
// +k8s:defaulter-gen=TypeMeta
// +k8s:openapi-gen=true
// +groupName=operators.coreos.com
package v1

View File

@ -0,0 +1,34 @@
package v1
import operatorsv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
// CreateCSVDescription creates a CSVDescription from a given CSV
func CreateCSVDescription(csv *operatorsv1alpha1.ClusterServiceVersion) CSVDescription {
desc := CSVDescription{
DisplayName: csv.Spec.DisplayName,
Version: csv.Spec.Version,
Provider: AppLink{
Name: csv.Spec.Provider.Name,
URL: csv.Spec.Provider.URL,
},
Annotations: csv.GetAnnotations(),
LongDescription: csv.Spec.Description,
InstallModes: csv.Spec.InstallModes,
CustomResourceDefinitions: csv.Spec.CustomResourceDefinitions,
APIServiceDefinitions: csv.Spec.APIServiceDefinitions,
}
icons := make([]Icon, len(csv.Spec.Icon))
for i, icon := range csv.Spec.Icon {
icons[i] = Icon{
Base64Data: icon.Data,
Mediatype: icon.MediaType,
}
}
if len(icons) > 0 {
desc.Icon = icons
}
return desc
}

View File

@ -0,0 +1,125 @@
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
operatorv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version"
)
// PackageManifestList is a list of PackageManifest objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type PackageManifestList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []PackageManifest `json:"items"`
}
// PackageManifest holds information about a package, which is a reference to one (or more)
// channels under a single package.
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type PackageManifest struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec PackageManifestSpec `json:"spec,omitempty"`
Status PackageManifestStatus `json:"status,omitempty"`
}
// PackageManifestSpec defines the desired state of PackageManifest
type PackageManifestSpec struct{}
// PackageManifestStatus represents the current status of the PackageManifest
type PackageManifestStatus struct {
// CatalogSource is the name of the CatalogSource this package belongs to
CatalogSource string `json:"catalogSource"`
CatalogSourceDisplayName string `json:"catalogSourceDisplayName"`
CatalogSourcePublisher string `json:"catalogSourcePublisher"`
// CatalogSourceNamespace is the namespace of the owning CatalogSource
CatalogSourceNamespace string `json:"catalogSourceNamespace"`
// Provider is the provider of the PackageManifest's default CSV
Provider AppLink `json:"provider,omitempty"`
// PackageName is the name of the overall package, ala `etcd`.
PackageName string `json:"packageName"`
// Channels are the declared channels for the package, ala `stable` or `alpha`.
Channels []PackageChannel `json:"channels"`
// DefaultChannel is, if specified, the name of the default channel for the package. The
// default channel will be installed if no other channel is explicitly given. If the package
// has a single channel, then that channel is implicitly the default.
DefaultChannel string `json:"defaultChannel"`
}
// GetDefaultChannel gets the default channel or returns the only one if there's only one. returns empty string if it
// can't determine the default
func (m PackageManifest) GetDefaultChannel() string {
if m.Status.DefaultChannel != "" {
return m.Status.DefaultChannel
}
if len(m.Status.Channels) == 1 {
return m.Status.Channels[0].Name
}
return ""
}
// PackageChannel defines a single channel under a package, pointing to a version of that
// package.
type PackageChannel struct {
// Name is the name of the channel, e.g. `alpha` or `stable`
Name string `json:"name"`
// CurrentCSV defines a reference to the CSV holding the version of this package currently
// for the channel.
CurrentCSV string `json:"currentCSV"`
// CurrentCSVSpec holds the spec of the current CSV
CurrentCSVDesc CSVDescription `json:"currentCSVDesc,omitempty"`
}
// CSVDescription defines a description of a CSV
type CSVDescription struct {
// DisplayName is the CSV's display name
DisplayName string `json:"displayName,omitempty"`
// Icon is the CSV's base64 encoded icon
Icon []Icon `json:"icon,omitempty"`
// Version is the CSV's semantic version
Version version.OperatorVersion `json:"version,omitempty"`
// Provider is the CSV's provider
Provider AppLink `json:"provider,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
// LongDescription is the CSV's description
LongDescription string `json:"description,omitempty"`
// InstallModes specify supported installation types
InstallModes []operatorv1alpha1.InstallMode `json:"installModes,omitempty"`
CustomResourceDefinitions operatorv1alpha1.CustomResourceDefinitions `json:"customresourcedefinitions,omitempty"`
APIServiceDefinitions operatorv1alpha1.APIServiceDefinitions `json:"apiservicedefinitions,omitempty"`
}
// AppLink defines a link to an application
type AppLink struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
}
// Icon defines a base64 encoded icon and media type
type Icon struct {
Base64Data string `json:"base64data,omitempty"`
Mediatype string `json:"mediatype,omitempty"`
}
// IsDefaultChannel returns true if the PackageChannel is the default for the PackageManifest
func (pc PackageChannel) IsDefaultChannel(pm PackageManifest) bool {
return pc.Name == pm.Status.DefaultChannel || len(pm.Status.Channels) == 1
}

View File

@ -0,0 +1,45 @@
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators"
)
const (
Group = "packages." + operators.GroupName
Version = "v1"
PackageManifestKind = "PackageManifest"
PackageManifestListKind = "PackageManifestList"
)
// SchemeGroupVersion is the group version used to register these objects.
var SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
// Resource takes an unqualified resource and returns a Group-qualified GroupResource.
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
// addKnownTypes adds the set of types defined in this package to the supplied scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypeWithName(
SchemeGroupVersion.WithKind(PackageManifestKind),
&PackageManifest{},
)
scheme.AddKnownTypeWithName(
SchemeGroupVersion.WithKind(PackageManifestListKind),
&PackageManifestList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View File

@ -0,0 +1,342 @@
// +build !ignore_autogenerated
/*
Copyright 2019 Red Hat, Inc.
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 conversion-gen. DO NOT EDIT.
package v1
import (
unsafe "unsafe"
v1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
operators "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
)
func init() {
localSchemeBuilder.Register(RegisterConversions)
}
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*AppLink)(nil), (*operators.AppLink)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_AppLink_To_operators_AppLink(a.(*AppLink), b.(*operators.AppLink), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*operators.AppLink)(nil), (*AppLink)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_operators_AppLink_To_v1_AppLink(a.(*operators.AppLink), b.(*AppLink), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*CSVDescription)(nil), (*operators.CSVDescription)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_CSVDescription_To_operators_CSVDescription(a.(*CSVDescription), b.(*operators.CSVDescription), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*operators.CSVDescription)(nil), (*CSVDescription)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_operators_CSVDescription_To_v1_CSVDescription(a.(*operators.CSVDescription), b.(*CSVDescription), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*Icon)(nil), (*operators.Icon)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_Icon_To_operators_Icon(a.(*Icon), b.(*operators.Icon), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*operators.Icon)(nil), (*Icon)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_operators_Icon_To_v1_Icon(a.(*operators.Icon), b.(*Icon), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*PackageChannel)(nil), (*operators.PackageChannel)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_PackageChannel_To_operators_PackageChannel(a.(*PackageChannel), b.(*operators.PackageChannel), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*operators.PackageChannel)(nil), (*PackageChannel)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_operators_PackageChannel_To_v1_PackageChannel(a.(*operators.PackageChannel), b.(*PackageChannel), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*PackageManifest)(nil), (*operators.PackageManifest)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_PackageManifest_To_operators_PackageManifest(a.(*PackageManifest), b.(*operators.PackageManifest), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*operators.PackageManifest)(nil), (*PackageManifest)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_operators_PackageManifest_To_v1_PackageManifest(a.(*operators.PackageManifest), b.(*PackageManifest), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*PackageManifestList)(nil), (*operators.PackageManifestList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_PackageManifestList_To_operators_PackageManifestList(a.(*PackageManifestList), b.(*operators.PackageManifestList), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*operators.PackageManifestList)(nil), (*PackageManifestList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_operators_PackageManifestList_To_v1_PackageManifestList(a.(*operators.PackageManifestList), b.(*PackageManifestList), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*PackageManifestSpec)(nil), (*operators.PackageManifestSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_PackageManifestSpec_To_operators_PackageManifestSpec(a.(*PackageManifestSpec), b.(*operators.PackageManifestSpec), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*operators.PackageManifestSpec)(nil), (*PackageManifestSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_operators_PackageManifestSpec_To_v1_PackageManifestSpec(a.(*operators.PackageManifestSpec), b.(*PackageManifestSpec), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*PackageManifestStatus)(nil), (*operators.PackageManifestStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_PackageManifestStatus_To_operators_PackageManifestStatus(a.(*PackageManifestStatus), b.(*operators.PackageManifestStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*operators.PackageManifestStatus)(nil), (*PackageManifestStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_operators_PackageManifestStatus_To_v1_PackageManifestStatus(a.(*operators.PackageManifestStatus), b.(*PackageManifestStatus), scope)
}); err != nil {
return err
}
return nil
}
func autoConvert_v1_AppLink_To_operators_AppLink(in *AppLink, out *operators.AppLink, s conversion.Scope) error {
out.Name = in.Name
out.URL = in.URL
return nil
}
// Convert_v1_AppLink_To_operators_AppLink is an autogenerated conversion function.
func Convert_v1_AppLink_To_operators_AppLink(in *AppLink, out *operators.AppLink, s conversion.Scope) error {
return autoConvert_v1_AppLink_To_operators_AppLink(in, out, s)
}
func autoConvert_operators_AppLink_To_v1_AppLink(in *operators.AppLink, out *AppLink, s conversion.Scope) error {
out.Name = in.Name
out.URL = in.URL
return nil
}
// Convert_operators_AppLink_To_v1_AppLink is an autogenerated conversion function.
func Convert_operators_AppLink_To_v1_AppLink(in *operators.AppLink, out *AppLink, s conversion.Scope) error {
return autoConvert_operators_AppLink_To_v1_AppLink(in, out, s)
}
func autoConvert_v1_CSVDescription_To_operators_CSVDescription(in *CSVDescription, out *operators.CSVDescription, s conversion.Scope) error {
out.DisplayName = in.DisplayName
out.Icon = *(*[]operators.Icon)(unsafe.Pointer(&in.Icon))
out.Version = in.Version
if err := Convert_v1_AppLink_To_operators_AppLink(&in.Provider, &out.Provider, s); err != nil {
return err
}
out.Annotations = *(*map[string]string)(unsafe.Pointer(&in.Annotations))
out.LongDescription = in.LongDescription
out.InstallModes = *(*[]v1alpha1.InstallMode)(unsafe.Pointer(&in.InstallModes))
out.CustomResourceDefinitions = in.CustomResourceDefinitions
out.APIServiceDefinitions = in.APIServiceDefinitions
return nil
}
// Convert_v1_CSVDescription_To_operators_CSVDescription is an autogenerated conversion function.
func Convert_v1_CSVDescription_To_operators_CSVDescription(in *CSVDescription, out *operators.CSVDescription, s conversion.Scope) error {
return autoConvert_v1_CSVDescription_To_operators_CSVDescription(in, out, s)
}
func autoConvert_operators_CSVDescription_To_v1_CSVDescription(in *operators.CSVDescription, out *CSVDescription, s conversion.Scope) error {
out.DisplayName = in.DisplayName
out.Icon = *(*[]Icon)(unsafe.Pointer(&in.Icon))
out.Version = in.Version
if err := Convert_operators_AppLink_To_v1_AppLink(&in.Provider, &out.Provider, s); err != nil {
return err
}
out.Annotations = *(*map[string]string)(unsafe.Pointer(&in.Annotations))
out.LongDescription = in.LongDescription
out.InstallModes = *(*[]v1alpha1.InstallMode)(unsafe.Pointer(&in.InstallModes))
out.CustomResourceDefinitions = in.CustomResourceDefinitions
out.APIServiceDefinitions = in.APIServiceDefinitions
return nil
}
// Convert_operators_CSVDescription_To_v1_CSVDescription is an autogenerated conversion function.
func Convert_operators_CSVDescription_To_v1_CSVDescription(in *operators.CSVDescription, out *CSVDescription, s conversion.Scope) error {
return autoConvert_operators_CSVDescription_To_v1_CSVDescription(in, out, s)
}
func autoConvert_v1_Icon_To_operators_Icon(in *Icon, out *operators.Icon, s conversion.Scope) error {
out.Base64Data = in.Base64Data
out.Mediatype = in.Mediatype
return nil
}
// Convert_v1_Icon_To_operators_Icon is an autogenerated conversion function.
func Convert_v1_Icon_To_operators_Icon(in *Icon, out *operators.Icon, s conversion.Scope) error {
return autoConvert_v1_Icon_To_operators_Icon(in, out, s)
}
func autoConvert_operators_Icon_To_v1_Icon(in *operators.Icon, out *Icon, s conversion.Scope) error {
out.Base64Data = in.Base64Data
out.Mediatype = in.Mediatype
return nil
}
// Convert_operators_Icon_To_v1_Icon is an autogenerated conversion function.
func Convert_operators_Icon_To_v1_Icon(in *operators.Icon, out *Icon, s conversion.Scope) error {
return autoConvert_operators_Icon_To_v1_Icon(in, out, s)
}
func autoConvert_v1_PackageChannel_To_operators_PackageChannel(in *PackageChannel, out *operators.PackageChannel, s conversion.Scope) error {
out.Name = in.Name
out.CurrentCSV = in.CurrentCSV
if err := Convert_v1_CSVDescription_To_operators_CSVDescription(&in.CurrentCSVDesc, &out.CurrentCSVDesc, s); err != nil {
return err
}
return nil
}
// Convert_v1_PackageChannel_To_operators_PackageChannel is an autogenerated conversion function.
func Convert_v1_PackageChannel_To_operators_PackageChannel(in *PackageChannel, out *operators.PackageChannel, s conversion.Scope) error {
return autoConvert_v1_PackageChannel_To_operators_PackageChannel(in, out, s)
}
func autoConvert_operators_PackageChannel_To_v1_PackageChannel(in *operators.PackageChannel, out *PackageChannel, s conversion.Scope) error {
out.Name = in.Name
out.CurrentCSV = in.CurrentCSV
if err := Convert_operators_CSVDescription_To_v1_CSVDescription(&in.CurrentCSVDesc, &out.CurrentCSVDesc, s); err != nil {
return err
}
return nil
}
// Convert_operators_PackageChannel_To_v1_PackageChannel is an autogenerated conversion function.
func Convert_operators_PackageChannel_To_v1_PackageChannel(in *operators.PackageChannel, out *PackageChannel, s conversion.Scope) error {
return autoConvert_operators_PackageChannel_To_v1_PackageChannel(in, out, s)
}
func autoConvert_v1_PackageManifest_To_operators_PackageManifest(in *PackageManifest, out *operators.PackageManifest, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_v1_PackageManifestSpec_To_operators_PackageManifestSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_v1_PackageManifestStatus_To_operators_PackageManifestStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
// Convert_v1_PackageManifest_To_operators_PackageManifest is an autogenerated conversion function.
func Convert_v1_PackageManifest_To_operators_PackageManifest(in *PackageManifest, out *operators.PackageManifest, s conversion.Scope) error {
return autoConvert_v1_PackageManifest_To_operators_PackageManifest(in, out, s)
}
func autoConvert_operators_PackageManifest_To_v1_PackageManifest(in *operators.PackageManifest, out *PackageManifest, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_operators_PackageManifestSpec_To_v1_PackageManifestSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_operators_PackageManifestStatus_To_v1_PackageManifestStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
// Convert_operators_PackageManifest_To_v1_PackageManifest is an autogenerated conversion function.
func Convert_operators_PackageManifest_To_v1_PackageManifest(in *operators.PackageManifest, out *PackageManifest, s conversion.Scope) error {
return autoConvert_operators_PackageManifest_To_v1_PackageManifest(in, out, s)
}
func autoConvert_v1_PackageManifestList_To_operators_PackageManifestList(in *PackageManifestList, out *operators.PackageManifestList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
out.Items = *(*[]operators.PackageManifest)(unsafe.Pointer(&in.Items))
return nil
}
// Convert_v1_PackageManifestList_To_operators_PackageManifestList is an autogenerated conversion function.
func Convert_v1_PackageManifestList_To_operators_PackageManifestList(in *PackageManifestList, out *operators.PackageManifestList, s conversion.Scope) error {
return autoConvert_v1_PackageManifestList_To_operators_PackageManifestList(in, out, s)
}
func autoConvert_operators_PackageManifestList_To_v1_PackageManifestList(in *operators.PackageManifestList, out *PackageManifestList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
out.Items = *(*[]PackageManifest)(unsafe.Pointer(&in.Items))
return nil
}
// Convert_operators_PackageManifestList_To_v1_PackageManifestList is an autogenerated conversion function.
func Convert_operators_PackageManifestList_To_v1_PackageManifestList(in *operators.PackageManifestList, out *PackageManifestList, s conversion.Scope) error {
return autoConvert_operators_PackageManifestList_To_v1_PackageManifestList(in, out, s)
}
func autoConvert_v1_PackageManifestSpec_To_operators_PackageManifestSpec(in *PackageManifestSpec, out *operators.PackageManifestSpec, s conversion.Scope) error {
return nil
}
// Convert_v1_PackageManifestSpec_To_operators_PackageManifestSpec is an autogenerated conversion function.
func Convert_v1_PackageManifestSpec_To_operators_PackageManifestSpec(in *PackageManifestSpec, out *operators.PackageManifestSpec, s conversion.Scope) error {
return autoConvert_v1_PackageManifestSpec_To_operators_PackageManifestSpec(in, out, s)
}
func autoConvert_operators_PackageManifestSpec_To_v1_PackageManifestSpec(in *operators.PackageManifestSpec, out *PackageManifestSpec, s conversion.Scope) error {
return nil
}
// Convert_operators_PackageManifestSpec_To_v1_PackageManifestSpec is an autogenerated conversion function.
func Convert_operators_PackageManifestSpec_To_v1_PackageManifestSpec(in *operators.PackageManifestSpec, out *PackageManifestSpec, s conversion.Scope) error {
return autoConvert_operators_PackageManifestSpec_To_v1_PackageManifestSpec(in, out, s)
}
func autoConvert_v1_PackageManifestStatus_To_operators_PackageManifestStatus(in *PackageManifestStatus, out *operators.PackageManifestStatus, s conversion.Scope) error {
out.CatalogSource = in.CatalogSource
out.CatalogSourceDisplayName = in.CatalogSourceDisplayName
out.CatalogSourcePublisher = in.CatalogSourcePublisher
out.CatalogSourceNamespace = in.CatalogSourceNamespace
if err := Convert_v1_AppLink_To_operators_AppLink(&in.Provider, &out.Provider, s); err != nil {
return err
}
out.PackageName = in.PackageName
out.Channels = *(*[]operators.PackageChannel)(unsafe.Pointer(&in.Channels))
out.DefaultChannel = in.DefaultChannel
return nil
}
// Convert_v1_PackageManifestStatus_To_operators_PackageManifestStatus is an autogenerated conversion function.
func Convert_v1_PackageManifestStatus_To_operators_PackageManifestStatus(in *PackageManifestStatus, out *operators.PackageManifestStatus, s conversion.Scope) error {
return autoConvert_v1_PackageManifestStatus_To_operators_PackageManifestStatus(in, out, s)
}
func autoConvert_operators_PackageManifestStatus_To_v1_PackageManifestStatus(in *operators.PackageManifestStatus, out *PackageManifestStatus, s conversion.Scope) error {
out.CatalogSource = in.CatalogSource
out.CatalogSourceDisplayName = in.CatalogSourceDisplayName
out.CatalogSourcePublisher = in.CatalogSourcePublisher
out.CatalogSourceNamespace = in.CatalogSourceNamespace
if err := Convert_operators_AppLink_To_v1_AppLink(&in.Provider, &out.Provider, s); err != nil {
return err
}
out.PackageName = in.PackageName
out.Channels = *(*[]PackageChannel)(unsafe.Pointer(&in.Channels))
out.DefaultChannel = in.DefaultChannel
return nil
}
// Convert_operators_PackageManifestStatus_To_v1_PackageManifestStatus is an autogenerated conversion function.
func Convert_operators_PackageManifestStatus_To_v1_PackageManifestStatus(in *operators.PackageManifestStatus, out *PackageManifestStatus, s conversion.Scope) error {
return autoConvert_operators_PackageManifestStatus_To_v1_PackageManifestStatus(in, out, s)
}

View File

@ -0,0 +1,213 @@
// +build !ignore_autogenerated
/*
Copyright 2019 Red Hat, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1
import (
v1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AppLink) DeepCopyInto(out *AppLink) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppLink.
func (in *AppLink) DeepCopy() *AppLink {
if in == nil {
return nil
}
out := new(AppLink)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CSVDescription) DeepCopyInto(out *CSVDescription) {
*out = *in
if in.Icon != nil {
in, out := &in.Icon, &out.Icon
*out = make([]Icon, len(*in))
copy(*out, *in)
}
in.Version.DeepCopyInto(&out.Version)
out.Provider = in.Provider
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.InstallModes != nil {
in, out := &in.InstallModes, &out.InstallModes
*out = make([]v1alpha1.InstallMode, len(*in))
copy(*out, *in)
}
in.CustomResourceDefinitions.DeepCopyInto(&out.CustomResourceDefinitions)
in.APIServiceDefinitions.DeepCopyInto(&out.APIServiceDefinitions)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CSVDescription.
func (in *CSVDescription) DeepCopy() *CSVDescription {
if in == nil {
return nil
}
out := new(CSVDescription)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Icon) DeepCopyInto(out *Icon) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Icon.
func (in *Icon) DeepCopy() *Icon {
if in == nil {
return nil
}
out := new(Icon)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PackageChannel) DeepCopyInto(out *PackageChannel) {
*out = *in
in.CurrentCSVDesc.DeepCopyInto(&out.CurrentCSVDesc)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageChannel.
func (in *PackageChannel) DeepCopy() *PackageChannel {
if in == nil {
return nil
}
out := new(PackageChannel)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PackageManifest) DeepCopyInto(out *PackageManifest) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageManifest.
func (in *PackageManifest) DeepCopy() *PackageManifest {
if in == nil {
return nil
}
out := new(PackageManifest)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *PackageManifest) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PackageManifestList) DeepCopyInto(out *PackageManifestList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]PackageManifest, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageManifestList.
func (in *PackageManifestList) DeepCopy() *PackageManifestList {
if in == nil {
return nil
}
out := new(PackageManifestList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *PackageManifestList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PackageManifestSpec) DeepCopyInto(out *PackageManifestSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageManifestSpec.
func (in *PackageManifestSpec) DeepCopy() *PackageManifestSpec {
if in == nil {
return nil
}
out := new(PackageManifestSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PackageManifestStatus) DeepCopyInto(out *PackageManifestStatus) {
*out = *in
out.Provider = in.Provider
if in.Channels != nil {
in, out := &in.Channels, &out.Channels
*out = make([]PackageChannel, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageManifestStatus.
func (in *PackageManifestStatus) DeepCopy() *PackageManifestStatus {
if in == nil {
return nil
}
out := new(PackageManifestStatus)
in.DeepCopyInto(out)
return out
}

View File

@ -0,0 +1,32 @@
// +build !ignore_autogenerated
/*
Copyright 2019 Red Hat, Inc.
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 defaulter-gen. DO NOT EDIT.
package v1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// RegisterDefaults adds defaulters functions to the given scheme.
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
return nil
}

View File

@ -0,0 +1,213 @@
// +build !ignore_autogenerated
/*
Copyright 2019 Red Hat, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package operators
import (
v1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AppLink) DeepCopyInto(out *AppLink) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppLink.
func (in *AppLink) DeepCopy() *AppLink {
if in == nil {
return nil
}
out := new(AppLink)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CSVDescription) DeepCopyInto(out *CSVDescription) {
*out = *in
if in.Icon != nil {
in, out := &in.Icon, &out.Icon
*out = make([]Icon, len(*in))
copy(*out, *in)
}
in.Version.DeepCopyInto(&out.Version)
out.Provider = in.Provider
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.InstallModes != nil {
in, out := &in.InstallModes, &out.InstallModes
*out = make([]v1alpha1.InstallMode, len(*in))
copy(*out, *in)
}
in.CustomResourceDefinitions.DeepCopyInto(&out.CustomResourceDefinitions)
in.APIServiceDefinitions.DeepCopyInto(&out.APIServiceDefinitions)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CSVDescription.
func (in *CSVDescription) DeepCopy() *CSVDescription {
if in == nil {
return nil
}
out := new(CSVDescription)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Icon) DeepCopyInto(out *Icon) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Icon.
func (in *Icon) DeepCopy() *Icon {
if in == nil {
return nil
}
out := new(Icon)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PackageChannel) DeepCopyInto(out *PackageChannel) {
*out = *in
in.CurrentCSVDesc.DeepCopyInto(&out.CurrentCSVDesc)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageChannel.
func (in *PackageChannel) DeepCopy() *PackageChannel {
if in == nil {
return nil
}
out := new(PackageChannel)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PackageManifest) DeepCopyInto(out *PackageManifest) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageManifest.
func (in *PackageManifest) DeepCopy() *PackageManifest {
if in == nil {
return nil
}
out := new(PackageManifest)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *PackageManifest) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PackageManifestList) DeepCopyInto(out *PackageManifestList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]PackageManifest, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageManifestList.
func (in *PackageManifestList) DeepCopy() *PackageManifestList {
if in == nil {
return nil
}
out := new(PackageManifestList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *PackageManifestList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PackageManifestSpec) DeepCopyInto(out *PackageManifestSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageManifestSpec.
func (in *PackageManifestSpec) DeepCopy() *PackageManifestSpec {
if in == nil {
return nil
}
out := new(PackageManifestSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PackageManifestStatus) DeepCopyInto(out *PackageManifestStatus) {
*out = *in
out.Provider = in.Provider
if in.Channels != nil {
in, out := &in.Channels, &out.Channels
*out = make([]PackageChannel, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageManifestStatus.
func (in *PackageManifestStatus) DeepCopy() *PackageManifestStatus {
if in == nil {
return nil
}
out := new(PackageManifestStatus)
in.DeepCopyInto(out)
return out
}

View File

@ -1,15 +1,10 @@
language: go
go_import_path: github.com/pkg/errors
go:
- 1.4.x
- 1.5.x
- 1.6.x
- 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
- 1.12.x
- 1.13.x
- tip
script:
- go test -v ./...
- make check

44
vendor/github.com/pkg/errors/Makefile generated vendored Normal file
View File

@ -0,0 +1,44 @@
PKGS := github.com/pkg/errors
SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS))
GO := go
check: test vet gofmt misspell unconvert staticcheck ineffassign unparam
test:
$(GO) test $(PKGS)
vet: | test
$(GO) vet $(PKGS)
staticcheck:
$(GO) get honnef.co/go/tools/cmd/staticcheck
staticcheck -checks all $(PKGS)
misspell:
$(GO) get github.com/client9/misspell/cmd/misspell
misspell \
-locale GB \
-error \
*.md *.go
unconvert:
$(GO) get github.com/mdempsky/unconvert
unconvert -v $(PKGS)
ineffassign:
$(GO) get github.com/gordonklaus/ineffassign
find $(SRCDIRS) -name '*.go' | xargs ineffassign
pedantic: check errcheck
unparam:
$(GO) get mvdan.cc/unparam
unparam ./...
errcheck:
$(GO) get github.com/kisielk/errcheck
errcheck $(PKGS)
gofmt:
@echo Checking code is gofmted
@test -z "$(shell gofmt -s -l -d -e $(SRCDIRS) | tee /dev/stderr)"

View File

@ -41,11 +41,18 @@ default:
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
## Roadmap
With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows:
- 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible)
- 1.0. Final release.
## Contributing
We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high.
Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports.
Before proposing a change, please discuss your change by raising an issue.
Before sending a PR, please discuss your change by raising an issue.
## License

View File

@ -82,7 +82,7 @@
//
// if err, ok := err.(stackTracer); ok {
// for _, f := range err.StackTrace() {
// fmt.Printf("%+s:%d", f)
// fmt.Printf("%+s:%d\n", f, f)
// }
// }
//
@ -159,6 +159,9 @@ type withStack struct {
func (w *withStack) Cause() error { return w.error }
// Unwrap provides compatibility for Go 1.13 error chains.
func (w *withStack) Unwrap() error { return w.error }
func (w *withStack) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
@ -241,6 +244,9 @@ type withMessage struct {
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
func (w *withMessage) Cause() error { return w.cause }
// Unwrap provides compatibility for Go 1.13 error chains.
func (w *withMessage) Unwrap() error { return w.cause }
func (w *withMessage) Format(s fmt.State, verb rune) {
switch verb {
case 'v':

38
vendor/github.com/pkg/errors/go113.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
// +build go1.13
package errors
import (
stderrors "errors"
)
// Is reports whether any error in err's chain matches target.
//
// The chain consists of err itself followed by the sequence of errors obtained by
// repeatedly calling Unwrap.
//
// An error is considered to match a target if it is equal to that target or if
// it implements a method Is(error) bool such that Is(target) returns true.
func Is(err, target error) bool { return stderrors.Is(err, target) }
// As finds the first error in err's chain that matches target, and if so, sets
// target to that error value and returns true.
//
// The chain consists of err itself followed by the sequence of errors obtained by
// repeatedly calling Unwrap.
//
// An error matches target if the error's concrete value is assignable to the value
// pointed to by target, or if the error has a method As(interface{}) bool such that
// As(target) returns true. In the latter case, the As method is responsible for
// setting target.
//
// As will panic if target is not a non-nil pointer to either a type that implements
// error, or to any interface type. As returns false if err is nil.
func As(err error, target interface{}) bool { return stderrors.As(err, target) }
// Unwrap returns the result of calling the Unwrap method on err, if err's
// type contains an Unwrap method returning error.
// Otherwise, Unwrap returns nil.
func Unwrap(err error) error {
return stderrors.Unwrap(err)
}

View File

@ -5,10 +5,13 @@ import (
"io"
"path"
"runtime"
"strconv"
"strings"
)
// Frame represents a program counter inside a stack frame.
// For historical reasons if Frame is interpreted as a uintptr
// its value represents the program counter + 1.
type Frame uintptr
// pc returns the program counter for this frame;
@ -37,6 +40,15 @@ func (f Frame) line() int {
return line
}
// name returns the name of this function, if known.
func (f Frame) name() string {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return "unknown"
}
return fn.Name()
}
// Format formats the frame according to the fmt.Formatter interface.
//
// %s source file
@ -54,22 +66,16 @@ func (f Frame) Format(s fmt.State, verb rune) {
case 's':
switch {
case s.Flag('+'):
pc := f.pc()
fn := runtime.FuncForPC(pc)
if fn == nil {
io.WriteString(s, "unknown")
} else {
file, _ := fn.FileLine(pc)
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
}
io.WriteString(s, f.name())
io.WriteString(s, "\n\t")
io.WriteString(s, f.file())
default:
io.WriteString(s, path.Base(f.file()))
}
case 'd':
fmt.Fprintf(s, "%d", f.line())
io.WriteString(s, strconv.Itoa(f.line()))
case 'n':
name := runtime.FuncForPC(f.pc()).Name()
io.WriteString(s, funcname(name))
io.WriteString(s, funcname(f.name()))
case 'v':
f.Format(s, 's')
io.WriteString(s, ":")
@ -77,6 +83,16 @@ func (f Frame) Format(s fmt.State, verb rune) {
}
}
// MarshalText formats a stacktrace Frame as a text string. The output is the
// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
func (f Frame) MarshalText() ([]byte, error) {
name := f.name()
if name == "unknown" {
return []byte(name), nil
}
return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil
}
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
type StackTrace []Frame
@ -94,18 +110,32 @@ func (st StackTrace) Format(s fmt.State, verb rune) {
switch {
case s.Flag('+'):
for _, f := range st {
fmt.Fprintf(s, "\n%+v", f)
io.WriteString(s, "\n")
f.Format(s, verb)
}
case s.Flag('#'):
fmt.Fprintf(s, "%#v", []Frame(st))
default:
fmt.Fprintf(s, "%v", []Frame(st))
st.formatSlice(s, verb)
}
case 's':
fmt.Fprintf(s, "%s", []Frame(st))
st.formatSlice(s, verb)
}
}
// formatSlice will format this StackTrace into the given buffer as a slice of
// Frame, only valid when called with '%s' or '%v'.
func (st StackTrace) formatSlice(s fmt.State, verb rune) {
io.WriteString(s, "[")
for i, f := range st {
if i > 0 {
io.WriteString(s, " ")
}
f.Format(s, verb)
}
io.WriteString(s, "]")
}
// stack represents a stack of program counters.
type stack []uintptr

View File

@ -23,10 +23,6 @@ TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
MOV a1+8(FP), A0
MOV a2+16(FP), A1
MOV a3+24(FP), A2
MOV $0, A3
MOV $0, A4
MOV $0, A5
MOV $0, A6
MOV trap+0(FP), A7 // syscall entry
ECALL
MOV A0, r1+32(FP) // r1
@ -44,9 +40,6 @@ TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
MOV a1+8(FP), A0
MOV a2+16(FP), A1
MOV a3+24(FP), A2
MOV ZERO, A3
MOV ZERO, A4
MOV ZERO, A5
MOV trap+0(FP), A7 // syscall entry
ECALL
MOV A0, r1+32(FP)

View File

@ -9,12 +9,11 @@ package unix
import "unsafe"
// fcntl64Syscall is usually SYS_FCNTL, but is overridden on 32-bit Linux
// systems by flock_linux_32bit.go to be SYS_FCNTL64.
// systems by fcntl_linux_32bit.go to be SYS_FCNTL64.
var fcntl64Syscall uintptr = SYS_FCNTL
// FcntlInt performs a fcntl syscall on fd with the provided command and argument.
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
valptr, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(arg))
func fcntl(fd int, cmd, arg int) (int, error) {
valptr, _, errno := Syscall(fcntl64Syscall, uintptr(fd), uintptr(cmd), uintptr(arg))
var err error
if errno != 0 {
err = errno
@ -22,6 +21,11 @@ func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
return int(valptr), err
}
// FcntlInt performs a fcntl syscall on fd with the provided command and argument.
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
return fcntl(int(fd), cmd, arg)
}
// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command.
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
_, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(unsafe.Pointer(lk)))

View File

@ -186,6 +186,7 @@ struct ltchars {
#include <sys/select.h>
#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/xattr.h>
#include <linux/bpf.h>
#include <linux/can.h>
@ -526,6 +527,7 @@ ccflags="$@"
$2 ~ /^WDIOC_/ ||
$2 ~ /^NFN/ ||
$2 ~ /^XDP_/ ||
$2 ~ /^RWF_/ ||
$2 ~ /^(HDIO|WIN|SMART)_/ ||
$2 ~ /^CRYPTO_/ ||
$2 ~ /^TIPC_/ ||

View File

@ -510,6 +510,23 @@ func SysctlRaw(name string, args ...int) ([]byte, error) {
return buf[:n], nil
}
func SysctlClockinfo(name string) (*Clockinfo, error) {
mib, err := sysctlmib(name)
if err != nil {
return nil, err
}
n := uintptr(SizeofClockinfo)
var ci Clockinfo
if err := sysctl(mib, (*byte)(unsafe.Pointer(&ci)), &n, nil, 0); err != nil {
return nil, err
}
if n != SizeofClockinfo {
return nil, EIO
}
return &ci, nil
}
//sys utimes(path string, timeval *[2]Timeval) (err error)
func Utimes(path string, tv []Timeval) error {
@ -577,8 +594,6 @@ func Futimes(fd int, tv []Timeval) error {
return futimes(fd, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
}
//sys fcntl(fd int, cmd int, arg int) (val int, err error)
//sys poll(fds *PollFd, nfds int, timeout int) (n int, err error)
func Poll(fds []PollFd, timeout int) (n int, err error) {

View File

@ -155,23 +155,6 @@ func getAttrList(path string, attrList attrList, attrBuf []byte, options uint) (
//sys getattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error)
func SysctlClockinfo(name string) (*Clockinfo, error) {
mib, err := sysctlmib(name)
if err != nil {
return nil, err
}
n := uintptr(SizeofClockinfo)
var ci Clockinfo
if err := sysctl(mib, (*byte)(unsafe.Pointer(&ci)), &n, nil, 0); err != nil {
return nil, err
}
if n != SizeofClockinfo {
return nil, EIO
}
return &ci, nil
}
//sysnb pipe() (r int, w int, err error)
func Pipe(p []int) (err error) {
@ -333,6 +316,8 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) error {
* Wrapped
*/
//sys fcntl(fd int, cmd int, arg int) (val int, err error)
//sys kill(pid int, signum int, posix int) (err error)
func Kill(pid int, signum syscall.Signal) (err error) { return kill(pid, int(signum), 1) }

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin,386,!go1.12
// +build darwin,arm,!go1.12
package unix

View File

@ -529,12 +529,6 @@ func PtraceGetRegs(pid int, regsout *Reg) (err error) {
return ptrace(PTRACE_GETREGS, pid, uintptr(unsafe.Pointer(regsout)), 0)
}
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint(countin)}
err = ptrace(PTRACE_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
}
func PtraceLwpEvents(pid int, enable int) (err error) {
return ptrace(PTRACE_LWPEVENTS, pid, 0, enable)
}

View File

@ -54,3 +54,9 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
}
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint32(countin)}
err = ptrace(PTRACE_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
}

View File

@ -54,3 +54,9 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
}
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint64(countin)}
err = ptrace(PTRACE_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
}

View File

@ -54,3 +54,9 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
}
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint32(countin)}
err = ptrace(PTRACE_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
}

View File

@ -54,3 +54,9 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
}
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint64(countin)}
err = ptrace(PTRACE_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
}

View File

@ -1575,7 +1575,6 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys Fchdir(fd int) (err error)
//sys Fchmod(fd int, mode uint32) (err error)
//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error)
//sys fcntl(fd int, cmd int, arg int) (val int, err error)
//sys Fdatasync(fd int) (err error)
//sys Fgetxattr(fd int, attr string, dest []byte) (sz int, err error)
//sys FinitModule(fd int, params string, flags int) (err error)
@ -1631,6 +1630,17 @@ func Getpgrp() (pid int) {
//sysnb Settimeofday(tv *Timeval) (err error)
//sys Setns(fd int, nstype int) (err error)
// PrctlRetInt performs a prctl operation specified by option and further
// optional arguments arg2 through arg5 depending on option. It returns a
// non-negative integer that is returned by the prctl syscall.
func PrctlRetInt(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (int, error) {
ret, _, err := Syscall6(SYS_PRCTL, uintptr(option), uintptr(arg2), uintptr(arg3), uintptr(arg4), uintptr(arg5), 0)
if err != 0 {
return 0, err
}
return int(ret), nil
}
// issue 1435.
// On linux Setuid and Setgid only affects the current thread, not the process.
// This does not match what most callers expect so we must return an error
@ -1644,6 +1654,30 @@ func Setgid(uid int) (err error) {
return EOPNOTSUPP
}
// SetfsgidRetGid sets fsgid for current thread and returns previous fsgid set.
// setfsgid(2) will return a non-nil error only if its caller lacks CAP_SETUID capability.
// If the call fails due to other reasons, current fsgid will be returned.
func SetfsgidRetGid(gid int) (int, error) {
return setfsgid(gid)
}
// SetfsuidRetUid sets fsuid for current thread and returns previous fsuid set.
// setfsgid(2) will return a non-nil error only if its caller lacks CAP_SETUID capability
// If the call fails due to other reasons, current fsuid will be returned.
func SetfsuidRetUid(uid int) (int, error) {
return setfsuid(uid)
}
func Setfsgid(gid int) error {
_, err := setfsgid(gid)
return err
}
func Setfsuid(uid int) error {
_, err := setfsuid(uid)
return err
}
func Signalfd(fd int, sigmask *Sigset_t, flags int) (newfd int, err error) {
return signalfd(fd, sigmask, _C__NSIG/8, flags)
}
@ -1666,6 +1700,123 @@ func Signalfd(fd int, sigmask *Sigset_t, flags int) (newfd int, err error) {
//sys exitThread(code int) (err error) = SYS_EXIT
//sys readlen(fd int, p *byte, np int) (n int, err error) = SYS_READ
//sys writelen(fd int, p *byte, np int) (n int, err error) = SYS_WRITE
//sys readv(fd int, iovs []Iovec) (n int, err error) = SYS_READV
//sys writev(fd int, iovs []Iovec) (n int, err error) = SYS_WRITEV
//sys preadv(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr) (n int, err error) = SYS_PREADV
//sys pwritev(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr) (n int, err error) = SYS_PWRITEV
//sys preadv2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PREADV2
//sys pwritev2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PWRITEV2
func bytes2iovec(bs [][]byte) []Iovec {
iovecs := make([]Iovec, len(bs))
for i, b := range bs {
iovecs[i].SetLen(len(b))
if len(b) > 0 {
iovecs[i].Base = &b[0]
} else {
iovecs[i].Base = (*byte)(unsafe.Pointer(&_zero))
}
}
return iovecs
}
// offs2lohi splits offs into its lower and upper unsigned long. On 64-bit
// systems, hi will always be 0. On 32-bit systems, offs will be split in half.
// preadv/pwritev chose this calling convention so they don't need to add a
// padding-register for alignment on ARM.
func offs2lohi(offs int64) (lo, hi uintptr) {
return uintptr(offs), uintptr(uint64(offs) >> SizeofLong)
}
func Readv(fd int, iovs [][]byte) (n int, err error) {
iovecs := bytes2iovec(iovs)
n, err = readv(fd, iovecs)
readvRacedetect(iovecs, n, err)
return n, err
}
func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) {
iovecs := bytes2iovec(iovs)
lo, hi := offs2lohi(offset)
n, err = preadv(fd, iovecs, lo, hi)
readvRacedetect(iovecs, n, err)
return n, err
}
func Preadv2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) {
iovecs := bytes2iovec(iovs)
lo, hi := offs2lohi(offset)
n, err = preadv2(fd, iovecs, lo, hi, flags)
readvRacedetect(iovecs, n, err)
return n, err
}
func readvRacedetect(iovecs []Iovec, n int, err error) {
if !raceenabled {
return
}
for i := 0; n > 0 && i < len(iovecs); i++ {
m := int(iovecs[i].Len)
if m > n {
m = n
}
n -= m
if m > 0 {
raceWriteRange(unsafe.Pointer(iovecs[i].Base), m)
}
}
if err == nil {
raceAcquire(unsafe.Pointer(&ioSync))
}
}
func Writev(fd int, iovs [][]byte) (n int, err error) {
iovecs := bytes2iovec(iovs)
if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync))
}
n, err = writev(fd, iovecs)
writevRacedetect(iovecs, n)
return n, err
}
func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
iovecs := bytes2iovec(iovs)
if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync))
}
lo, hi := offs2lohi(offset)
n, err = pwritev(fd, iovecs, lo, hi)
writevRacedetect(iovecs, n)
return n, err
}
func Pwritev2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) {
iovecs := bytes2iovec(iovs)
if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync))
}
lo, hi := offs2lohi(offset)
n, err = pwritev2(fd, iovecs, lo, hi, flags)
writevRacedetect(iovecs, n)
return n, err
}
func writevRacedetect(iovecs []Iovec, n int) {
if !raceenabled {
return
}
for i := 0; n > 0 && i < len(iovecs); i++ {
m := int(iovecs[i].Len)
if m > n {
m = n
}
n -= m
if m > 0 {
raceReadRange(unsafe.Pointer(iovecs[i].Base), m)
}
}
}
// mmap varies by architecture; see syscall_linux_*.go.
//sys munmap(addr uintptr, length uintptr) (err error)

View File

@ -70,8 +70,8 @@ func Pipe2(p []int, flags int) (err error) {
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
//sys Setfsgid(gid int) (err error) = SYS_SETFSGID32
//sys Setfsuid(uid int) (err error) = SYS_SETFSUID32
//sys setfsgid(gid int) (prev int, err error) = SYS_SETFSGID32
//sys setfsuid(uid int) (prev int, err error) = SYS_SETFSUID32
//sysnb Setregid(rgid int, egid int) (err error) = SYS_SETREGID32
//sysnb Setresgid(rgid int, egid int, sgid int) (err error) = SYS_SETRESGID32
//sysnb Setresuid(ruid int, euid int, suid int) (err error) = SYS_SETRESUID32

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