Skip to content

Commit

Permalink
Merge pull request operator-framework#556 from ecordell/registry-pods
Browse files Browse the repository at this point in the history
Create registry pods for ConfigMap CatalogSources
  • Loading branch information
openshift-merge-robot authored Dec 18, 2018
2 parents 410a01b + c0c351f commit 9bc496a
Show file tree
Hide file tree
Showing 55 changed files with 14,938 additions and 10,723 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -459,4 +459,6 @@ __pycache__/
*.bak
**/resources/*
/build
/apiserver.local.config
/apiserver.local.config
e2e.namespace
minikube.kubeconfig
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,11 @@ run-local-shift:
. ./scripts/install_local.sh local build/resources
rm -rf build

e2e.namespace:
@printf "e2e-tests-$(shell date +%s)-$$RANDOM" > e2e.namespace

# useful if running e2e directly with `go test -tags=bare`
setup-bare:
setup-bare: clean e2e.namespace
. ./scripts/build_bare.sh
. ./scripts/package-release.sh 1.0.0 test/e2e/resources test/e2e/e2e-bare-values.yaml
. ./scripts/install_bare.sh $(shell cat ./e2e.namespace) test/e2e/resources
Expand Down Expand Up @@ -111,6 +114,7 @@ clean:
@rm -rf test/e2e/resources
@rm -rf test/e2e/test-resources
@rm -rf test/e2e/log
@rm -rf e2e.namespace

CI := $(shell find . -iname "*.jsonnet") $(shell find . -iname "*.libsonnet")
$(CI):
Expand Down
10 changes: 7 additions & 3 deletions cmd/catalog/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import (
)

const (
defaultWakeupInterval = 15 * time.Minute
defaultCatalogNamespace = "tectonic-system"
defaultWakeupInterval = 15 * time.Minute
defaultCatalogNamespace = "openshift-operator-lifecycle-manager"
defaultConfigMapServerImage = "quay.io/operatorframework/configmap-operator-registry:latest"
)

// config flags defined globally so that they appear on the test binary as well
Expand All @@ -35,6 +36,9 @@ var (
catalogNamespace = flag.String(
"namespace", defaultCatalogNamespace, "namespace where catalog will run and install catalog resources")

configmapServerImage = flag.String(
"configmapServerImage", defaultConfigMapServerImage, "the image to use for serving the operator registry api for a configmap")

debug = flag.Bool(
"debug", false, "use debug log level")

Expand Down Expand Up @@ -78,7 +82,7 @@ func main() {
logger.Infof("log level %s", logger.Level)

// Create a new instance of the operator.
catalogOperator, err := catalog.NewOperator(*kubeConfigPath, logger, *wakeupInterval, *catalogNamespace, namespaces...)
catalogOperator, err := catalog.NewOperator(*kubeConfigPath, logger, *wakeupInterval, *configmapServerImage, *catalogNamespace, namespaces...)
if err != nil {
log.Panicf("error configuring operator: %s", err.Error())
}
Expand Down
50 changes: 44 additions & 6 deletions deploy/chart/templates/0000_30_05-catalogsource.crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,17 @@ spec:
openAPIV3Schema:
properties:
spec:
type: object
description: Represents a subscription to a source and channel
required:
- spec
type: object
description: Spec for a catalog source.
required:
- sourceType
properties:
sourceType:
type: string
description: The type of the source. Currently the only supported type is "internal".
description: The type of the source. `configmap` is the new name for `internal`
enum:
- internal
- internal # deprecated
- configmap

configMap:
type: string
Expand All @@ -77,3 +74,44 @@ spec:
items:
type: string
description: A name of a secret in the namespace where the CatalogSource is defined.
status:
type: object
description: The status of the CatalogSource
properties:
configMapReference:
type: object
description: If sourceType is `internal` or `configmap`, then this holds a reference to the configmap associated with this CatalogSource.
properties:
name:
type: string
description: name of the configmap
namespace:
type: string
description: namespace of the configmap
resourceVersion:
type: string
description: resourceVersion of the configmap
uid:
type: string
description: uid of the configmap
registryService:
type: object
properties:
protocol:
type: string
description: protocol of the registry service
enum:
- grpc
serviceName:
type: string
description: name of the registry service
serviceNamespace:
type: string
description: namespace of the registry service
port:
type: string
description: port of the registry service
lastSync:
type: string
description: the last time the catalog was updated. If this time is less than the last updated time on the object, the catalog will be re-cached.

23 changes: 3 additions & 20 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,18 @@ require (
github.com/go-openapi/spec v0.17.2
github.com/go-openapi/strfmt v0.17.2 // indirect
github.com/go-openapi/validate v0.17.2 // indirect
github.com/gogo/protobuf v1.1.1 // indirect
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff // indirect
github.com/golang/mock v1.1.1
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c // indirect
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
github.com/googleapis/gnostic v0.2.0 // indirect
github.com/gorilla/websocket v1.4.0 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.5.1 // indirect
github.com/hashicorp/golang-lru v0.5.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jonboulle/clockwork v0.1.0 // indirect
github.com/json-iterator/go v1.1.5 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/maxbrunsfeld/counterfeiter v0.0.0-20181017030959-1aadac120687
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/operator-framework/operator-registry v1.0.1
github.com/pkg/errors v0.8.0
github.com/prometheus/client_golang v0.8.0
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect
Expand All @@ -51,21 +41,14 @@ require (
github.com/sirupsen/logrus v1.1.1
github.com/soheilhy/cmux v0.1.4 // indirect
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3 // indirect
github.com/stevvooe/resumable v0.0.0-20180830230917-22b14a53ba50 // indirect
github.com/stretchr/testify v1.2.2
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
github.com/ugorji/go v1.1.1 // indirect
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e // indirect
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 // indirect
golang.org/x/oauth2 v0.0.0-20181105165119-ca4130e427c7 // indirect
golang.org/x/sys v0.0.0-20181023152157-44b849a8bc13 // indirect
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect
golang.org/x/tools v0.0.0-20181207222222-4c874b978acb // indirect
google.golang.org/genproto v0.0.0-20181016170114-94acd270e44e // indirect
google.golang.org/grpc v1.16.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
google.golang.org/grpc v1.16.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3 // indirect
k8s.io/api v0.0.0-20181203235848-2dd39edadc55
k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc
Expand All @@ -77,5 +60,5 @@ require (
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92 // indirect
k8s.io/kube-aggregator v0.0.0-20181204002017-122bac39d429
k8s.io/kube-openapi v0.0.0-20181031203759-72693cb1fadd
k8s.io/kubernetes v1.11.6-beta.0.0.20181207014600-4600add36de5
k8s.io/kubernetes v1.11.6-beta.0.0.20181212102158-c4240ecfddd7
)
17 changes: 15 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20180924190550-6f2cf27854a4/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff h1:kOkM9whyQYodu09SJ6W3NCsHG7crFaJILQ22Gozp3lg=
github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
Expand Down Expand Up @@ -114,6 +115,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGi
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/maxbrunsfeld/counterfeiter v0.0.0-20181017030959-1aadac120687 h1:fJasMUaV/LYZvzK4bUOj13rNXc4fhVzU0Vu1OlcGUd4=
Expand All @@ -122,14 +124,19 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.2-0.20180831124310-ae19f1b56d53 h1:W43ZAQzmBARaVM1WrnDDKjtfIkF6OyeElrMdKDQIYhY=
github.com/onsi/gomega v1.4.2-0.20180831124310-ae19f1b56d53/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/operator-framework/operator-lifecycle-manager v0.0.0-20181023032605-e838f7fb2186/go.mod h1:Ma5ZXd4S1vmMyewWlF7aO8CZiokR7Sd8dhSfkGkNU4U=
github.com/operator-framework/operator-registry v1.0.1 h1:Z2155w77HzIkTrdp2qoY0QMkywxhJpuABUSGcgogXuc=
github.com/operator-framework/operator-registry v1.0.1/go.mod h1:1xEdZjjUg2hPEd52LG3YQ0jtwiwEGdm98S1TH5P4RAA=
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/petar/GoLLRB v0.0.0-20130427215148-53be0d36a84c/go.mod h1:HUpKUBZnpzkdx0kD/+Yfuft+uD3zHGtXF/XJB14TUr4=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
Expand Down Expand Up @@ -212,14 +219,20 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.0.0-20180904230853-4e7be11eab3f h1:DLRkv8Ps4Sdx8Srj+UtGisj4whV7v/HezlHx6QqiZqE=
k8s.io/api v0.0.0-20180904230853-4e7be11eab3f/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/api v0.0.0-20181203235848-2dd39edadc55 h1:FmAMYGd999iHkN+swot+oART9AumJiAvH0idpIZ3Ozo=
k8s.io/api v0.0.0-20181203235848-2dd39edadc55/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/apiextensions-apiserver v0.0.0-20180905004947-16750353bf97 h1:s4lWWs6JN5kWVzk5bztddkr5kgO/cGIbqTDP+QttUeQ=
k8s.io/apiextensions-apiserver v0.0.0-20180905004947-16750353bf97/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc h1:IOukeE9HtTwpLslbujLDfRpfFU6tsjq28yO0fjnl/hk=
k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
k8s.io/apimachinery v0.0.0-20180904193909-def12e63c512/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/apimachinery v0.0.0-20181203235515-3d8ee2261517 h1:p6GEgV1/cc7H0AT6XfjHwHNIypirOprIB09oKp2DQ/M=
k8s.io/apimachinery v0.0.0-20181203235515-3d8ee2261517/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/apiserver v0.0.0-20181026151315-13cfe3978170 h1:CqI85nZvPaV+7JFono0nAOGOx2brocqefcOhDPVhHKI=
k8s.io/apiserver v0.0.0-20181026151315-13cfe3978170/go.mod h1:6bqaTSOSJavUIXUtfaR9Os9JtTCm8ZqH2SUl2S60C4w=
k8s.io/client-go v0.0.0-20180718001006-59698c7d9724/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/client-go v8.0.0+incompatible h1:2pUaSg2x6iEHr8cia6zmWhoCXG1EDG9TCx9s//Aq7HY=
k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/code-generator v0.0.0-20181203235156-f8cba74510f3 h1:f/Aa24HPnPEDWia884BCF94E1b29KYjOTVTHcBzvT2Q=
Expand All @@ -232,5 +245,5 @@ k8s.io/kube-aggregator v0.0.0-20181204002017-122bac39d429 h1:wIDPKpRuwEfyt+ImBaP
k8s.io/kube-aggregator v0.0.0-20181204002017-122bac39d429/go.mod h1:8sbzT4QQKDEmSCIbfqjV0sd97GpUT7A4W626sBiYJmU=
k8s.io/kube-openapi v0.0.0-20181031203759-72693cb1fadd h1:ggv/Vfza0i5xuhUZyYyxcc25AmQvHY8Zi1C2m8WgBvA=
k8s.io/kube-openapi v0.0.0-20181031203759-72693cb1fadd/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
k8s.io/kubernetes v1.11.6-beta.0.0.20181207014600-4600add36de5 h1:+oToqFCSumcTuKDfnGBfGDTufqPJK3JmI0+ItcYB2tg=
k8s.io/kubernetes v1.11.6-beta.0.0.20181207014600-4600add36de5/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/kubernetes v1.11.6-beta.0.0.20181212102158-c4240ecfddd7 h1:o48C0Emh8uKTGznb+GRliG2C7PnKwB+yWdddTG7CNbw=
k8s.io/kubernetes v1.11.6-beta.0.0.20181212102158-c4240ecfddd7/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
36 changes: 30 additions & 6 deletions pkg/api/apis/operators/v1alpha1/catalogsource_types.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
package v1alpha1

import (
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators"
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"

"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators"
)

const (
CatalogSourceCRDAPIVersion = operators.GroupName + "/" + GroupVersion
CatalogSourceKind = "CatalogSource"
)

// SourceType indicates the type of backing store for a CatalogSource
type SourceType string

const (
SourceTypeInternal SourceType = "internal"
SourceTypeConfigmap SourceType = "configmap"
)

type CatalogSourceSpec struct {
SourceType string `json:"sourceType"`
ConfigMap string `json:"configMap,omitempty"`
Secrets []string `json:"secrets,omitempty"`
SourceType SourceType `json:"sourceType"`
ConfigMap string `json:"configMap,omitempty"`
Secrets []string `json:"secrets,omitempty"`

// Metadata
DisplayName string `json:"displayName,omitempty"`
Expand All @@ -23,9 +34,22 @@ type CatalogSourceSpec struct {
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.cluster.local:%s", s.ServiceName, s.ServiceNamespace, s.Port)
}

type CatalogSourceStatus struct {
ConfigMapResource *ConfigMapResourceReference `json:"configMapReference,omitempty"`
LastSync metav1.Time `json:"lastSync,omitempty"`
ConfigMapResource *ConfigMapResourceReference `json:"configMapReference,omitempty"`
RegistryServiceStatus *RegistryServiceStatus `json:"registryService,omitempty"`
LastSync metav1.Time `json:"lastSync,omitempty"`
}
type ConfigMapResourceReference struct {
Name string `json:"name"`
Expand Down
22 changes: 22 additions & 0 deletions pkg/api/apis/operators/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 9bc496a

Please sign in to comment.