diff --git a/deploy/crd/kube-bind.io_apiservicebindings.yaml b/deploy/crd/kube-bind.io_apiservicebindings.yaml index 132432963..dd4fe66bc 100644 --- a/deploy/crd/kube-bind.io_apiservicebindings.yaml +++ b/deploy/crd/kube-bind.io_apiservicebindings.yaml @@ -84,6 +84,320 @@ spec: x-kubernetes-validations: - message: kubeconfigSecretRef is immutable rule: self == oldSelf + permissionClaims: + description: permissionClaims records decisions about permission claims + requested by the API service provider. Individual claims can be + accepted or rejected. If accepted, the API service provider gets + the requested access to the specified resources in this workspace. + Access is granted per GroupResource and other properties like selectors. + items: + description: AcceptablePermissionClaim is a permission claim that + stores the users acceptance in the field state. Only accepted + permission claims are reconciled. + properties: + create: + description: "create determines whether the provider can create + new objects in the consumer cluster by syncing a provider-owned + source to the consumer cluster. Created objects on the consumer + cluster are marked as owned by the provider by default. \n + Note that create permissions do not imply update permissions." + properties: + replaceExisting: + description: "replaceExisting means that an existing object + owned by the consumer will be replaced by the provider + object. \n If not true, and a conflicting consumer object + exists, it is not touched." + type: boolean + type: object + group: + default: "" + description: group is the name of an API group. For core groups + this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + objectSelector: + description: selector restricts which objects of the given resource + are being claimed. If unset, all objects across all namespaces + are being claimed, both consumer- and provider owned. + properties: + fieldSelectors: + description: fieldSelectors is a disjunctive list of field + selectors, following the same rules as kubernetes field + selectors, see https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/, + i.e. each field selector can be a conjunction of requirements. + items: + type: string + type: array + labelSelectors: + description: labelSelectors is a disjunctive list of label + selectors, following the same rules as kubernetes label + selectors, see https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/. + items: + additionalProperties: + type: string + type: object + type: array + names: + default: + - '*' + description: names is a list of values selecting by metadata.name, + or "*" which matches all names. + items: + type: string + type: array + x-kubernetes-validations: + - message: only names or * are allowed + rule: self.all(n, n.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?|[*]$')) + namespaces: + default: + - '*' + description: namespaces is a list of values selecting by + metadata.namespace, or "*" which matches all namespaces, + or empty string that matches cluster-scoped resources. + items: + type: string + type: array + x-kubernetes-validations: + - message: only namespace names,* or empty string are allowed + rule: self.all(n, n.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?|[*]?$')) + owner: + description: owner set means that resources of a specific + owner are selected and those owned by the other side are + ignored. A resource on the consumer cluster is owned by + the consumer by default, if not marked as owned by the + provider through the `kube-bind.io/owner=provider` annotation. + enum: + - Provider + - Consumer + type: string + type: object + onConflict: + description: onConflict determines how conflicts between objects + on the consumer and provider clusters will be resolved. + properties: + recreateWhenConsumerSideDeleted: + default: true + description: "recreateWhenConsumerSideDeleted set to true + (the default) means the provider will recreate the object + in case the object is missing on the consumer cluster, + but has been synchronized before. \n If set to false, + deleted provider-owned objects get deleted on the provider + cluster as well." + type: boolean + type: object + ownerTransfer: + description: ownerTransfer determines how ownership of objects + is transferred between the consumer and the provider. By default, + no transfer happens. If set to Donate, objects created by + the provider on the consumer cluster are immediately donated + to the consumer. If set to Adopt, objects created by the consumer + on the consumer cluster are immediately owned by the provider + and synced to the provider cluster. Ownership determines the + direction of synchronization. + enum: + - Donate + - Adopt + - "" + type: string + read: + description: read claims read access for the provider to matching + objects, excluding labels and annotations by default. Read + access is realized by syncing the objects or fields from the + consumer cluster to the provider cluster. + properties: + annotations: + description: annotations is a list of claimed annotation + key wildcard patterns that are synchronized from the consumer + cluster to the provider on objects that are owned by the + consumer. + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + annotationsOnProviderOwnedObjects: + description: overrideAnnotations is a list of claimed annotation + key wildcard patterns that are synchronized from the consumer + cluster to the provider on provider-owned objects. + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + labels: + description: labels is a list of label key wildcard patterns + that are synchronized from the consumer to the provider + on consumer-owned objects. + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + labelsOnProviderOwnedObjects: + description: LabelsOnProviderOwnedObjects is a list of claimed + label key wildcard patterns that are synchronized from + the consumer cluster to the provider on objects owned + by the provider. + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + type: object + resource: + description: 'resource is the name of the resource. Note: it + is worth noting that you can not ask for permissions for resource + provided by a CRD not provided by an service binding export.' + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + state: + description: state indicates if the claim is accepted or rejected. + enum: + - Accepted + - Rejected + type: string + update: + description: "update lists which updates to objects on the consumer + cluster are claimed. By default, the whole object is synced, + but metadata is not. \n Note that update permissions do not + imply create permissions." + properties: + alwaysRecreate: + description: "alwaysRecreate set to true means that matching + objects will be deleted and recreated on update. This + is useful for immutable objects. \n This does not apply + to metadata field updates." + type: boolean + annotations: + description: "annotations is a list of annotation key wildcard + patterns that are synced from the provider to the consumer + for provider-owned objects. \n By default, no annotations + are synced." + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + annotationsOnConsumerOwnedObjects: + description: "annotationsOnConsumerOwnedObjects is a list + of annotation key wildcard patterns that are synchronized + from the provider to the consumer for consumer-owned objects. + \n By default, no annotations are synced." + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + fields: + description: "fields are a list of JSON Paths in consumer-owned + objects on the consumer cluster that the provider wants + to control. \n This is ignored for provider-owned objects." + items: + type: string + type: array + labels: + description: "labels is a list of label key wildcard patterns + that are synced from the provider to the consumer for + provider-owned objects. \n By default, no labels are synced." + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + labelsOnConsumerOwnedObjects: + description: "labelsOnConsumerOwnedObjects is a list of + label key wildcard patterns that are synced from the provider + to the consumer for consumer-owner objects. \n By default, + no labels are synced." + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + preserving: + description: "preserving is a list of JSON Paths in provider-owned + objects on the consumer cluster that the consumer keeps + controlling, i.e. that are not overwritten by the provider, + but synced back to the provider side. \n This is ignored + for consumer-owned objects." + items: + type: string + type: array + type: object + version: + description: version is the version of the claimed resource. + minLength: 1 + type: string + required: + - resource + - state + - version + type: object + type: array required: - kubeconfigSecretRef type: object diff --git a/deploy/crd/kube-bind.io_apiserviceexportrequests.yaml b/deploy/crd/kube-bind.io_apiserviceexportrequests.yaml index 7a38c14f2..7a7771fcc 100644 --- a/deploy/crd/kube-bind.io_apiserviceexportrequests.yaml +++ b/deploy/crd/kube-bind.io_apiserviceexportrequests.yaml @@ -63,6 +63,345 @@ spec: this is the empty string '""'. pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ type: string + permissionClaims: + description: permissionClaims records decisions about permission + claims requested by the service provider. Individual claims + can be accepted or rejected. If accepted, the API service + provider gets the requested access to the specified resources + in this workspace. Access is granted per GroupResource, identity, + and other properties. + items: + description: "PermissionClaim specifies permissions for a + service provider to access resources and fields in a consumer + cluster. A PermissionClaim must be accepted before the specified + permissions are effective. Permission claims are implemented + through the konnector by syncing the right objects and fields + in the right direction. \n Permission claims distinguish + objects owned by the provider and objects owned by the consumer. + The owner of an object determines which side is the source + of truth for the object, i.e. whether the object is synced + from the consumer to the provider cluster or vice versa. + Exceptions can be specified for individual JSON Paths to + be owned by the other side. Metadata in general is not synced. + Exceptions for labels and annotations can be specified. + \n Ownership of an object is determined by the `kube-bind.io/owner` + annotation. Objects on the consumer cluster are owned by + the consumer by default. Objects on the provider cluster + are owned by the provider by default. The annotation is + only set if the object is owned by the respective other + side." + properties: + create: + description: "create determines whether the provider can + create new objects in the consumer cluster by syncing + a provider-owned source to the consumer cluster. Created + objects on the consumer cluster are marked as owned + by the provider by default. \n Note that create permissions + do not imply update permissions." + properties: + replaceExisting: + description: "replaceExisting means that an existing + object owned by the consumer will be replaced by + the provider object. \n If not true, and a conflicting + consumer object exists, it is not touched." + type: boolean + type: object + group: + default: "" + description: group is the name of an API group. For core + groups this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + objectSelector: + description: selector restricts which objects of the given + resource are being claimed. If unset, all objects across + all namespaces are being claimed, both consumer- and + provider owned. + properties: + fieldSelectors: + description: fieldSelectors is a disjunctive list + of field selectors, following the same rules as + kubernetes field selectors, see https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/, + i.e. each field selector can be a conjunction of + requirements. + items: + type: string + type: array + labelSelectors: + description: labelSelectors is a disjunctive list + of label selectors, following the same rules as + kubernetes label selectors, see https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/. + items: + additionalProperties: + type: string + type: object + type: array + names: + default: + - '*' + description: names is a list of values selecting by + metadata.name, or "*" which matches all names. + items: + type: string + type: array + x-kubernetes-validations: + - message: only names or * are allowed + rule: self.all(n, n.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?|[*]$')) + namespaces: + default: + - '*' + description: namespaces is a list of values selecting + by metadata.namespace, or "*" which matches all + namespaces, or empty string that matches cluster-scoped + resources. + items: + type: string + type: array + x-kubernetes-validations: + - message: only namespace names,* or empty string + are allowed + rule: self.all(n, n.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?|[*]?$')) + owner: + description: owner set means that resources of a specific + owner are selected and those owned by the other + side are ignored. A resource on the consumer cluster + is owned by the consumer by default, if not marked + as owned by the provider through the `kube-bind.io/owner=provider` + annotation. + enum: + - Provider + - Consumer + type: string + type: object + onConflict: + description: onConflict determines how conflicts between + objects on the consumer and provider clusters will be + resolved. + properties: + recreateWhenConsumerSideDeleted: + default: true + description: "recreateWhenConsumerSideDeleted set + to true (the default) means the provider will recreate + the object in case the object is missing on the + consumer cluster, but has been synchronized before. + \n If set to false, deleted provider-owned objects + get deleted on the provider cluster as well." + type: boolean + type: object + ownerTransfer: + description: ownerTransfer determines how ownership of + objects is transferred between the consumer and the + provider. By default, no transfer happens. If set to + Donate, objects created by the provider on the consumer + cluster are immediately donated to the consumer. If + set to Adopt, objects created by the consumer on the + consumer cluster are immediately owned by the provider + and synced to the provider cluster. Ownership determines + the direction of synchronization. + enum: + - Donate + - Adopt + - "" + type: string + read: + description: read claims read access for the provider + to matching objects, excluding labels and annotations + by default. Read access is realized by syncing the objects + or fields from the consumer cluster to the provider + cluster. + properties: + annotations: + description: annotations is a list of claimed annotation + key wildcard patterns that are synchronized from + the consumer cluster to the provider on objects + that are owned by the consumer. + items: + properties: + pattern: + description: pattern is a wildcard pattern that + is matched against the key. This means it + is either a literal string or starts or ends + in `*` but is not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + annotationsOnProviderOwnedObjects: + description: overrideAnnotations is a list of claimed + annotation key wildcard patterns that are synchronized + from the consumer cluster to the provider on provider-owned + objects. + items: + properties: + pattern: + description: pattern is a wildcard pattern that + is matched against the key. This means it + is either a literal string or starts or ends + in `*` but is not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + labels: + description: labels is a list of label key wildcard + patterns that are synchronized from the consumer + to the provider on consumer-owned objects. + items: + properties: + pattern: + description: pattern is a wildcard pattern that + is matched against the key. This means it + is either a literal string or starts or ends + in `*` but is not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + labelsOnProviderOwnedObjects: + description: LabelsOnProviderOwnedObjects is a list + of claimed label key wildcard patterns that are + synchronized from the consumer cluster to the provider + on objects owned by the provider. + items: + properties: + pattern: + description: pattern is a wildcard pattern that + is matched against the key. This means it + is either a literal string or starts or ends + in `*` but is not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + type: object + resource: + description: 'resource is the name of the resource. Note: + it is worth noting that you can not ask for permissions + for resource provided by a CRD not provided by an service + binding export.' + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + update: + description: "update lists which updates to objects on + the consumer cluster are claimed. By default, the whole + object is synced, but metadata is not. \n Note that + update permissions do not imply create permissions." + properties: + alwaysRecreate: + description: "alwaysRecreate set to true means that + matching objects will be deleted and recreated on + update. This is useful for immutable objects. \n + This does not apply to metadata field updates." + type: boolean + annotations: + description: "annotations is a list of annotation + key wildcard patterns that are synced from the provider + to the consumer for provider-owned objects. \n By + default, no annotations are synced." + items: + properties: + pattern: + description: pattern is a wildcard pattern that + is matched against the key. This means it + is either a literal string or starts or ends + in `*` but is not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + annotationsOnConsumerOwnedObjects: + description: "annotationsOnConsumerOwnedObjects is + a list of annotation key wildcard patterns that + are synchronized from the provider to the consumer + for consumer-owned objects. \n By default, no annotations + are synced." + items: + properties: + pattern: + description: pattern is a wildcard pattern that + is matched against the key. This means it + is either a literal string or starts or ends + in `*` but is not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + fields: + description: "fields are a list of JSON Paths in consumer-owned + objects on the consumer cluster that the provider + wants to control. \n This is ignored for provider-owned + objects." + items: + type: string + type: array + labels: + description: "labels is a list of label key wildcard + patterns that are synced from the provider to the + consumer for provider-owned objects. \n By default, + no labels are synced." + items: + properties: + pattern: + description: pattern is a wildcard pattern that + is matched against the key. This means it + is either a literal string or starts or ends + in `*` but is not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + labelsOnConsumerOwnedObjects: + description: "labelsOnConsumerOwnedObjects is a list + of label key wildcard patterns that are synced from + the provider to the consumer for consumer-owner + objects. \n By default, no labels are synced." + items: + properties: + pattern: + description: pattern is a wildcard pattern that + is matched against the key. This means it + is either a literal string or starts or ends + in `*` but is not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + preserving: + description: "preserving is a list of JSON Paths in + provider-owned objects on the consumer cluster that + the consumer keeps controlling, i.e. that are not + overwritten by the provider, but synced back to + the provider side. \n This is ignored for consumer-owned + objects." + items: + type: string + type: array + type: object + version: + description: version is the version of the claimed resource. + minLength: 1 + type: string + required: + - resource + - version + type: object + type: array resource: description: 'resource is the name of the resource. Note: it is worth noting that you can not ask for permissions for resource diff --git a/deploy/crd/kube-bind.io_apiserviceexports.yaml b/deploy/crd/kube-bind.io_apiserviceexports.yaml index 6b5a75827..f83a94964 100644 --- a/deploy/crd/kube-bind.io_apiserviceexports.yaml +++ b/deploy/crd/kube-bind.io_apiserviceexports.yaml @@ -108,6 +108,317 @@ spec: - kind - plural type: object + permissionClaims: + description: "permissionClaims is a list of permission claims that + the service provider asks the consumer to accept in the consumer + cluster binding to this export. The consumer can accept or deny + each claim. Some claims are required and with that a successful + binding is not possible. Others are optional. \n Note that a claim + added at a later point is not guaranteed to be seen and processed + (accepted or rejected) by the consumer." + items: + properties: + create: + description: "create determines whether the provider can create + new objects in the consumer cluster by syncing a provider-owned + source to the consumer cluster. Created objects on the consumer + cluster are marked as owned by the provider by default. \n + Note that create permissions do not imply update permissions." + properties: + replaceExisting: + description: "replaceExisting means that an existing object + owned by the consumer will be replaced by the provider + object. \n If not true, and a conflicting consumer object + exists, it is not touched." + type: boolean + type: object + group: + default: "" + description: group is the name of an API group. For core groups + this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + objectSelector: + description: selector restricts which objects of the given resource + are being claimed. If unset, all objects across all namespaces + are being claimed, both consumer- and provider owned. + properties: + fieldSelectors: + description: fieldSelectors is a disjunctive list of field + selectors, following the same rules as kubernetes field + selectors, see https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/, + i.e. each field selector can be a conjunction of requirements. + items: + type: string + type: array + labelSelectors: + description: labelSelectors is a disjunctive list of label + selectors, following the same rules as kubernetes label + selectors, see https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/. + items: + additionalProperties: + type: string + type: object + type: array + names: + default: + - '*' + description: names is a list of values selecting by metadata.name, + or "*" which matches all names. + items: + type: string + type: array + x-kubernetes-validations: + - message: only names or * are allowed + rule: self.all(n, n.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?|[*]$')) + namespaces: + default: + - '*' + description: namespaces is a list of values selecting by + metadata.namespace, or "*" which matches all namespaces, + or empty string that matches cluster-scoped resources. + items: + type: string + type: array + x-kubernetes-validations: + - message: only namespace names,* or empty string are allowed + rule: self.all(n, n.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?|[*]?$')) + owner: + description: owner set means that resources of a specific + owner are selected and those owned by the other side are + ignored. A resource on the consumer cluster is owned by + the consumer by default, if not marked as owned by the + provider through the `kube-bind.io/owner=provider` annotation. + enum: + - Provider + - Consumer + type: string + type: object + onConflict: + description: onConflict determines how conflicts between objects + on the consumer and provider clusters will be resolved. + properties: + recreateWhenConsumerSideDeleted: + default: true + description: "recreateWhenConsumerSideDeleted set to true + (the default) means the provider will recreate the object + in case the object is missing on the consumer cluster, + but has been synchronized before. \n If set to false, + deleted provider-owned objects get deleted on the provider + cluster as well." + type: boolean + type: object + ownerTransfer: + description: ownerTransfer determines how ownership of objects + is transferred between the consumer and the provider. By default, + no transfer happens. If set to Donate, objects created by + the provider on the consumer cluster are immediately donated + to the consumer. If set to Adopt, objects created by the consumer + on the consumer cluster are immediately owned by the provider + and synced to the provider cluster. Ownership determines the + direction of synchronization. + enum: + - Donate + - Adopt + - "" + type: string + read: + description: read claims read access for the provider to matching + objects, excluding labels and annotations by default. Read + access is realized by syncing the objects or fields from the + consumer cluster to the provider cluster. + properties: + annotations: + description: annotations is a list of claimed annotation + key wildcard patterns that are synchronized from the consumer + cluster to the provider on objects that are owned by the + consumer. + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + annotationsOnProviderOwnedObjects: + description: overrideAnnotations is a list of claimed annotation + key wildcard patterns that are synchronized from the consumer + cluster to the provider on provider-owned objects. + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + labels: + description: labels is a list of label key wildcard patterns + that are synchronized from the consumer to the provider + on consumer-owned objects. + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + labelsOnProviderOwnedObjects: + description: LabelsOnProviderOwnedObjects is a list of claimed + label key wildcard patterns that are synchronized from + the consumer cluster to the provider on objects owned + by the provider. + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + type: object + required: + description: required indicates whether the APIServiceBinding + will work if this claim is not accepted. If a required claim + is denied, the binding is aborted. + type: boolean + resource: + description: 'resource is the name of the resource. Note: it + is worth noting that you can not ask for permissions for resource + provided by a CRD not provided by an service binding export.' + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + update: + description: "update lists which updates to objects on the consumer + cluster are claimed. By default, the whole object is synced, + but metadata is not. \n Note that update permissions do not + imply create permissions." + properties: + alwaysRecreate: + description: "alwaysRecreate set to true means that matching + objects will be deleted and recreated on update. This + is useful for immutable objects. \n This does not apply + to metadata field updates." + type: boolean + annotations: + description: "annotations is a list of annotation key wildcard + patterns that are synced from the provider to the consumer + for provider-owned objects. \n By default, no annotations + are synced." + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + annotationsOnConsumerOwnedObjects: + description: "annotationsOnConsumerOwnedObjects is a list + of annotation key wildcard patterns that are synchronized + from the provider to the consumer for consumer-owned objects. + \n By default, no annotations are synced." + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + fields: + description: "fields are a list of JSON Paths in consumer-owned + objects on the consumer cluster that the provider wants + to control. \n This is ignored for provider-owned objects." + items: + type: string + type: array + labels: + description: "labels is a list of label key wildcard patterns + that are synced from the provider to the consumer for + provider-owned objects. \n By default, no labels are synced." + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + labelsOnConsumerOwnedObjects: + description: "labelsOnConsumerOwnedObjects is a list of + label key wildcard patterns that are synced from the provider + to the consumer for consumer-owner objects. \n By default, + no labels are synced." + items: + properties: + pattern: + description: pattern is a wildcard pattern that is + matched against the key. This means it is either + a literal string or starts or ends in `*` but is + not '*' itself. + minLength: 1 + type: string + required: + - pattern + type: object + type: array + preserving: + description: "preserving is a list of JSON Paths in provider-owned + objects on the consumer cluster that the consumer keeps + controlling, i.e. that are not overwritten by the provider, + but synced back to the provider side. \n This is ignored + for consumer-owned objects." + items: + type: string + type: array + type: object + version: + description: version is the version of the claimed resource. + minLength: 1 + type: string + required: + - resource + - version + type: object + type: array scope: description: scope indicates whether the defined custom resource is cluster- or namespace-scoped. Allowed values are `Cluster` and `Namespaced`. diff --git a/deploy/crd/kube-bind.io_apiserviceexporttemplates.yaml b/deploy/crd/kube-bind.io_apiserviceexporttemplates.yaml new file mode 100644 index 000000000..2a15d271d --- /dev/null +++ b/deploy/crd/kube-bind.io_apiserviceexporttemplates.yaml @@ -0,0 +1,186 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: apiserviceexporttemplates.kube-bind.io +spec: + group: kube-bind.io + names: + categories: + - kube-bindings + kind: APIServiceExportTemplate + listKind: APIServiceExportTemplateList + plural: apiserviceexporttemplates + singular: apiserviceexporttemplate + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Established")].status + name: Established + priority: 5 + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: APIServiceExportTemplate specifies the resource to be exported. + It references the CRD to be exported along with additional resources that + are synchronized from and to the consumer cluster. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: spec specifies the resource. + properties: + APIServiceSelector: + properties: + group: + type: string + resource: + type: string + versions: + items: + type: string + type: array + type: object + permissionClaims: + items: + description: "PermissionClaim selects objects of a GVR that a service + provider may request and that a consumer may accept and allow + the service provider access to. \n TODO fix validation +kubebuilder:validation:XValidation:rule=\"!(self.create.donate + && self.adopt)\",message=\"donate and adopt are mutually exclusive\"" + properties: + adopt: + description: adopt set to true means that objects created by + the consumer are adopted by the provider. i.e. the provider + will become the owner. + type: boolean + create: + description: "only for owner Provider \n create determines whether + the kube-bind konnector will sync matching objects from the + provider side down to the consumer cluster." + properties: + donate: + description: donate set to true means that a newly created + object by the provider is immediately owned by the consumer. + If false, the object stays in ownership of the provider + type: boolean + type: object + global: + description: Global claims global resources for the given group/resource. + This is mutually exclusive with resourceSelector. + type: boolean + group: + default: "" + description: group is the name of an API group. For core groups + this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + onConflict: + description: onConflict determines how the conflicts between + objects on the consumer side will be resolved. + properties: + providerOverrides: + description: providerOverrides will make the provider override + any object that might already exist in the consumer cluster + if it has the same namespaced name as a resource created + by the provider, but is not the result of syncing. + type: boolean + recreateWhenConsumerSideDeleted: + description: recreateWhenConsumerSideDeleted set to true + means the provider will recreate the object in case the + object is missing on the consumer side. Even if the consumer + mistakenly or intentionally deletes the objet, the provider + will recreate it. If the field is set as false, the provider + will not recreate the object in case the object is deleted + on the RecreateWhenConsumerSideDeleted side. + type: boolean + type: object + required: + description: required indicates whether the APIServiceBinding + will work if this claim is not accepted. + type: boolean + resource: + description: 'resource is the name of the resource. Note: it + is worth noting that you can not ask for permissions for resource + provided by a CRD not provided by an service binding export.' + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + selector: + description: selector selects which resources are affected by + this claim. + properties: + name: + description: name of an object within a claimed group/resource. + It matches the metadata.name field of the underlying object. + If name is unset, all objects in bound namespaces will + be claimed. + maxLength: 253 + minLength: 1 + pattern: ^([a-z0-9][-a-z0-9_.]*)?[a-z0-9]$ + type: string + owner: + enum: + - Provider + - Consumer + type: string + type: object + update: + description: update lists a number of claimed permissions for + the provider. "field" and "preserving" are mutually exclusive. + properties: + alwaysRecreate: + description: alwaysRecreate, when true will make the konnector + delete the old object and create a new one instead of + updating. Useful for immutable objects. + type: boolean + fields: + description: fields are the fields owned by the owner of + the claim. If the owner sets values of those fields, they + will be synced to the other participant. Mutually exclusive + with preservings. + items: + type: string + type: array + preservings: + description: Preservings are the fields that are preserved + by the konnector during synchronization. The owner is + not able to set those fields. If the owner changes the + value of these fields, their change will be overwritten. + items: + type: string + type: array + type: object + version: + type: string + required: + - resource + - version + type: object + type: array + type: object + status: + description: status contains reconciliation information for the resource. + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/pkg/apis/kubebind/v1alpha1/apiservicebinding_types.go b/pkg/apis/kubebind/v1alpha1/apiservicebinding_types.go index 4a664a7af..f8522d695 100644 --- a/pkg/apis/kubebind/v1alpha1/apiservicebinding_types.go +++ b/pkg/apis/kubebind/v1alpha1/apiservicebinding_types.go @@ -87,8 +87,35 @@ type APIServiceBindingSpec struct { // +kubebuilder:validation:Required // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="kubeconfigSecretRef is immutable" KubeconfigSecretRef ClusterSecretKeyRef `json:"kubeconfigSecretRef"` + + // permissionClaims records decisions about permission claims requested by the API service provider. + // Individual claims can be accepted or rejected. If accepted, the API service provider gets the + // requested access to the specified resources in this workspace. Access is granted per + // GroupResource and other properties like selectors. + // + // +optional + PermissionClaims []AcceptablePermissionClaim `json:"permissionClaims,omitempty"` +} + +// AcceptablePermissionClaim is a permission claim that stores the users acceptance in the field state. Only accepted permission claims are reconciled. +type AcceptablePermissionClaim struct { + PermissionClaim `json:",inline"` + + // state indicates if the claim is accepted or rejected. + // + // +required + // +kubebuilder:validation:Required + // +kubebuilder:validation:Enum=Accepted;Rejected + State AcceptablePermissionClaimState `json:"state"` } +type AcceptablePermissionClaimState string + +const ( + ClaimAccepted AcceptablePermissionClaimState = "Accepted" + ClaimRejected AcceptablePermissionClaimState = "Rejected" +) + type APIServiceBindingStatus struct { // providerPrettyName is the pretty name of the service provider cluster. This // can be shared among different APIServiceBindings. diff --git a/pkg/apis/kubebind/v1alpha1/apiserviceexport_types.go b/pkg/apis/kubebind/v1alpha1/apiserviceexport_types.go index a802ad167..0a61d9ce3 100644 --- a/pkg/apis/kubebind/v1alpha1/apiserviceexport_types.go +++ b/pkg/apis/kubebind/v1alpha1/apiserviceexport_types.go @@ -79,6 +79,17 @@ func (in *APIServiceExport) SetConditions(conditions conditionsapi.Conditions) { type APIServiceExportSpec struct { APIServiceExportCRDSpec `json:",inline"` + // permissionClaims is a list of permission claims that the service provider + // asks the consumer to accept in the consumer cluster binding to this export. + // The consumer can accept or deny each claim. Some claims are required and + // with that a successful binding is not possible. Others are optional. + // + // Note that a claim added at a later point is not guaranteed to be seen and + // processed (accepted or rejected) by the consumer. + // + // +optional + PermissionClaims []ExportPermissionClaim `json:"permissionClaims,omitempty"` + // informerScope is the scope of the APIServiceExport. It can be either Cluster or Namespace. // // Cluster: The konnector has permission to watch all namespaces at once and cluster-scoped resources. @@ -92,6 +103,14 @@ type APIServiceExportSpec struct { InformerScope Scope `json:"informerScope"` } +type ExportPermissionClaim struct { + PermissionClaim `json:",inline"` + + // required indicates whether the APIServiceBinding will work if this claim + // is not accepted. If a required claim is denied, the binding is aborted. + Required bool `json:"required"` +} + type APIServiceExportCRDSpec struct { // group is the API group of the defined custom resource. Empty string means the // core API group. The resources are served under `/apis//...` or `/api` for the core group. diff --git a/pkg/apis/kubebind/v1alpha1/apiserviceexportrequest_types.go b/pkg/apis/kubebind/v1alpha1/apiserviceexportrequest_types.go index f5234bb05..a0d329825 100644 --- a/pkg/apis/kubebind/v1alpha1/apiserviceexportrequest_types.go +++ b/pkg/apis/kubebind/v1alpha1/apiserviceexportrequest_types.go @@ -110,6 +110,12 @@ type APIServiceExportRequestResource struct { // versions is a list of versions that should be exported. If this is empty // a sensible default is chosen by the service provider. Versions []string `json:"versions,omitempty"` + + // permissionClaims records decisions about permission claims requested by the service provider. + // Individual claims can be accepted or rejected. If accepted, the API service provider gets the + // requested access to the specified resources in this workspace. Access is granted per + // GroupResource, identity, and other properties. + PermissionClaims []PermissionClaim `json:"permissionClaims,omitempty"` } // GroupResource identifies a resource. diff --git a/pkg/apis/kubebind/v1alpha1/permissionclaims_types.go b/pkg/apis/kubebind/v1alpha1/permissionclaims_types.go new file mode 100644 index 000000000..2863e74d2 --- /dev/null +++ b/pkg/apis/kubebind/v1alpha1/permissionclaims_types.go @@ -0,0 +1,272 @@ +/* +Copyright 2022 The Kube Bind Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +// PermissionClaim specifies permissions for a service provider to access +// resources and fields in a consumer cluster. A PermissionClaim must be +// accepted before the specified permissions are effective. Permission claims +// are implemented through the konnector by syncing the right objects and +// fields in the right direction. +// +// Permission claims distinguish objects owned by the provider and objects +// owned by the consumer. The owner of an object determines which side is the +// source of truth for the object, i.e. whether the object is synced from the +// consumer to the provider cluster or vice versa. Exceptions can be specified +// for individual JSON Paths to be owned by the other side. Metadata in general +// is not synced. Exceptions for labels and annotations can be specified. +// +// Ownership of an object is determined by the `kube-bind.io/owner` annotation. +// Objects on the consumer cluster are owned by the consumer by default. Objects +// on the provider cluster are owned by the provider by default. The annotation +// is only set if the object is owned by the respective other side. +type PermissionClaim struct { + GroupResource `json:","` + + // version is the version of the claimed resource. + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:MinLength:=1 + Version string `json:"version"` + + // selector restricts which objects of the given resource are being claimed. + // If unset, all objects across all namespaces are being claimed, both + // consumer- and provider owned. + // + // +optional + // +kubebuilder:default:={} + ObjectSelector *ObjectSelector `json:"objectSelector,omitempty"` + + // read claims read access for the provider to matching objects, excluding + // labels and annotations by default. Read access is realized by syncing + // the objects or fields from the consumer cluster to the provider cluster. + // + // +optional + // +kubebuilder:default={} + Read *PermissionClaimReadOptions `json:"read,omitempty"` + + // create determines whether the provider can create new objects in the + // consumer cluster by syncing a provider-owned source to the consumer + // cluster. Created objects on the consumer cluster are marked as owned by + // the provider by default. + // + // Note that create permissions do not imply update permissions. + // + // +optional + Create *PermissionClaimCreateOptions `json:"create,omitempty"` + + // onConflict determines how conflicts between objects on the consumer + // and provider clusters will be resolved. + // + // +optional + // +kubebuilder:default:={} + OnConflict *PermissionClaimOnConflictOptions `json:"onConflict,omitempty"` + + // update lists which updates to objects on the consumer cluster are claimed. + // By default, the whole object is synced, but metadata is not. + // + // Note that update permissions do not imply create permissions. + // + // +optional + Update *PermissionClaimUpdateOptions `json:"update,omitempty"` + + // ownerTransfer determines how ownership of objects is transferred between + // the consumer and the provider. By default, no transfer happens. If set to + // Donate, objects created by the provider on the consumer cluster are + // immediately donated to the consumer. If set to Adopt, objects created by + // the consumer on the consumer cluster are immediately owned by the + // provider and synced to the provider cluster. Ownership determines the + // direction of synchronization. + // + // +kubebuilder:validation:Enum=Donate;Adopt;"" + // +optional + OwnerTransfer OwnerTransfer `json:"ownerTransfer,omitempty"` +} + +type OwnerTransfer string + +const ( + OwnerTransferNone OwnerTransfer = "" + OwnerTransferDonate OwnerTransfer = "Donate" + OwnerTransferAdopt OwnerTransfer = "Adopt" +) + +type PermissionClaimReadOptions struct { + // labels is a list of label key wildcard patterns that are synchronized + // from the consumer to the provider on consumer-owned objects. + // + // +optional + Labels []Matcher `json:"labels,omitempty"` + + // LabelsOnProviderOwnedObjects is a list of claimed label key wildcard + // patterns that are synchronized from the consumer cluster to the provider + // on objects owned by the provider. + // + // +optional + LabelsOnProviderOwnedObjects []Matcher `json:"labelsOnProviderOwnedObjects,omitempty"` + + // annotations is a list of claimed annotation key wildcard patterns + // that are synchronized from the consumer cluster to the provider on + // objects that are owned by the consumer. + // + // +optional + Annotations []Matcher `json:"annotations,omitempty"` + + // overrideAnnotations is a list of claimed annotation key wildcard + // patterns that are synchronized from the consumer cluster to the provider + // on provider-owned objects. + // + // +optional + AnnotationsOnProviderOwnedObjects []Matcher `json:"annotationsOnProviderOwnedObjects,omitempty"` +} + +type Matcher struct { + // pattern is a wildcard pattern that is matched against the key. This means + // it is either a literal string or starts or ends in `*` but is not '*' + // itself. + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:MinLength:=1 + Pattern string `json:"pattern,omitempty"` +} + +type PermissionClaimOnConflictOptions struct { + // recreateWhenConsumerSideDeleted set to true (the default) means the provider will recreate the object + // in case the object is missing on the consumer cluster, but has been synchronized before. + // + // If set to false, deleted provider-owned objects get deleted on the provider cluster as well. + // + // +kubebuilder:default:=true + RecreateWhenConsumerSideDeleted bool `json:"recreateWhenConsumerSideDeleted"` +} + +type PermissionClaimCreateOptions struct { + // replaceExisting means that an existing object owned by the consumer will + // be replaced by the provider object. + // + // If not true, and a conflicting consumer object exists, it is not touched. + // + // +optional + ReplaceExisting bool `json:"replaceExisting,omitempty"` +} + +type PermissionClaimUpdateOptions struct { + // fields are a list of JSON Paths in consumer-owned objects on the consumer + // cluster that the provider wants to control. + // + // This is ignored for provider-owned objects. + // + // +optional + Fields []string `json:"fields,omitempty"` + + // preserving is a list of JSON Paths in provider-owned objects on the + // consumer cluster that the consumer keeps controlling, i.e. that are not + // overwritten by the provider, but synced back to the provider side. + // + // This is ignored for consumer-owned objects. + // + // +optional + Preserving []string `json:"preserving,omitempty"` + + // alwaysRecreate set to true means that matching objects will be deleted + // and recreated on update. This is useful for immutable objects. + // + // This does not apply to metadata field updates. + // + // +optional + AlwaysRecreate bool `json:"alwaysRecreate,omitempty"` + + // labels is a list of label key wildcard patterns that are synced from the + // provider to the consumer for provider-owned objects. + // + // By default, no labels are synced. + // + // +optional + Labels []Matcher `json:"labels,omitempty"` + + // labelsOnConsumerOwnedObjects is a list of label key wildcard patterns + // that are synced from the provider to the consumer for consumer-owner + // objects. + // + // By default, no labels are synced. + // + // +optional + LabelsOnConsumerOwnedObjects []Matcher `json:"labelsOnConsumerOwnedObjects,omitempty"` + + // annotations is a list of annotation key wildcard patterns that are synced + // from the provider to the consumer for provider-owned objects. + // + // By default, no annotations are synced. + // + // +optional + Annotations []Matcher `json:"annotations,omitempty"` + + // annotationsOnConsumerOwnedObjects is a list of annotation key wildcard + // patterns that are synchronized from the provider to the consumer for + // consumer-owned objects. + // + // By default, no annotations are synced. + // + // +optional + AnnotationsOnConsumerOwnedObjects []Matcher `json:"annotationsOnConsumerOwnedObjects,omitempty"` +} + +type ObjectSelector struct { + // names is a list of values selecting by metadata.name, or "*" which + // matches all names. + // + // +kubebuilder:validation:XValidation:rule="self.all(n, n.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?|[*]$'))",message="only names or * are allowed" + // +kubebuilder:default:={"*"} + // +optional + Names []string `json:"names,omitempty"` + + // namespaces is a list of values selecting by metadata.namespace, or "*" + // which matches all namespaces, or empty string that matches cluster-scoped + // resources. + // + // +kubebuilder:validation:XValidation:rule="self.all(n, n.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?|[*]?$'))",message="only namespace names,* or empty string are allowed" + // +kubebuilder:default:={"*"} + // +optional + Namespaces []string `json:"namespaces,omitempty"` + + // labelSelectors is a disjunctive list of label selectors, following the + // same rules as kubernetes label selectors, see https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/. + LabelSelectors []map[string]string `json:"labelSelectors,omitempty"` + + // fieldSelectors is a disjunctive list of field selectors, following the + // same rules as kubernetes field selectors, see https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/, + // i.e. each field selector can be a conjunction of requirements. + FieldSelectors []string `json:"fieldSelectors,omitempty"` + + // owner set means that resources of a specific owner are selected and those + // owned by the other side are ignored. A resource on the consumer cluster + // is owned by the consumer by default, if not marked as owned by the + // provider through the `kube-bind.io/owner=provider` annotation. + // + // +kubebuilder:validation:Enum=Provider;Consumer + // +optional + Owner PermissionClaimResourceOwner `json:"owner,omitempty"` +} + +type PermissionClaimResourceOwner string + +const ( + // Provider means that the owner of the resource is the Provider. + Provider PermissionClaimResourceOwner = "Provider" + + // Consumer means that the owner of the resource is the Consumer. + Consumer PermissionClaimResourceOwner = "Consumer" +) diff --git a/pkg/apis/kubebind/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/kubebind/v1alpha1/zz_generated.deepcopy.go index f16e3d1d0..910deaa77 100644 --- a/pkg/apis/kubebind/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/kubebind/v1alpha1/zz_generated.deepcopy.go @@ -33,7 +33,7 @@ func (in *APIServiceBinding) DeepCopyInto(out *APIServiceBinding) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) return } @@ -93,6 +93,13 @@ func (in *APIServiceBindingList) DeepCopyObject() runtime.Object { func (in *APIServiceBindingSpec) DeepCopyInto(out *APIServiceBindingSpec) { *out = *in out.KubeconfigSecretRef = in.KubeconfigSecretRef + if in.PermissionClaims != nil { + in, out := &in.PermissionClaims, &out.PermissionClaims + *out = make([]AcceptablePermissionClaim, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } @@ -284,6 +291,13 @@ func (in *APIServiceExportRequestResource) DeepCopyInto(out *APIServiceExportReq *out = make([]string, len(*in)) copy(*out, *in) } + if in.PermissionClaims != nil { + in, out := &in.PermissionClaims, &out.PermissionClaims + *out = make([]PermissionClaim, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } @@ -397,6 +411,13 @@ func (in *APIServiceExportSchema) DeepCopy() *APIServiceExportSchema { func (in *APIServiceExportSpec) DeepCopyInto(out *APIServiceExportSpec) { *out = *in in.APIServiceExportCRDSpec.DeepCopyInto(&out.APIServiceExportCRDSpec) + if in.PermissionClaims != nil { + in, out := &in.PermissionClaims, &out.PermissionClaims + *out = make([]ExportPermissionClaim, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } @@ -560,6 +581,23 @@ func (in *APIServiceNamespaceStatus) DeepCopy() *APIServiceNamespaceStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AcceptablePermissionClaim) DeepCopyInto(out *AcceptablePermissionClaim) { + *out = *in + in.PermissionClaim.DeepCopyInto(&out.PermissionClaim) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AcceptablePermissionClaim. +func (in *AcceptablePermissionClaim) DeepCopy() *AcceptablePermissionClaim { + if in == nil { + return nil + } + out := new(AcceptablePermissionClaim) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AuthenticationMethod) DeepCopyInto(out *AuthenticationMethod) { *out = *in @@ -809,6 +847,23 @@ func (in *ClusterSecretKeyRef) DeepCopy() *ClusterSecretKeyRef { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExportPermissionClaim) DeepCopyInto(out *ExportPermissionClaim) { + *out = *in + in.PermissionClaim.DeepCopyInto(&out.PermissionClaim) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExportPermissionClaim. +func (in *ExportPermissionClaim) DeepCopy() *ExportPermissionClaim { + if in == nil { + return nil + } + out := new(ExportPermissionClaim) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GroupResource) DeepCopyInto(out *GroupResource) { *out = *in @@ -841,6 +896,22 @@ func (in *LocalSecretKeyRef) DeepCopy() *LocalSecretKeyRef { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Matcher) DeepCopyInto(out *Matcher) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Matcher. +func (in *Matcher) DeepCopy() *Matcher { + if in == nil { + return nil + } + out := new(Matcher) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NameObjectMeta) DeepCopyInto(out *NameObjectMeta) { *out = *in @@ -872,3 +943,203 @@ func (in *OAuth2CodeGrant) DeepCopy() *OAuth2CodeGrant { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectSelector) DeepCopyInto(out *ObjectSelector) { + *out = *in + if in.Names != nil { + in, out := &in.Names, &out.Names + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.LabelSelectors != nil { + in, out := &in.LabelSelectors, &out.LabelSelectors + *out = make([]map[string]string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + } + if in.FieldSelectors != nil { + in, out := &in.FieldSelectors, &out.FieldSelectors + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectSelector. +func (in *ObjectSelector) DeepCopy() *ObjectSelector { + if in == nil { + return nil + } + out := new(ObjectSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PermissionClaim) DeepCopyInto(out *PermissionClaim) { + *out = *in + out.GroupResource = in.GroupResource + if in.ObjectSelector != nil { + in, out := &in.ObjectSelector, &out.ObjectSelector + *out = new(ObjectSelector) + (*in).DeepCopyInto(*out) + } + if in.Read != nil { + in, out := &in.Read, &out.Read + *out = new(PermissionClaimReadOptions) + (*in).DeepCopyInto(*out) + } + if in.Create != nil { + in, out := &in.Create, &out.Create + *out = new(PermissionClaimCreateOptions) + **out = **in + } + if in.OnConflict != nil { + in, out := &in.OnConflict, &out.OnConflict + *out = new(PermissionClaimOnConflictOptions) + **out = **in + } + if in.Update != nil { + in, out := &in.Update, &out.Update + *out = new(PermissionClaimUpdateOptions) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PermissionClaim. +func (in *PermissionClaim) DeepCopy() *PermissionClaim { + if in == nil { + return nil + } + out := new(PermissionClaim) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PermissionClaimCreateOptions) DeepCopyInto(out *PermissionClaimCreateOptions) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PermissionClaimCreateOptions. +func (in *PermissionClaimCreateOptions) DeepCopy() *PermissionClaimCreateOptions { + if in == nil { + return nil + } + out := new(PermissionClaimCreateOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PermissionClaimOnConflictOptions) DeepCopyInto(out *PermissionClaimOnConflictOptions) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PermissionClaimOnConflictOptions. +func (in *PermissionClaimOnConflictOptions) DeepCopy() *PermissionClaimOnConflictOptions { + if in == nil { + return nil + } + out := new(PermissionClaimOnConflictOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PermissionClaimReadOptions) DeepCopyInto(out *PermissionClaimReadOptions) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make([]Matcher, len(*in)) + copy(*out, *in) + } + if in.LabelsOnProviderOwnedObjects != nil { + in, out := &in.LabelsOnProviderOwnedObjects, &out.LabelsOnProviderOwnedObjects + *out = make([]Matcher, len(*in)) + copy(*out, *in) + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make([]Matcher, len(*in)) + copy(*out, *in) + } + if in.AnnotationsOnProviderOwnedObjects != nil { + in, out := &in.AnnotationsOnProviderOwnedObjects, &out.AnnotationsOnProviderOwnedObjects + *out = make([]Matcher, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PermissionClaimReadOptions. +func (in *PermissionClaimReadOptions) DeepCopy() *PermissionClaimReadOptions { + if in == nil { + return nil + } + out := new(PermissionClaimReadOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PermissionClaimUpdateOptions) DeepCopyInto(out *PermissionClaimUpdateOptions) { + *out = *in + if in.Fields != nil { + in, out := &in.Fields, &out.Fields + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Preserving != nil { + in, out := &in.Preserving, &out.Preserving + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make([]Matcher, len(*in)) + copy(*out, *in) + } + if in.LabelsOnConsumerOwnedObjects != nil { + in, out := &in.LabelsOnConsumerOwnedObjects, &out.LabelsOnConsumerOwnedObjects + *out = make([]Matcher, len(*in)) + copy(*out, *in) + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make([]Matcher, len(*in)) + copy(*out, *in) + } + if in.AnnotationsOnConsumerOwnedObjects != nil { + in, out := &in.AnnotationsOnConsumerOwnedObjects, &out.AnnotationsOnConsumerOwnedObjects + *out = make([]Matcher, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PermissionClaimUpdateOptions. +func (in *PermissionClaimUpdateOptions) DeepCopy() *PermissionClaimUpdateOptions { + if in == nil { + return nil + } + out := new(PermissionClaimUpdateOptions) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/kubectl/bind-apiservice/plugin/servicebindings.go b/pkg/kubectl/bind-apiservice/plugin/servicebindings.go index 431ed7f14..edafa0be9 100644 --- a/pkg/kubectl/bind-apiservice/plugin/servicebindings.go +++ b/pkg/kubectl/bind-apiservice/plugin/servicebindings.go @@ -17,8 +17,12 @@ limitations under the License. package plugin import ( + "bufio" + "bytes" "context" "fmt" + "io" + "strings" "time" apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" @@ -115,3 +119,179 @@ func (b *BindAPIServiceOptions) createAPIServiceBindings(ctx context.Context, co return bindings, nil } + +func printPermissionClaim(w io.Writer, p kubebindv1alpha1.ExportPermissionClaim) error { + var b bytes.Buffer + + var groupResource string + if p.GroupResource.Group != "" { + groupResource = fmt.Sprintf("%s objects (apiVersion: \"%s/%s\")", p.GroupResource.Resource, p.GroupResource.Group, p.Version) + } else { + groupResource = fmt.Sprintf("%s objects (apiVersion: \"%s\")", p.GroupResource.Resource, p.Version) + } + + if err := writeFirstLines(&b, groupResource, p.PermissionClaim); err != nil { + return err + } + + if err := writeCreate(&b, p.PermissionClaim); err != nil { + return err + } + + if err := writeOnConflict(&b, p.PermissionClaim); err != nil { + return err + } + + if err := writeUpdateClause(&b, p.PermissionClaim); err != nil { + return err + } + + if err := writeRequiredAndAcceptance(&b, p.Required); err != nil { + return err + } + + _, err := fmt.Fprint(w, b.String()) + return err +} + +func writeFirstLines(b *bytes.Buffer, groupResource string, claim kubebindv1alpha1.PermissionClaim) error { + var err error + + donate := claim.OwnerTransfer == kubebindv1alpha1.OwnerTransferDonate + adopt := claim.OwnerTransfer == kubebindv1alpha1.OwnerTransferAdopt + + var names []string + var owner kubebindv1alpha1.PermissionClaimResourceOwner + if claim.ObjectSelector != nil { + names = claim.ObjectSelector.Names + owner = claim.ObjectSelector.Owner + } + + var verb string + switch owner { + case kubebindv1alpha1.Provider: + verb = "write" + case kubebindv1alpha1.Consumer: + verb = "read" + default: + verb = "read and write" + } + + switch { + case !donate && !adopt: + groupResource = verb + " " + groupResource + case donate && !adopt: + groupResource = "create user owned " + groupResource + case !donate && adopt: + groupResource = "have ownership of " + groupResource + } + + var ref string + if len(names) > 0 { + ref = " which are referenced with:" + for _, name := range names { + ref = fmt.Sprintf("%s\n\t- name: \"%s\"", ref, name) + } + ref += "\n" + } else { + ref += " " + } + + _, err = fmt.Fprintf(b, "The provider wants to %s%son your cluster.\n", groupResource, ref) + + return err + +} + +func writeCreate(b io.StringWriter, claim kubebindv1alpha1.PermissionClaim) error { + var err error + + switch { + case claim.Create == nil || !claim.Create.ReplaceExisting: + //_, err = b.WriteString("Conflicting objects will not be overwritten. ") + case claim.Create.ReplaceExisting: + _, err = b.WriteString("Conflicting objects will be replaced by the provider. ") + } + + return err +} + +func writeOnConflict(b io.StringWriter, claim kubebindv1alpha1.PermissionClaim) error { + var err error + + switch { + case claim.OnConflict == nil || !claim.OnConflict.RecreateWhenConsumerSideDeleted: + //_, err = b.WriteString("Created objects will not be recreated upon deletion. ") + case claim.OnConflict.RecreateWhenConsumerSideDeleted: + _, err = b.WriteString("Created objects will be recreated upon deletion. ") + default: //Do nothing + } + + return err +} + +func writeUpdateClause(b *bytes.Buffer, claim kubebindv1alpha1.PermissionClaim) error { + var err error + + if claim.Update == nil { + return nil + } + + if claim.Update.Fields != nil { + _, err = fmt.Fprintf(b, "The following fields of the objects will still be able to be changed by the provider:\n") + } + if claim.Update.Preserving != nil { + _, err = b.WriteString("The following fields of the objects will be preserved by the provider:\n") + } + + for _, s := range append(claim.Update.Fields, claim.Update.Preserving...) { + _, err = fmt.Fprintf(b, "\t\"%s\"\n", s) + } + + if claim.Update.AlwaysRecreate { + _, err = b.WriteString("Modification of said objects will by handled by deletion and recreation of said objects.\n") + } + + return err +} + +func writeRequiredAndAcceptance(b *bytes.Buffer, required bool) error { + var err error + + if required { + _, err = fmt.Fprint(b, "Accepting this Permission is required in order to proceed.\n") + } + if !required { + _, err = fmt.Fprint(b, "Accepting this Permission is optional.\n") + } + if err != nil { + return nil + } + + _, err = fmt.Fprint(b, "Do you accept this Permission? [No,Yes]\n") + + return err +} + +func (opt BindAPIServiceOptions) promptYesNo(p kubebindv1alpha1.ExportPermissionClaim) (bool, error) { + + reader := bufio.NewReader(opt.Options.IOStreams.In) + + for { + if err := printPermissionClaim(opt.Options.Out, p); err != nil { + return false, err + } + + response, err := reader.ReadString('\n') + if err != nil { + return false, err + } + + response = strings.ToLower(strings.TrimSpace(response)) + if response == "y" || response == "yes" { + return true, nil + } else if response == "n" || response == "no" { + return false, nil + } + } +} diff --git a/pkg/kubectl/bind-apiservice/plugin/servicebindings_test.go b/pkg/kubectl/bind-apiservice/plugin/servicebindings_test.go new file mode 100644 index 000000000..d38aab910 --- /dev/null +++ b/pkg/kubectl/bind-apiservice/plugin/servicebindings_test.go @@ -0,0 +1,906 @@ +/* +Copyright 2023 The Kube Bind Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package plugin + +import ( + "bytes" + "os" + "testing" + + "k8s.io/cli-runtime/pkg/genericclioptions" + + kubebindv1alpha1 "github.com/kube-bind/kube-bind/pkg/apis/kubebind/v1alpha1" +) + +func TestHumanReadablePrompt(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + testData kubebindv1alpha1.ExportPermissionClaim + expectedOutput string + }{ + {"Owner=Provider", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,Required=false", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + }, + Required: false, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is optional.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,Selector.Names={foo}", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Names: []string{"bar"}, + Owner: kubebindv1alpha1.Provider, + }, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") which are referenced with:\n" + + "\t- name: \"bar\"\n" + + "on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,GroupResource.Group", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "example.com", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"example.com/v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,Selector.Names={bar},GroupResource.Group", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "example.com", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Names: []string{"bar"}, + Owner: kubebindv1alpha1.Provider, + }, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"example.com/v1\") which are referenced with:\n" + + "\t- name: \"bar\"\n" + + "on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,CreateOptions={}", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + Create: &kubebindv1alpha1.PermissionClaimCreateOptions{}, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,AutoDonate=false", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + OwnerTransfer: kubebindv1alpha1.OwnerTransferNone, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,AutoDonate=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + OwnerTransfer: kubebindv1alpha1.OwnerTransferDonate, + }, + Required: true, + }, + "The provider wants to create user owned foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,OnConflict={}", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + OnConflict: &kubebindv1alpha1.PermissionClaimOnConflictOptions{}, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,Create.ReplaceExisting=false", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + Create: &kubebindv1alpha1.PermissionClaimCreateOptions{ + ReplaceExisting: false, + }, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,Create.ReplaceExisting=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + Create: &kubebindv1alpha1.PermissionClaimCreateOptions{ + ReplaceExisting: true, + }, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Conflicting objects will be replaced by the provider. " + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,OnConflict.RecreateWhenConsumerSideDeleted=false", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + OnConflict: &kubebindv1alpha1.PermissionClaimOnConflictOptions{ + RecreateWhenConsumerSideDeleted: false, + }, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,OnConflict.RecreateWhenConsumerSideDeleted=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + OnConflict: &kubebindv1alpha1.PermissionClaimOnConflictOptions{ + RecreateWhenConsumerSideDeleted: true, + }, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Created objects will be recreated upon deletion. " + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,UpdateOptions={}", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{}, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,UpdateOptions.Fields", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{ + Fields: []string{"foo", "bar"}, + }, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "The following fields of the objects will still be able to be changed by the provider:\n" + // TODO + "\t\"foo\"\n" + + "\t\"bar\"\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,UpdateOptions.Preserving", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{ + Preserving: []string{"foo", "bar"}, + }, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "The following fields of the objects will be preserved by the provider:\n" + + "\t\"foo\"\n" + + "\t\"bar\"\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,UpdateOptions.AlwaysRecreate=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{ + AlwaysRecreate: true, + }, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Modification of said objects will by handled by deletion and recreation of said objects.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,UpdateOptions.Fields,AutoDonate=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{ + Fields: []string{"foo", "bar"}, + }, + OwnerTransfer: kubebindv1alpha1.OwnerTransferDonate, + }, + Required: true, + }, + "The provider wants to create user owned foo objects (apiVersion: \"v1\") on your cluster.\n" + + "The following fields of the objects will still be able to be changed by the provider:\n" + + "\t\"foo\"\n" + + "\t\"bar\"\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,UpdateOptions.Preserving,AutoDonate=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Provider, + }, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{ + Preserving: []string{"foo", "bar"}, + }, + OwnerTransfer: kubebindv1alpha1.OwnerTransferDonate, + }, + Required: true, + }, + "The provider wants to create user owned foo objects (apiVersion: \"v1\") on your cluster.\n" + + "The following fields of the objects will be preserved by the provider:\n" + + "\t\"foo\"\n" + + "\t\"bar\"\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Consumer, + }, + }, + Required: true, + }, + "The provider wants to read foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,Selector.Names={bar}", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Names: []string{"bar"}, + Owner: kubebindv1alpha1.Consumer, + }, + }, + Required: true, + }, + "The provider wants to read foo objects (apiVersion: \"v1\") which are referenced with:\n" + + "\t- name: \"bar\"\n" + + "on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,GroupResource.Group", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "example.com", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Consumer, + }, + }, + Required: true, + }, + "The provider wants to read foo objects (apiVersion: \"example.com/v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,Selector.Names={bar},GroupResource.Group", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "example.com", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Names: []string{"bar"}, + Owner: kubebindv1alpha1.Consumer, + }, + }, + Required: true, + }, + "The provider wants to read foo objects (apiVersion: \"example.com/v1\") which are referenced with:\n" + + "\t- name: \"bar\"\n" + + "on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,Adopt=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Consumer, + }, + OwnerTransfer: kubebindv1alpha1.OwnerTransferAdopt, + }, + Required: true, + }, + "The provider wants to have ownership of foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,Selector.Names={bar},Adopt=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Names: []string{"bar"}, + Owner: kubebindv1alpha1.Consumer, + }, + OwnerTransfer: kubebindv1alpha1.OwnerTransferAdopt, + }, + Required: true, + }, + "The provider wants to have ownership of foo objects (apiVersion: \"v1\") which are referenced with:\n" + + "\t- name: \"bar\"\n" + + "on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,OnConflict={}", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Consumer, + }, + OnConflict: &kubebindv1alpha1.PermissionClaimOnConflictOptions{}, + }, + Required: true, + }, + "The provider wants to read foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,Create.ReplaceExisting=false", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Consumer, + }, + Create: &kubebindv1alpha1.PermissionClaimCreateOptions{ + ReplaceExisting: false, + }, + }, + Required: true, + }, + "The provider wants to read foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,Create.ReplaceExisting=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Consumer, + }, + Create: &kubebindv1alpha1.PermissionClaimCreateOptions{ + ReplaceExisting: true, + }, + }, + Required: true, + }, + "The provider wants to read foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Conflicting objects will be replaced by the provider. " + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,UpdateOptions={}", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Consumer, + }, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{}, + }, + Required: true, + }, + "The provider wants to read foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,UpdateOptions.Fields", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Consumer, + }, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{ + Fields: []string{"foo", "bar"}, + }, + }, + Required: true, + }, + "The provider wants to read foo objects (apiVersion: \"v1\") on your cluster.\n" + + "The following fields of the objects will still be able to be changed by the provider:\n" + + "\t\"foo\"\n" + + "\t\"bar\"\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,UpdateOptions.Preserving", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Consumer, + }, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{ + Preserving: []string{"foo", "bar"}, + }, + }, + Required: true, + }, + "The provider wants to read foo objects (apiVersion: \"v1\") on your cluster.\n" + + "The following fields of the objects will be preserved by the provider:\n" + + "\t\"foo\"\n" + + "\t\"bar\"\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,UpdateOptions.AlwaysRecreate=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Consumer, + }, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{ + AlwaysRecreate: true, + }, + }, + Required: true, + }, + "The provider wants to read foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Modification of said objects will by handled by deletion and recreation of said objects.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,UpdateOptions.Fields,Adopt=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Consumer, + }, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{ + Fields: []string{"foo", "bar"}, + }, + OwnerTransfer: kubebindv1alpha1.OwnerTransferAdopt, + }, + Required: true, + }, + "The provider wants to have ownership of foo objects (apiVersion: \"v1\") on your cluster.\n" + + "The following fields of the objects will still be able to be changed by the provider:\n" + + "\t\"foo\"\n" + + "\t\"bar\"\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Consumer,UpdateOptions.Preserving,Adopt=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Owner: kubebindv1alpha1.Consumer, + }, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{ + Preserving: []string{"foo", "bar"}, + }, + OwnerTransfer: kubebindv1alpha1.OwnerTransferAdopt, + }, + Required: true, + }, + "The provider wants to have ownership of foo objects (apiVersion: \"v1\") on your cluster.\n" + + "The following fields of the objects will be preserved by the provider:\n" + " \"foo\"\n" + + "\t\"bar\"\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Selector={}", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{}, + }, + Required: true, + }, + "The provider wants to read and write foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Selector.Owner=\"\",Selector.Names={bar}", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Names: []string{"bar"}, + }, + }, + Required: true, + }, + "The provider wants to read and write foo objects (apiVersion: \"v1\") which are referenced with:\n" + + "\t- name: \"bar\"\n" + + "on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Selector={},AutoDonate=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{}, + OwnerTransfer: kubebindv1alpha1.OwnerTransferDonate, + }, + Required: true, + }, + "The provider wants to create user owned foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Selector={},AutoDonate=true,update.Fields=[\"spec\"]", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{}, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{ + Fields: []string{"spec"}, + }, + OwnerTransfer: kubebindv1alpha1.OwnerTransferDonate, + }, + }, + "The provider wants to create user owned foo objects (apiVersion: \"v1\") on your cluster.\n" + + "The following fields of the objects will still be able to be changed by the provider:\n" + + "\t\"spec\"\n" + + "Accepting this Permission is optional.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Selector={},adopt=true", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{}, + OwnerTransfer: kubebindv1alpha1.OwnerTransferAdopt, + }, + Required: true, + }, + "The provider wants to have ownership of foo objects (apiVersion: \"v1\") on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Selector={},adopt=true,update.Fields=[\"spec\"]", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{}, + Update: &kubebindv1alpha1.PermissionClaimUpdateOptions{ + Fields: []string{"spec"}, + }, + OwnerTransfer: kubebindv1alpha1.OwnerTransferAdopt, + }, + }, + "The provider wants to have ownership of foo objects (apiVersion: \"v1\") on your cluster.\n" + + "The following fields of the objects will still be able to be changed by the provider:\n" + + "\t\"spec\"\n" + + "Accepting this Permission is optional.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + {"Owner=Provider,Selector.Names={bar,baz}", + kubebindv1alpha1.ExportPermissionClaim{ + PermissionClaim: kubebindv1alpha1.PermissionClaim{ + GroupResource: kubebindv1alpha1.GroupResource{ + Group: "", + Resource: "foo", + }, + Version: "v1", + ObjectSelector: &kubebindv1alpha1.ObjectSelector{ + Names: []string{"bar", "baz"}, + Owner: kubebindv1alpha1.Provider, + }, + }, + Required: true, + }, + "The provider wants to write foo objects (apiVersion: \"v1\") which are referenced with:\n" + + "\t- name: \"bar\"\n" + + "\t- name: \"baz\"\n" + + "on your cluster.\n" + + "Accepting this Permission is required in order to proceed.\n" + + "Do you accept this Permission? [No,Yes]\n", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var output bytes.Buffer + var input bytes.Buffer + input.WriteString("y\n") + opts := NewBindAPIServiceOptions(genericclioptions.IOStreams{In: &input, Out: &output, ErrOut: os.Stderr}) + b, err := opts.promptYesNo(tt.testData) + if output.String() != tt.expectedOutput { + t.Errorf("Expected IO Output did not match. got: \"\n%s\"\nwanted: \"\n%s\"\n", output.String(), tt.expectedOutput) + } + if b == false || (err != nil) { + t.Errorf("Expected Return value did not match. got: \"%v\", \"%v\"", b, err) + } + }) + } +}