11from pprint import pformat
22
33from kubernetes .dynamic .exceptions import ResourceNotFoundError
4+ from ocp_resources .catalog_source import CatalogSource
45from ocp_resources .cluster_service_version import ClusterServiceVersion
6+ from ocp_resources .image_content_source_policy import ImageContentSourcePolicy
57from ocp_resources .installplan import InstallPlan
68from ocp_resources .namespace import Namespace
79from ocp_resources .operator import Operator
810from ocp_resources .operator_group import OperatorGroup
11+ from ocp_resources .resource import ResourceEditor
912from ocp_resources .subscription import Subscription
1013from ocp_resources .utils import TimeoutExpiredError , TimeoutSampler
14+ from ocp_resources .validating_webhook_config import ValidatingWebhookConfiguration
1115from simple_logger .logger import get_logger
1216
13- from ocp_utilities .infra import cluster_resource
17+ from ocp_utilities .infra import cluster_resource , create_icsp , create_update_secret
1418
1519
1620LOGGER = get_logger (name = __name__ )
@@ -125,10 +129,12 @@ def install_operator(
125129 admin_client ,
126130 name ,
127131 channel ,
128- source ,
132+ source = None ,
129133 target_namespaces = None ,
130134 timeout = TIMEOUT_30MIN ,
131135 operator_namespace = None ,
136+ iib_index_image = None ,
137+ brew_token = None ,
132138):
133139 """
134140 Install operator on cluster.
@@ -137,12 +143,30 @@ def install_operator(
137143 admin_client (DynamicClient): Cluster client.
138144 name (str): Name of the operator to install.
139145 channel (str): Channel to install operator from.
140- source (str): CatalogSource name.
146+ source (str, optional ): CatalogSource name.
141147 target_namespaces (list, optional): Target namespaces for the operator install process.
142148 If not provided, a namespace with te operator name will be created and used.
143149 timeout (int): Timeout in seconds to wait for operator to be ready.
144- operator_namespace (str, optional): Operator namespace, if not provided, operator name will be used
150+ operator_namespace (str, optional): Operator namespace, if not provided, operator name will be used.
151+ iib_index_image (str, optional): iib index image url, If provided install operator from iib index image.
152+ brew_token (str, optional): Token to access iib index image registry.
145153 """
154+ catalog_source = None
155+ operator_market_namespace = "openshift-marketplace"
156+
157+ if iib_index_image :
158+ if not brew_token :
159+ raise ValueError ("brew_token must be provided for iib_index_image" )
160+
161+ catalog_source = create_catalog_source_for_iib_install (
162+ name = "iib-catalog" ,
163+ iib_index_image = iib_index_image ,
164+ brew_token = brew_token ,
165+ operator_market_namespace = operator_market_namespace ,
166+ )
167+ else :
168+ if not source :
169+ raise ValueError ("source must be provided if not using iib_index_image" )
146170
147171 operator_namespace = operator_namespace or name
148172 if target_namespaces :
@@ -170,8 +194,8 @@ def install_operator(
170194 name = name ,
171195 namespace = operator_namespace ,
172196 channel = channel ,
173- source = source ,
174- source_namespace = "openshift-marketplace" ,
197+ source = catalog_source . name if catalog_source else source ,
198+ source_namespace = operator_market_namespace ,
175199 install_plan_approval = "Automatic" ,
176200 )
177201 subscription .deploy (wait = True )
@@ -231,3 +255,109 @@ def uninstall_operator(
231255 )
232256
233257 csv .wait_deleted (timeout = timeout )
258+
259+
260+ def create_catalog_source_for_iib_install (
261+ name , iib_index_image , brew_token , operator_market_namespace
262+ ):
263+ """
264+ Create ICSP and catalog source for given iib index image
265+
266+ Args:
267+ name (str): Name for the catalog source (used in 'name, display_name and publisher').
268+ iib_index_image (str): iib index image url.
269+ brew_token (str): Token to access iib index image registry.
270+ operator_market_namespace (str): Namespace of the marketplace.
271+
272+ Returns:
273+ CatalogSource: catalog source object.
274+ """
275+
276+ def _manipulate_validating_webhook_configuration (_validating_webhook_configuration ):
277+ _resource_name = "imagecontentsourcepolicies"
278+ _validating_webhook_configuration_dict = (
279+ _validating_webhook_configuration .instance .to_dict ()
280+ )
281+ for webhook in _validating_webhook_configuration_dict ["webhooks" ]:
282+ for rule in webhook ["rules" ]:
283+ all_resources = rule ["resources" ]
284+ for _resources in all_resources :
285+ if _resource_name in _resources :
286+ all_resources [all_resources .index (_resource_name )] = "nonexists"
287+ break
288+
289+ return _validating_webhook_configuration_dict
290+
291+ def _icsp (_repository_digest_mirrors ):
292+ if icsp .exists :
293+ ResourceEditor (
294+ patches = {
295+ icsp : {
296+ "spec:" : {
297+ "repository_digest_mirrors" : _repository_digest_mirrors
298+ }
299+ }
300+ }
301+ ).update ()
302+ else :
303+ create_icsp (
304+ icsp_name = "brew-registry" ,
305+ repository_digest_mirrors = _repository_digest_mirrors ,
306+ )
307+
308+ brew_registry = "brew.registry.redhat.io"
309+ source_iib_registry = iib_index_image .split ("/" )[0 ]
310+ _iib_index_image = iib_index_image .replace (source_iib_registry , brew_registry )
311+ icsp = ImageContentSourcePolicy (name = "brew-registry" )
312+ validating_webhook_configuration = ValidatingWebhookConfiguration (
313+ name = "sre-imagecontentpolicies-validation"
314+ )
315+ repository_digest_mirrors = [
316+ {
317+ "source" : source_iib_registry ,
318+ "mirrors" : [brew_registry ],
319+ },
320+ {
321+ "source" : "registry.redhat.io" ,
322+ "mirrors" : [brew_registry ],
323+ },
324+ ]
325+
326+ if validating_webhook_configuration .exists :
327+ # This is managed cluster, we need to disable ValidatingWebhookConfiguration rule
328+ # for 'imagecontentsourcepolicies'
329+ validating_webhook_configuration_dict = (
330+ _manipulate_validating_webhook_configuration (
331+ _validating_webhook_configuration = validating_webhook_configuration
332+ )
333+ )
334+
335+ with ResourceEditor (
336+ patches = {
337+ validating_webhook_configuration : {
338+ "webhooks" : validating_webhook_configuration_dict ["webhooks" ]
339+ }
340+ }
341+ ):
342+ _icsp (_repository_digest_mirrors = repository_digest_mirrors )
343+ else :
344+ _icsp (_repository_digest_mirrors = repository_digest_mirrors )
345+
346+ secret_data_dict = {"auths" : {brew_registry : {"auth" : brew_token }}}
347+ create_update_secret (
348+ secret_data_dict = secret_data_dict ,
349+ name = "pull-secret" , # pragma: allowlist secret
350+ namespace = "openshift-config" ,
351+ )
352+
353+ catalog_source = CatalogSource (
354+ name = name ,
355+ namespace = operator_market_namespace ,
356+ display_name = name ,
357+ image = _iib_index_image ,
358+ publisher = name ,
359+ source_type = "grpc" ,
360+ update_strategy_registry_poll_interval = "30m" ,
361+ )
362+ catalog_source .deploy (wait = True )
363+ return catalog_source
0 commit comments