Skip to content

Commit e20bcc1

Browse files
Merge pull request #1223 from spadgett/rebase-master-next
Merge master into master-next
2 parents 41171ca + f5ece42 commit e20bcc1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1110
-551
lines changed

auth/auth.go

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ type Config struct {
9393
ClientSecret string
9494
Scope []string
9595

96-
// DiscoveryCA is required for OpenShift OAuth metadata discovery. This is the CA
96+
// K8sCA is required for OpenShift OAuth metadata discovery. This is the CA
9797
// used to talk to the master, which might be different than the issuer CA.
98-
DiscoveryCA string
98+
K8sCA string
9999

100100
SuccessURL string
101101
ErrorURL string
@@ -140,33 +140,37 @@ func newHTTPClient(issuerCA string, includeSystemRoots bool) (*http.Client, erro
140140
// NewAuthenticator initializes an Authenticator struct. It blocks until the authenticator is
141141
// able to contact the provider.
142142
func NewAuthenticator(ctx context.Context, c *Config) (*Authenticator, error) {
143-
a, err := newUnstartedAuthenticator(c)
144-
if err != nil {
145-
return nil, err
146-
}
147-
148143
// Retry connecting to the identity provider a few times
149144
backoff := time.Second * 2
150-
maxSteps := 5
145+
maxSteps := 7
151146
steps := 0
152147

153148
for {
154149
var (
150+
a *Authenticator
155151
lm loginMethod
156152
endpoint oauth2.Endpoint
157153
err error
158154
)
155+
156+
a, err = newUnstartedAuthenticator(c)
157+
if err != nil {
158+
return nil, err
159+
}
160+
159161
switch c.AuthSource {
160162
case AuthSourceOpenShift:
161163
// Use the k8s CA for OAuth metadata discovery.
162-
var client *http.Client
163-
client, err = newHTTPClient(c.DiscoveryCA, false)
164+
var k8sClient *http.Client
165+
// Don't include system roots when talking to the API server.
166+
k8sClient, err = newHTTPClient(c.K8sCA, false)
164167
if err != nil {
165168
return nil, err
166169
}
167170

168171
endpoint, lm, err = newOpenShiftAuth(ctx, &openShiftConfig{
169-
client: client,
172+
k8sClient: k8sClient,
173+
oauthClient: a.client,
170174
issuerURL: c.IssuerURL,
171175
cookiePath: c.CookiePath,
172176
secureCookies: c.SecureCookies,
@@ -183,11 +187,11 @@ func NewAuthenticator(ctx context.Context, c *Config) (*Authenticator, error) {
183187
if err != nil {
184188
steps++
185189
if steps > maxSteps {
186-
log.Errorf("error contacting openid connect provider: %v", err)
190+
log.Errorf("error contacting auth provider: %v", err)
187191
return nil, err
188192
}
189193

190-
log.Errorf("error contacting openid connect provider (retrying in %s): %v", backoff, err)
194+
log.Errorf("error contacting auth provider (retrying in %s): %v", backoff, err)
191195

192196
time.Sleep(backoff)
193197
backoff *= 2

auth/auth_openshift.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ type openShiftAuth struct {
2323
}
2424

2525
type openShiftConfig struct {
26-
client *http.Client
26+
k8sClient *http.Client
27+
oauthClient *http.Client
2728
issuerURL string
2829
cookiePath string
2930
secureCookies bool
@@ -52,7 +53,7 @@ func newOpenShiftAuth(ctx context.Context, c *openShiftConfig) (oauth2.Endpoint,
5253
return oauth2.Endpoint{}, nil, err
5354
}
5455

55-
resp, err := c.client.Do(req.WithContext(ctx))
56+
resp, err := c.k8sClient.Do(req.WithContext(ctx))
5657
if err != nil {
5758
return oauth2.Endpoint{}, nil, err
5859
}
@@ -86,6 +87,19 @@ func newOpenShiftAuth(ctx context.Context, c *openShiftConfig) (oauth2.Endpoint,
8687
return oauth2.Endpoint{}, nil, err
8788
}
8889

90+
// Make sure we can talk to the issuer endpoint.
91+
req, err = http.NewRequest(http.MethodHead, metadata.Issuer, nil)
92+
if err != nil {
93+
return oauth2.Endpoint{}, nil, err
94+
}
95+
96+
resp, err = c.oauthClient.Do(req.WithContext(ctx))
97+
if err != nil {
98+
return oauth2.Endpoint{}, nil, fmt.Errorf("request to OAuth issuer endpoint %s failed: %v",
99+
metadata.Token, err)
100+
}
101+
defer resp.Body.Close()
102+
89103
kubeAdminLogoutURL := proxy.SingleJoiningSlash(metadata.Issuer, "/logout")
90104
return oauth2.Endpoint{
91105
AuthURL: metadata.Auth,

cmd/bridge/main.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,6 @@ func main() {
143143
if branding == "origin" {
144144
branding = "okd"
145145
}
146-
// Temporarily default okd to openshift
147-
if branding == "okd" {
148-
branding = "openshift"
149-
}
150146
switch branding {
151147
case "okd":
152148
case "openshift":
@@ -366,7 +362,7 @@ func main() {
366362

367363
// Use the k8s CA file for OpenShift OAuth metadata discovery.
368364
// This might be different than IssuerCA.
369-
DiscoveryCA: caCertFilePath,
365+
K8sCA: caCertFilePath,
370366

371367
ErrorURL: authLoginErrorEndpoint,
372368
SuccessURL: authLoginSuccessEndpoint,
@@ -394,7 +390,7 @@ func main() {
394390
}
395391

396392
if srv.Auther, err = auth.NewAuthenticator(context.Background(), oidcClientConfig); err != nil {
397-
log.Fatalf("Error initializing OIDC authenticator: %v", err)
393+
log.Fatalf("Error initializing authenticator: %v", err)
398394
}
399395
case "disabled":
400396
log.Warningf("running with AUTHENTICATION DISABLED!")

frontend/__mocks__/k8sResourcesMocks.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export const testClusterServiceVersion: ClusterServiceVersionKind = {
5858
'alm-owner-testapp': 'testapp.clusterserviceversions.operators.coreos.com.v1alpha1',
5959
},
6060
},
61+
installModes: [],
6162
install: {
6263
strategy: 'Deployment',
6364
spec: {
@@ -129,6 +130,7 @@ export const localClusterServiceVersion: ClusterServiceVersionKind = {
129130
'alm-owner-local-testapp': 'local-testapp.clusterserviceversions.operators.coreos.com.v1alpha1',
130131
},
131132
},
133+
installModes: [],
132134
install: {
133135
strategy: 'Deployment',
134136
spec: {
@@ -268,6 +270,7 @@ export const testPackageManifest: PackageManifestKind = {
268270
provider: {
269271
name: 'CoreOS, Inc',
270272
},
273+
installModes: [],
271274
},
272275
}],
273276
defaultChannel: 'alpha',

frontend/__mocks__/operatorHubItemsMocks.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const amqPackageManifest = {
4040
provider: {
4141
name: 'Red Hat',
4242
},
43+
installModes: [],
4344
annotations: {
4445
'alm-examples': '[{"apiVersion":"kafka.strimzi.io/v1alpha1","kind":"Kafka","metadata":{"name":"my-cluster"},"spec":{"kafka":{"replicas":3,"listeners":{"plain":{},"tls":{}},"config":{"offsets.topic.replication.factor":3,"transaction.state.log.replication.factor":3,"transaction.state.log.min.isr":2},"storage":{"type":"ephemeral"}},"zookeeper":{"replicas":3,"storage":{"type":"ephemeral"}},"entityOperator":{"topicOperator":{},"userOperator":{}}}}, {"apiVersion":"kafka.strimzi.io/v1alpha1","kind":"KafkaConnect","metadata":{"name":"my-connect-cluster"},"spec":{"replicas":1,"bootstrapServers":"my-cluster-kafka-bootstrap:9093","tls":{"trustedCertificates":[{"secretName":"my-cluster-cluster-ca-cert","certificate":"ca.crt"}]}}}, {"apiVersion":"kafka.strimzi.io/v1alpha1","kind":"KafkaConnectS2I","metadata":{"name":"my-connect-cluster"},"spec":{"replicas":1,"bootstrapServers":"my-cluster-kafka-bootstrap:9093","tls":{"trustedCertificates":[{"secretName":"my-cluster-cluster-ca-cert","certificate":"ca.crt"}]}}}, {"apiVersion":"kafka.strimzi.io/v1alpha1","kind":"KafkaTopic","metadata":{"name":"my-topic","labels":{"strimzi.io/cluster":"my-cluster"}},"spec":{"partitions":10,"replicas":3,"config":{"retention.ms":604800000,"segment.bytes":1073741824}}}, {"apiVersion":"kafka.strimzi.io/v1alpha1","kind":"KafkaUser","metadata":{"name":"my-user","labels":{"strimzi.io/cluster":"my-cluster"}},"spec":{"authentication":{"type":"tls"},"authorization":{"type":"simple","acls":[{"resource":{"type":"topic","name":"my-topic","patternType":"literal"},"operation":"Read","host":"*"},{"resource":{"type":"topic","name":"my-topic","patternType":"literal"},"operation":"Describe","host":"*"},{"resource":{"type":"group","name":"my-group","patternType":"literal"},"operation":"Read","host":"*"},{"resource":{"type":"topic","name":"my-topic","patternType":"literal"},"operation":"Write","host":"*"},{"resource":{"type":"topic","name":"my-topic","patternType":"literal"},"operation":"Create","host":"*"},{"resource":{"type":"topic","name":"my-topic","patternType":"literal"},"operation":"Describe","host":"*"}]}}}]',
4546
description: '**Red Hat AMQ Streams** is a massively scalable, distributed, and high performance data streaming platform based on the Apache Kafka project. \nAMQ Streams provides an event streaming backbone that allows microservices and other application components to exchange data with extremely high throughput and low latency.\n\n**The core capabilities include**\n* A pub/sub messaging model, similar to a traditional enterprise messaging system, in which application components publish and consume events to/from an ordered stream\n* The long term, fault-tolerant storage of events\n* The ability for a consumer to replay streams of events\n* The ability to partition topics for horizontal scalability\n\n# Before you start\n\n1. Create AMQ Streams Cluster Roles\n```\n$ oc apply -f http://amq.io/amqstreams/rbac.yaml\n```\n2. Create following bindings\n```\n$ oc adm policy add-cluster-role-to-user strimzi-cluster-operator -z strimzi-cluster-operator --namespace <namespace>\n$ oc adm policy add-cluster-role-to-user strimzi-kafka-broker -z strimzi-cluster-operator --namespace <namespace>\n```',
@@ -89,6 +90,7 @@ const etcdPackageManifest = {
8990
provider: {
9091
name: 'CoreOS, Inc',
9192
},
93+
installModes: [],
9294
annotations: {
9395
'alm-examples': '[{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdCluster","metadata":{"name":"example","namespace":"default"},"spec":{"size":3,"version":"3.2.13"}},{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdRestore","metadata":{"name":"example-etcd-cluster"},"spec":{"etcdCluster":{"name":"example-etcd-cluster"},"backupStorageType":"S3","s3":{"path":"<full-s3-path>","awsSecret":"<aws-secret>"}}},{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdBackup","metadata":{"name":"example-etcd-cluster-backup"},"spec":{"etcdEndpoints":["<etcd-cluster-endpoints>"],"storageType":"S3","s3":{"path":"<full-s3-path>","awsSecret":"<aws-secret>"}}}]',
9496
'tectonic-visibility': 'ocs',
@@ -136,6 +138,7 @@ const federationv2PackageManifest = {
136138
provider: {
137139
name: 'Red Hat',
138140
},
141+
installModes: [],
139142
annotations: {
140143
description: 'Kubernetes Federation V2 namespace-scoped installation',
141144
categories: '',
@@ -184,6 +187,7 @@ const prometheusPackageManifest = {
184187
provider: {
185188
name: 'Red Hat',
186189
},
190+
installModes: [],
187191
annotations: {
188192
'alm-examples': '[{"apiVersion":"monitoring.coreos.com/v1","kind":"Prometheus","metadata":{"name":"example","labels":{"prometheus":"k8s"}},"spec":{"replicas":2,"version":"v2.3.2","serviceAccountName":"prometheus-k8s","securityContext": {}, "serviceMonitorSelector":{"matchExpressions":[{"key":"k8s-app","operator":"Exists"}]},"ruleSelector":{"matchLabels":{"role":"prometheus-rulefiles","prometheus":"k8s"}},"alerting":{"alertmanagers":[{"namespace":"monitoring","name":"alertmanager-main","port":"web"}]}}},{"apiVersion":"monitoring.coreos.com/v1","kind":"ServiceMonitor","metadata":{"name":"example","labels":{"k8s-app":"prometheus"}},"spec":{"selector":{"matchLabels":{"k8s-app":"prometheus"}},"endpoints":[{"port":"web","interval":"30s"}]}},{"apiVersion":"monitoring.coreos.com/v1","kind":"Alertmanager","metadata":{"name":"alertmanager-main"},"spec":{"replicas":3, "securityContext": {}}}]',
189193
description: 'The Prometheus Operator for Kubernetes provides easy monitoring definitions for Kubernetes services and deployment and management of Prometheus instances.',
@@ -230,6 +234,7 @@ const svcatPackageManifest = {
230234
provider: {
231235
name: 'Red Hat',
232236
},
237+
installModes: [],
233238
annotations: {
234239
description: 'Service Catalog lets you provision cloud services directly from the comfort of native Kubernetes tooling.',
235240
categories: 'catalog',
@@ -276,6 +281,7 @@ const dummyPackageManifest = {
276281
provider: {
277282
name: 'Dummy',
278283
},
284+
installModes: [],
279285
annotations: {
280286
description: 'Dummy is not a real operator',
281287
categories: 'dummy',

frontend/__tests__/components/modals/subscription-channel-modal.spec.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ describe(SubscriptionChannelModal.name, () => {
3737
provider: {
3838
name: 'CoreOS, Inc',
3939
},
40+
installModes: [],
4041
},
4142
}, {
4243
name: 'nightly',
@@ -48,6 +49,7 @@ describe(SubscriptionChannelModal.name, () => {
4849
provider: {
4950
name: 'CoreOS, Inc',
5051
},
52+
installModes: [],
5153
},
5254
}];
5355

frontend/__tests__/components/operator-lifecycle-manager/operator-group.spec.tsx

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
/* eslint-disable no-undef, no-unused-vars */
22

33
import * as React from 'react';
4+
import * as _ from 'lodash-es';
45
import { shallow } from 'enzyme';
56

6-
import { requireOperatorGroup, NoOperatorGroupMsg } from '../../../public/components/operator-lifecycle-manager/operator-group';
7-
import { testOperatorGroup } from '../../../__mocks__/k8sResourcesMocks';
7+
import { requireOperatorGroup, NoOperatorGroupMsg, supports, InstallModeSet, InstallModeType, installedFor } from '../../../public/components/operator-lifecycle-manager/operator-group';
8+
import { OperatorGroupKind, SubscriptionKind } from '../../../public/components/operator-lifecycle-manager';
9+
import { testOperatorGroup, testSubscription } from '../../../__mocks__/k8sResourcesMocks';
810

911
describe('requireOperatorGroup', () => {
1012
const SomeComponent = () => <div>Requires OperatorGroup</div>;
@@ -33,3 +35,111 @@ describe('requireOperatorGroup', () => {
3335
expect(wrapper.find(NoOperatorGroupMsg).exists()).toBe(false);
3436
});
3537
});
38+
39+
describe('installedFor', () => {
40+
const pkgName = testSubscription.spec.name;
41+
const ns = testSubscription.metadata.namespace;
42+
let subscriptions: SubscriptionKind[];
43+
let operatorGroups: OperatorGroupKind[];
44+
45+
beforeEach(() => {
46+
subscriptions = [];
47+
operatorGroups = [];
48+
});
49+
50+
it('returns false if no `Subscriptions` exist for the given package', () => {
51+
subscriptions = [testSubscription];
52+
operatorGroups = [{...testOperatorGroup, status: {namespaces: [ns], lastUpdated: null}}];
53+
54+
expect(installedFor(subscriptions)(operatorGroups)('new-operator')(ns)).toBe(false);
55+
});
56+
57+
it('returns false if no `OperatorGroups` target the given namespace', () => {
58+
subscriptions = [testSubscription];
59+
operatorGroups = [{...testOperatorGroup, status: {namespaces: ['prod-a', 'prod-b'], lastUpdated: null}}];
60+
61+
expect(installedFor(subscriptions)(operatorGroups)(pkgName)(ns)).toBe(false);
62+
});
63+
64+
it('returns false if checking for `all-namespaces`', () => {
65+
subscriptions = [testSubscription];
66+
operatorGroups = [{...testOperatorGroup, status: {namespaces: [ns], lastUpdated: null}}];
67+
68+
expect(installedFor(subscriptions)(operatorGroups)(pkgName)('')).toBe(false);
69+
});
70+
71+
it('returns true if `Subscription` exists in the "global" `OperatorGroup`', () => {
72+
subscriptions = [testSubscription];
73+
operatorGroups = [{...testOperatorGroup, status: {namespaces: [''], lastUpdated: null}}];
74+
75+
expect(installedFor(subscriptions)(operatorGroups)(pkgName)(ns)).toBe(true);
76+
});
77+
78+
it('returns true if `Subscription` exists in an `OperatorGroup` that targets given namespace', () => {
79+
subscriptions = [testSubscription];
80+
operatorGroups = [{...testOperatorGroup, status: {namespaces: [ns], lastUpdated: null}}];
81+
82+
expect(installedFor(subscriptions)(operatorGroups)(pkgName)(ns)).toBe(true);
83+
});
84+
});
85+
86+
describe('supports', () => {
87+
let set: InstallModeSet;
88+
let ownNamespaceGroup: OperatorGroupKind;
89+
let singleNamespaceGroup: OperatorGroupKind;
90+
let multiNamespaceGroup: OperatorGroupKind;
91+
let allNamespacesGroup: OperatorGroupKind;
92+
93+
beforeEach(() => {
94+
ownNamespaceGroup = _.cloneDeep(testOperatorGroup);
95+
ownNamespaceGroup.status = {namespaces: [ownNamespaceGroup.metadata.namespace], lastUpdated: null};
96+
singleNamespaceGroup = _.cloneDeep(testOperatorGroup);
97+
singleNamespaceGroup.status = {namespaces: ['test-ns'], lastUpdated: null};
98+
multiNamespaceGroup = _.cloneDeep(testOperatorGroup);
99+
multiNamespaceGroup.status = {namespaces: ['test-ns', 'default'], lastUpdated: null};
100+
allNamespacesGroup = _.cloneDeep(testOperatorGroup);
101+
allNamespacesGroup.status = {namespaces: [''], lastUpdated: null};
102+
});
103+
104+
it('correctly returns for an Operator that can only run in its own namespace', () => {
105+
set = [
106+
{type: InstallModeType.InstallModeTypeOwnNamespace, supported: true},
107+
{type: InstallModeType.InstallModeTypeSingleNamespace, supported: true},
108+
{type: InstallModeType.InstallModeTypeMultiNamespace, supported: false},
109+
{type: InstallModeType.InstallModeTypeAllNamespaces, supported: false},
110+
];
111+
112+
expect(supports(set)(ownNamespaceGroup)).toBe(true);
113+
expect(supports(set)(singleNamespaceGroup)).toBe(true);
114+
expect(supports(set)(multiNamespaceGroup)).toBe(false);
115+
expect(supports(set)(allNamespacesGroup)).toBe(false);
116+
});
117+
118+
it('correctly returns for an Operator which can run in several namespaces', () => {
119+
set = [
120+
{type: InstallModeType.InstallModeTypeOwnNamespace, supported: true},
121+
{type: InstallModeType.InstallModeTypeSingleNamespace, supported: true},
122+
{type: InstallModeType.InstallModeTypeMultiNamespace, supported: true},
123+
{type: InstallModeType.InstallModeTypeAllNamespaces, supported: false},
124+
];
125+
126+
expect(supports(set)(ownNamespaceGroup)).toBe(true);
127+
expect(supports(set)(singleNamespaceGroup)).toBe(true);
128+
expect(supports(set)(multiNamespaceGroup)).toBe(true);
129+
expect(supports(set)(allNamespacesGroup)).toBe(false);
130+
});
131+
132+
it('correctly returns for an Operator which can only run in all namespaces', () => {
133+
set = [
134+
{type: InstallModeType.InstallModeTypeOwnNamespace, supported: true},
135+
{type: InstallModeType.InstallModeTypeSingleNamespace, supported: false},
136+
{type: InstallModeType.InstallModeTypeMultiNamespace, supported: false},
137+
{type: InstallModeType.InstallModeTypeAllNamespaces, supported: true},
138+
];
139+
140+
expect(supports(set)(ownNamespaceGroup)).toBe(false);
141+
expect(supports(set)(singleNamespaceGroup)).toBe(false);
142+
expect(supports(set)(multiNamespaceGroup)).toBe(false);
143+
expect(supports(set)(allNamespacesGroup)).toBe(true);
144+
});
145+
});

0 commit comments

Comments
 (0)