diff --git a/cmd/ks-apiserver/app/options/options.go b/cmd/ks-apiserver/app/options/options.go index 587622047727a44d8191d2006ab233c7c5baef68..0712abd23ae8783e19329b48e2e0c237f5b49967 100644 --- a/cmd/ks-apiserver/app/options/options.go +++ b/cmd/ks-apiserver/app/options/options.go @@ -66,6 +66,7 @@ func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) { s.GenericServerRunOptions.AddFlags(fs, s.GenericServerRunOptions) s.KubernetesOptions.AddFlags(fss.FlagSet("kubernetes"), s.KubernetesOptions) s.AuthenticationOptions.AddFlags(fss.FlagSet("authentication"), s.AuthenticationOptions) + s.AuthorizationOptions.AddFlags(fss.FlagSet("authorization"), s.AuthorizationOptions) s.DevopsOptions.AddFlags(fss.FlagSet("devops"), s.DevopsOptions) s.SonarQubeOptions.AddFlags(fss.FlagSet("sonarqube"), s.SonarQubeOptions) s.LdapOptions.AddFlags(fss.FlagSet("ldap"), s.LdapOptions) diff --git a/cmd/ks-apiserver/app/options/validation.go b/cmd/ks-apiserver/app/options/validation.go index 17b8c9fb51d1e23e354602a107c98d8a196cb945..b4627f1a858b17bdadf8cbadb472a9da1a0f75cf 100644 --- a/cmd/ks-apiserver/app/options/validation.go +++ b/cmd/ks-apiserver/app/options/validation.go @@ -15,6 +15,7 @@ func (s *ServerRunOptions) Validate() []error { errors = append(errors, s.OpenPitrixOptions.Validate()...) errors = append(errors, s.NetworkOptions.Validate()...) errors = append(errors, s.LoggingOptions.Validate()...) + errors = append(errors, s.AuthorizationOptions.Validate()...) return errors } diff --git a/config/crds/iam.kubesphere.io_rolebindings.yaml b/config/crds/iam.kubesphere.io_globalrolebindings.yaml similarity index 70% rename from config/crds/iam.kubesphere.io_rolebindings.yaml rename to config/crds/iam.kubesphere.io_globalrolebindings.yaml index ede871b227ba7a860d28349530db101487c4a7a4..1884d5a62197df8bba245f153b19c1249f60a2df 100644 --- a/config/crds/iam.kubesphere.io_rolebindings.yaml +++ b/config/crds/iam.kubesphere.io_globalrolebindings.yaml @@ -6,28 +6,17 @@ metadata: annotations: controller-gen.kubebuilder.io/version: (devel) creationTimestamp: null - name: rolebindings.iam.kubesphere.io + name: globalrolebindings.iam.kubesphere.io spec: - additionalPrinterColumns: - - JSONPath: .scope - name: Scope - type: string - - JSONPath: .roleRef.name - name: RoleRef - type: string - - JSONPath: .subjects[*].name - name: Subjects - type: string group: iam.kubesphere.io names: categories: - iam - kind: RoleBinding - listKind: RoleBindingList - plural: rolebindings - singular: rolebinding + kind: GlobalRoleBinding + listKind: GlobalRoleBindingList + plural: globalrolebindings + singular: globalrolebinding scope: Cluster - subresources: {} validation: openAPIV3Schema: description: RoleBinding is the Schema for the rolebindings API @@ -43,10 +32,11 @@ spec: 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: + description: Standard object's metadata. type: object roleRef: - description: RoleRef contains information that points to the role being - used + description: RoleRef can only reference a ClusterRole in the global namespace. + If the RoleRef cannot be resolved, the Authorizer must return an error. properties: apiGroup: description: APIGroup is the group for the resource being referenced @@ -62,15 +52,17 @@ spec: - kind - name type: object - scope: - type: string subjects: - description: Subjects holds references to the users the role applies to. + description: Subjects holds references to the objects the role applies to. items: - description: or a value for non-objects such as user and group names. + description: Subject contains a reference to the object or user identities + a role binding applies to. This can either hold a direct API object + reference, or a value for non-objects such as user and group names. properties: apiGroup: description: APIGroup holds the API group of the referenced subject. + Defaults to "" for ServiceAccount subjects. Defaults to "rbac.authorization.k8s.io" + for User and Group subjects. type: string kind: description: Kind of object being referenced. Values defined by this @@ -81,15 +73,18 @@ spec: name: description: Name of the object being referenced. type: string + namespace: + description: Namespace of the referenced object. If the object kind + is non-namespace, such as "User" or "Group", and this value is not + empty the Authorizer should report an error. + type: string required: - - apiGroup - kind - name type: object type: array required: - roleRef - - scope type: object version: v1alpha2 versions: diff --git a/config/crds/iam.kubesphere.io_globalroles.yaml b/config/crds/iam.kubesphere.io_globalroles.yaml new file mode 100644 index 0000000000000000000000000000000000000000..480516a6016bbb2ccdcffd4db5fd95d1896a7f97 --- /dev/null +++ b/config/crds/iam.kubesphere.io_globalroles.yaml @@ -0,0 +1,156 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: globalroles.iam.kubesphere.io +spec: + group: iam.kubesphere.io + names: + categories: + - iam + kind: GlobalRole + listKind: GlobalRoleList + plural: globalroles + singular: globalrole + scope: Cluster + validation: + openAPIV3Schema: + properties: + aggregationRule: + description: AggregationRule is an optional field that describes how to + build the Rules for this GlobalRole. If AggregationRule is set, then the + Rules are controller managed and direct changes to Rules will be stomped + by the controller. + properties: + roleSelectors: + description: ClusterRoleSelectors holds a list of selectors which will + be used to find ClusterRoles and create the rules. If any of the selectors + match, then the ClusterRole's permissions will be added + items: + description: A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + type: array + type: object + 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: + description: Standard object's metadata. + type: object + rules: + description: Rules holds all the PolicyRules for this ClusterRole + items: + description: PolicyRule holds information that describes a policy rule, + but does not contain information about who the rule applies to or which + namespace the rule applies to. + properties: + apiGroups: + description: APIGroups is the name of the APIGroup that contains the + resources. If multiple API groups are specified, any action requested + against one of the enumerated resources in any API group will be + allowed. + items: + type: string + type: array + nonResourceURLs: + description: NonResourceURLs is a set of partial urls that a user + should have access to. *s are allowed, but only as the full, final + step in the path Since non-resource URLs are not namespaced, this + field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. + Rules can either apply to API resources (such as "pods" or "secrets") + or non-resource URL paths (such as "/api"), but not both. + items: + type: string + type: array + resourceNames: + description: ResourceNames is an optional white list of names that + the rule applies to. An empty set means that everything is allowed. + items: + type: string + type: array + resources: + description: Resources is a list of resources this rule applies to. ResourceAll + represents all resources. + items: + type: string + type: array + verbs: + description: Verbs is a list of Verbs that apply to ALL the ResourceKinds + and AttributeRestrictions contained in this rule. VerbAll represents + all kinds. + items: + type: string + type: array + required: + - verbs + type: object + type: array + required: + - rules + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/iam.kubesphere.io_policyrules.yaml b/config/crds/iam.kubesphere.io_policyrules.yaml deleted file mode 100644 index c0eb6ed9c9df9f966fe33e819f6c7172c677ff13..0000000000000000000000000000000000000000 --- a/config/crds/iam.kubesphere.io_policyrules.yaml +++ /dev/null @@ -1,58 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: (devel) - creationTimestamp: null - name: policyrules.iam.kubesphere.io -spec: - additionalPrinterColumns: - - JSONPath: .scope - name: Scope - type: string - group: iam.kubesphere.io - names: - categories: - - iam - kind: PolicyRule - listKind: PolicyRuleList - plural: policyrules - singular: policyrule - scope: Cluster - subresources: {} - validation: - openAPIV3Schema: - 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 - rego: - type: string - scope: - type: string - required: - - rego - - scope - type: object - version: v1alpha2 - versions: - - name: v1alpha2 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crds/iam.kubesphere.io_roles.yaml b/config/crds/iam.kubesphere.io_roles.yaml deleted file mode 100644 index 91b01e128fd8a0902337ab5fbddfc11b3cfe9807..0000000000000000000000000000000000000000 --- a/config/crds/iam.kubesphere.io_roles.yaml +++ /dev/null @@ -1,87 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: (devel) - creationTimestamp: null - name: roles.iam.kubesphere.io -spec: - additionalPrinterColumns: - - JSONPath: .target.scope - name: Scope - type: string - - JSONPath: .target.name - name: Target - type: string - group: iam.kubesphere.io - names: - categories: - - iam - kind: Role - listKind: RoleList - plural: roles - singular: role - scope: Cluster - subresources: {} - validation: - openAPIV3Schema: - 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 - rules: - items: - description: RuleRef contains information that points to the role being - used - properties: - apiGroup: - description: APIGroup is the group for the resource being referenced - type: string - kind: - description: Kind is the type of resource being referenced - type: string - name: - description: Name is the name of resource being referenced - type: string - required: - - apiGroup - - kind - - name - type: object - type: array - target: - properties: - name: - type: string - scope: - type: string - required: - - name - - scope - type: object - required: - - rules - - target - type: object - version: v1alpha2 - versions: - - name: v1alpha2 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crds/iam.kubesphere.io_users.yaml b/config/crds/iam.kubesphere.io_users.yaml index eec35e012b4d550a7b0d9f119652f897ad5084ce..ce60e7e355330f7b6541de34a868d6e4ed86630f 100644 --- a/config/crds/iam.kubesphere.io_users.yaml +++ b/config/crds/iam.kubesphere.io_users.yaml @@ -40,6 +40,7 @@ spec: 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: + description: Standard object's metadata. type: object spec: description: UserSpec defines the desired state of User @@ -66,6 +67,7 @@ spec: description: The preferred written or spoken language for the user. type: string password: + description: password will be encrypted by mutating admission webhook type: string required: - email diff --git a/config/crds/iam.kubesphere.io_workspacerolebindings.yaml b/config/crds/iam.kubesphere.io_workspacerolebindings.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c06e75f2fa46fba7ed6c18f6d663e6fc1d8a40c9 --- /dev/null +++ b/config/crds/iam.kubesphere.io_workspacerolebindings.yaml @@ -0,0 +1,103 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: workspacerolebindings.iam.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .metadata.labels.kubesphere\.io/workspace + name: Workspace + type: string + group: iam.kubesphere.io + names: + categories: + - iam + kind: WorkspaceRoleBinding + listKind: WorkspaceRoleBindingList + plural: workspacerolebindings + singular: workspacerolebinding + scope: Cluster + subresources: {} + validation: + openAPIV3Schema: + description: RoleBinding is the Schema for the rolebindings API + 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 + roleRef: + description: RoleRef can only reference a ClusterRole in the global namespace. + If the RoleRef cannot be resolved, the Authorizer must return an error. + properties: + apiGroup: + description: APIGroup is the group for the resource being referenced + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - apiGroup + - kind + - name + type: object + subjects: + description: Subjects holds references to the objects the role applies to. + items: + description: Subject contains a reference to the object or user identities + a role binding applies to. This can either hold a direct API object + reference, or a value for non-objects such as user and group names. + properties: + apiGroup: + description: APIGroup holds the API group of the referenced subject. + Defaults to "" for ServiceAccount subjects. Defaults to "rbac.authorization.k8s.io" + for User and Group subjects. + type: string + kind: + description: Kind of object being referenced. Values defined by this + API group are "User", "Group", and "ServiceAccount". If the Authorizer + does not recognized the kind value, the Authorizer should report + an error. + type: string + name: + description: Name of the object being referenced. + type: string + namespace: + description: Namespace of the referenced object. If the object kind + is non-namespace, such as "User" or "Group", and this value is not + empty the Authorizer should report an error. + type: string + required: + - kind + - name + type: object + type: array + required: + - roleRef + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/iam.kubesphere.io_workspaceroles.yaml b/config/crds/iam.kubesphere.io_workspaceroles.yaml new file mode 100644 index 0000000000000000000000000000000000000000..09d8b0b7d69a4440a09a4a786515b88946dd6d6d --- /dev/null +++ b/config/crds/iam.kubesphere.io_workspaceroles.yaml @@ -0,0 +1,164 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: workspaceroles.iam.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .metadata.labels.kubesphere\.io/workspace + name: Workspace + type: string + - JSONPath: .metadata.labels.kubesphere\.io/alias-name + name: Alias + type: string + group: iam.kubesphere.io + names: + categories: + - iam + kind: WorkspaceRole + listKind: WorkspaceRoleList + plural: workspaceroles + singular: workspacerole + scope: Cluster + subresources: {} + validation: + openAPIV3Schema: + properties: + aggregationRule: + description: AggregationRule is an optional field that describes how to + build the Rules for this WorkspaceRole. If AggregationRule is set, then + the Rules are controller managed and direct changes to Rules will be stomped + by the controller. + properties: + roleSelectors: + description: ClusterRoleSelectors holds a list of selectors which will + be used to find ClusterRoles and create the rules. If any of the selectors + match, then the ClusterRole's permissions will be added + items: + description: A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + type: array + type: object + 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: + description: Standard object's metadata. + type: object + rules: + description: Rules holds all the PolicyRules for this ClusterRole + items: + description: PolicyRule holds information that describes a policy rule, + but does not contain information about who the rule applies to or which + namespace the rule applies to. + properties: + apiGroups: + description: APIGroups is the name of the APIGroup that contains the + resources. If multiple API groups are specified, any action requested + against one of the enumerated resources in any API group will be + allowed. + items: + type: string + type: array + nonResourceURLs: + description: NonResourceURLs is a set of partial urls that a user + should have access to. *s are allowed, but only as the full, final + step in the path Since non-resource URLs are not namespaced, this + field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. + Rules can either apply to API resources (such as "pods" or "secrets") + or non-resource URL paths (such as "/api"), but not both. + items: + type: string + type: array + resourceNames: + description: ResourceNames is an optional white list of names that + the rule applies to. An empty set means that everything is allowed. + items: + type: string + type: array + resources: + description: Resources is a list of resources this rule applies to. ResourceAll + represents all resources. + items: + type: string + type: array + verbs: + description: Verbs is a list of Verbs that apply to ALL the ResourceKinds + and AttributeRestrictions contained in this rule. VerbAll represents + all kinds. + items: + type: string + type: array + required: + - verbs + type: object + type: array + required: + - rules + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/samples/iam_v1alpha2_globalrole.yaml b/config/samples/iam_v1alpha2_globalrole.yaml new file mode 100644 index 0000000000000000000000000000000000000000..268374d0687a1f90e688ac8ae69b69d883b414cb --- /dev/null +++ b/config/samples/iam_v1alpha2_globalrole.yaml @@ -0,0 +1,14 @@ +apiVersion: iam.kubesphere.io/v1alpha2 +kind: GlobalRole +metadata: + labels: + controller-tools.k8s.io: "1.0" + name: global-admin +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' + diff --git a/config/samples/iam_v1alpha2_rolebinding.yaml b/config/samples/iam_v1alpha2_globalrolebinding.yaml similarity index 71% rename from config/samples/iam_v1alpha2_rolebinding.yaml rename to config/samples/iam_v1alpha2_globalrolebinding.yaml index 3caf4fa87a750ab09460174abfb4c5a0b653fc91..11550dcdc3e42cf3f6def7ffdb290807c81ea9c2 100644 --- a/config/samples/iam_v1alpha2_rolebinding.yaml +++ b/config/samples/iam_v1alpha2_globalrolebinding.yaml @@ -1,14 +1,13 @@ apiVersion: iam.kubesphere.io/v1alpha2 -kind: RoleBinding +kind: GlobalRoleBinding metadata: labels: controller-tools.k8s.io: "1.0" - name: cluster-admin -scope: Global + name: admin roleRef: apiGroup: iam.kubesphere.io/v1alpha2 - kind: Role - name: cluster-admin + kind: GlobalRole + name: global-admin subjects: - apiGroup: iam.kubesphere.io/v1alpha2 kind: User diff --git a/config/samples/iam_v1alpha2_policyrule.yaml b/config/samples/iam_v1alpha2_policyrule.yaml deleted file mode 100644 index d22ad3e5b10b3f61f74e0a595bd502dc3608dd73..0000000000000000000000000000000000000000 --- a/config/samples/iam_v1alpha2_policyrule.yaml +++ /dev/null @@ -1,55 +0,0 @@ -apiVersion: iam.kubesphere.io/v1alpha2 -kind: PolicyRule -metadata: - labels: - controller-tools.k8s.io: "1.0" - name: always-allow -scope: Global -rego: 'package authz\ndefault allow = true' - ---- - -apiVersion: iam.kubesphere.io/v1alpha2 -kind: PolicyRule -metadata: - labels: - controller-tools.k8s.io: "1.0" - name: always-deny -scope: Global -rego: | - package authz - default allow = false - ---- - -apiVersion: iam.kubesphere.io/v1alpha2 -kind: PolicyRule -metadata: - labels: - controller-tools.k8s.io: "1.0" - name: cluster-manage -scope: Global -rego: | - package authz - default allow = false - allow { - input.Resource == 'clusters' - } - ---- - -apiVersion: iam.kubesphere.io/v1alpha2 -kind: PolicyRule -metadata: - labels: - controller-tools.k8s.io: "1.0" - name: some-namespace-manage -scope: Namespace -rego: | - package authz - default allow = false - allow { - input.Resource == 'clusters' - } - - diff --git a/config/samples/iam_v1alpha2_role.yaml b/config/samples/iam_v1alpha2_role.yaml deleted file mode 100644 index a85cad08d0c9cb3b45a2018aab36b9d839b6ba63..0000000000000000000000000000000000000000 --- a/config/samples/iam_v1alpha2_role.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: iam.kubesphere.io/v1alpha2 -kind: Role -metadata: - labels: - controller-tools.k8s.io: "1.0" - name: cluster-admin -target: - scope: Global - name: '' -rules: - - apiGroup: iam.kubesphere.io/v1alpha2 - kind: PolicyRule - name: always-allow - ---- - -apiVersion: iam.kubesphere.io/v1alpha2 -kind: Role -metadata: - labels: - controller-tools.k8s.io: "1.0" - name: anonymous -target: - scope: Global - name: '' -rules: - - apiGroup: iam.kubesphere.io/v1alpha2 - kind: PolicyRule - name: always-deny - diff --git a/go.mod b/go.mod index 3f05a0dec8f01d25dff0ec0bbbffe37f59be341e..a60c213d4132f9e64b1a8c631db6573692049787 100644 --- a/go.mod +++ b/go.mod @@ -81,7 +81,7 @@ require ( github.com/syndtr/goleveldb v1.0.0 // indirect github.com/xanzy/ssh-agent v0.2.1 // indirect golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 - golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 + golang.org/x/net v0.0.0-20190923162816-aa69164e4478 google.golang.org/grpc v1.23.1 gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect gopkg.in/go-playground/validator.v9 v9.29.1 // indirect @@ -141,10 +141,8 @@ replace ( github.com/bitly/go-simplejson => github.com/bitly/go-simplejson v0.5.0 github.com/blang/semver => github.com/blang/semver v3.5.0+incompatible github.com/bmizerany/assert => github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 - github.com/cenkalti/backoff => github.com/cenkalti/backoff v2.2.1+incompatible github.com/cespare/xxhash => github.com/cespare/xxhash v1.1.0 github.com/chai2010/gettext-go => github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 - github.com/cheekybits/genny => github.com/cheekybits/genny v1.0.0 github.com/client9/misspell => github.com/client9/misspell v0.3.4 github.com/coreos/bbolt => github.com/coreos/bbolt v1.3.3 github.com/coreos/etcd => github.com/coreos/etcd v3.3.17+incompatible @@ -158,14 +156,12 @@ replace ( github.com/deckarep/golang-set => github.com/deckarep/golang-set v1.7.1 github.com/denisenkom/go-mssqldb => github.com/denisenkom/go-mssqldb v0.0.0-20190204142019-df6d76eb9289 github.com/dgrijalva/jwt-go => github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/dgryski/go-sip13 => github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 github.com/docker/distribution => github.com/docker/distribution v2.7.1+incompatible github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190822205725-ed20165a37b4 github.com/docker/go-connections => github.com/docker/go-connections v0.3.0 github.com/docker/go-units => github.com/docker/go-units v0.3.3 github.com/docker/spdystream => github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c github.com/docopt/docopt-go => github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 - github.com/dustin/go-humanize => github.com/dustin/go-humanize v1.0.0 github.com/elastic/go-elasticsearch/v5 => github.com/elastic/go-elasticsearch/v5 v5.6.1 github.com/elastic/go-elasticsearch/v6 => github.com/elastic/go-elasticsearch/v6 v6.8.2 github.com/elastic/go-elasticsearch/v7 => github.com/elastic/go-elasticsearch/v7 v7.3.0 @@ -185,7 +181,6 @@ replace ( github.com/ghodss/yaml => github.com/ghodss/yaml v1.0.0 github.com/gliderlabs/ssh => github.com/gliderlabs/ssh v0.1.1 github.com/globalsign/mgo => github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 - github.com/go-acme/lego => github.com/go-acme/lego v2.5.0+incompatible github.com/go-kit/kit => github.com/go-kit/kit v0.8.0 github.com/go-ldap/ldap => github.com/go-ldap/ldap v3.0.3+incompatible github.com/go-logfmt/logfmt => github.com/go-logfmt/logfmt v0.4.0 @@ -238,7 +233,6 @@ replace ( github.com/grpc-ecosystem/go-grpc-middleware => github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 github.com/grpc-ecosystem/go-grpc-prometheus => github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/grpc-gateway => github.com/grpc-ecosystem/grpc-gateway v1.9.6 - github.com/hashicorp/go-syslog => github.com/hashicorp/go-syslog v1.0.0 github.com/hashicorp/go-version => github.com/hashicorp/go-version v1.2.0 github.com/hashicorp/golang-lru => github.com/hashicorp/golang-lru v0.5.3 github.com/hashicorp/hcl => github.com/hashicorp/hcl v1.0.0 @@ -247,7 +241,6 @@ replace ( github.com/inconshreveable/mousetrap => github.com/inconshreveable/mousetrap v1.0.0 github.com/jbenet/go-context => github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 github.com/jessevdk/go-flags => github.com/jessevdk/go-flags v1.4.0 - github.com/jimstudt/http-authentication => github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a github.com/jinzhu/gorm => github.com/jinzhu/gorm v1.9.2 github.com/jinzhu/inflection => github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a github.com/jinzhu/now => github.com/jinzhu/now v1.0.0 @@ -263,7 +256,6 @@ replace ( github.com/kiali/kiali => github.com/kubesphere/kiali v0.15.1-0.20191210080139-edbbad1ef779 github.com/kisielk/errcheck => github.com/kisielk/errcheck v1.2.0 github.com/kisielk/gotool => github.com/kisielk/gotool v1.0.0 - github.com/klauspost/cpuid => github.com/klauspost/cpuid v1.2.1 github.com/koding/multiconfig => github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 github.com/konsorten/go-windows-terminal-sequences => github.com/konsorten/go-windows-terminal-sequences v1.0.2 github.com/kr/logfmt => github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 @@ -271,26 +263,18 @@ replace ( github.com/kr/pty => github.com/kr/pty v1.1.5 github.com/kr/text => github.com/kr/text v0.1.0 github.com/kubernetes-sigs/application => github.com/kubesphere/application v0.0.0-20191210100950-18cc93526ab4 - github.com/kubernetes-sigs/federation-v2 => github.com/kubernetes-sigs/federation-v2 v0.0.10 - github.com/kubesphere/s2ioperator => github.com/kubesphere/s2ioperator v0.0.14 github.com/kubesphere/sonargo => github.com/kubesphere/sonargo v0.0.2 - github.com/kylelemons/godebug => github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 github.com/leodido/go-urn => github.com/leodido/go-urn v1.1.0 github.com/lib/pq => github.com/lib/pq v1.2.0 github.com/liggitt/tabwriter => github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de github.com/lithammer/dedent => github.com/lithammer/dedent v1.1.0 - github.com/lucas-clemente/quic-go => github.com/lucas-clemente/quic-go v0.11.1 github.com/magiconair/properties => github.com/magiconair/properties v1.8.0 github.com/mailru/easyjson => github.com/mailru/easyjson v0.7.0 - github.com/marten-seemann/qtls => github.com/marten-seemann/qtls v0.2.3 github.com/mattn/go-colorable => github.com/mattn/go-colorable v0.1.2 github.com/mattn/go-isatty => github.com/mattn/go-isatty v0.0.8 github.com/mattn/go-runewidth => github.com/mattn/go-runewidth v0.0.0-20181025052659-b20a3daf6a39 github.com/mattn/go-sqlite3 => github.com/mattn/go-sqlite3 v1.11.0 github.com/matttproud/golang_protobuf_extensions => github.com/matttproud/golang_protobuf_extensions v1.0.1 - github.com/mholt/caddy => github.com/mholt/caddy v1.0.0 - github.com/mholt/certmagic => github.com/mholt/certmagic v0.5.1 - github.com/miekg/dns => github.com/miekg/dns v1.1.9 github.com/mitchellh/go-homedir => github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-wordwrap => github.com/mitchellh/go-wordwrap v1.0.0 github.com/mitchellh/mapstructure => github.com/mitchellh/mapstructure v1.1.2 @@ -301,9 +285,6 @@ replace ( github.com/munnerz/goautoneg => github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d github.com/mwitkow/go-conntrack => github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 github.com/mxk/go-flowrate => github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f - github.com/naoina/go-stringutil => github.com/naoina/go-stringutil v0.1.0 - github.com/naoina/toml => github.com/naoina/toml v0.1.1 - github.com/oklog/ulid => github.com/oklog/ulid v1.3.1 github.com/olekukonko/tablewriter => github.com/olekukonko/tablewriter v0.0.1 github.com/onsi/ginkgo => github.com/onsi/ginkgo v1.8.0 github.com/onsi/gomega => github.com/onsi/gomega v1.5.0 @@ -311,7 +292,6 @@ replace ( github.com/opencontainers/go-digest => github.com/opencontainers/go-digest v1.0.0-rc1 github.com/opencontainers/image-spec => github.com/opencontainers/image-spec v1.0.1 github.com/openshift/api => github.com/openshift/api v0.0.0-20180801171038-322a19404e37 - github.com/openshift/build-machinery-go => github.com/openshift/build-machinery-go v0.0.0-20200211121458-5e3d6e570160 github.com/openshift/generic-admission-server => github.com/openshift/generic-admission-server v1.14.0 github.com/opentracing/opentracing-go => github.com/opentracing/opentracing-go v1.1.0 github.com/pborman/uuid => github.com/pborman/uuid v1.2.0 @@ -333,7 +313,6 @@ replace ( github.com/prometheus/common => github.com/prometheus/common v0.4.0 github.com/prometheus/procfs => github.com/prometheus/procfs v0.0.2 github.com/prometheus/prometheus => github.com/prometheus/prometheus v1.8.2 - github.com/prometheus/tsdb => github.com/prometheus/tsdb v0.7.1 github.com/rcrowley/go-metrics => github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a github.com/remyoudompheng/bigfft => github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446 github.com/rogpeppe/fastuuid => github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af @@ -400,7 +379,6 @@ replace ( gopkg.in/go-playground/assert.v1 => gopkg.in/go-playground/assert.v1 v1.2.1 gopkg.in/go-playground/validator.v9 => gopkg.in/go-playground/validator.v9 v9.29.1 gopkg.in/inf.v0 => gopkg.in/inf.v0 v0.9.1 - gopkg.in/mcuadros/go-syslog.v2 => gopkg.in/mcuadros/go-syslog.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 => gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/resty.v1 => gopkg.in/resty.v1 v1.12.0 gopkg.in/square/go-jose.v2 => gopkg.in/square/go-jose.v2 v2.3.1 @@ -424,7 +402,6 @@ replace ( k8s.io/apiserver => k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682 k8s.io/cli-runtime => k8s.io/cli-runtime v0.17.3 k8s.io/client-go => k8s.io/client-go v0.0.0-20191114101535-6c5935290e33 - k8s.io/cluster-registry => k8s.io/cluster-registry v0.0.6 k8s.io/code-generator => k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894 k8s.io/component-base => k8s.io/component-base v0.0.0-20191114102325-35a9586014f7 k8s.io/gengo => k8s.io/gengo v0.0.0-20191120174120-e74f70b9b27e diff --git a/pkg/apis/iam/v1alpha2/register.go b/pkg/apis/iam/v1alpha2/register.go index 69c539720ed20b3a04d7c96f7e9f1981bc93865d..ddb23db84078e9c6b7298c8382bc45766f2d97f7 100644 --- a/pkg/apis/iam/v1alpha2/register.go +++ b/pkg/apis/iam/v1alpha2/register.go @@ -51,12 +51,14 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &User{}, &UserList{}, - &Role{}, - &RoleList{}, - &RoleBinding{}, - &RoleBindingList{}, - &PolicyRule{}, - &PolicyRuleList{}, + &GlobalRole{}, + &GlobalRoleList{}, + &GlobalRoleBinding{}, + &GlobalRoleBindingList{}, + &WorkspaceRole{}, + &WorkspaceRoleList{}, + &WorkspaceRoleBinding{}, + &WorkspaceRoleBindingList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/pkg/apis/iam/v1alpha2/types.go b/pkg/apis/iam/v1alpha2/types.go index 872bac1c8f897547dd97398dac408d495f51ed6c..20267a7d27395fedac8fc7828635c397b73508ac 100644 --- a/pkg/apis/iam/v1alpha2/types.go +++ b/pkg/apis/iam/v1alpha2/types.go @@ -17,9 +17,39 @@ limitations under the License. package v1alpha2 import ( + rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +const ( + ResourceKindUser = "User" + ResourcesSingularUser = "user" + ResourcesPluralUser = "users" + ResourceKindGlobalRoleBinding = "GlobalRoleBinding" + ResourcesSingularGlobalRoleBinding = "globalrolebinding" + ResourcesPluralGlobalRoleBinding = "globalrolebindings" + ResourceKindGlobalRole = "GlobalRole" + ResourcesSingularGlobalRole = "globalrole" + ResourcesPluralGlobalRole = "globalroles" + ResourceKindWorkspaceRoleBinding = "WorkspaceRoleBinding" + ResourcesSingularWorkspaceRoleBinding = "workspacerolebinding" + ResourcesPluralWorkspaceRoleBinding = "workspacerolebindings" + ResourceKindWorkspaceRole = "WorkspaceRole" + ResourcesSingularWorkspaceRole = "workspacerole" + ResourcesPluralWorkspaceRole = "workspaceroles" + ResourceKindClusterRole = "ClusterRole" + ResourcesSingularClusterRole = "clusterrole" + ResourcesPluralClusterRole = "clusterroles" + ResourceKindRole = "Role" + ResourcesSingularRole = "role" + ResourcesPluralRole = "roles" + RegoOverrideAnnotation = "iam.kubesphere.io/rego-override" + GlobalScope = "Global" + ClusterScope = "Cluster" + WorkspaceScope = "Workspace" + NamespaceScope = "Namespace" +) + // +genclient // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -30,7 +60,9 @@ import ( // +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.state" // +kubebuilder:resource:categories="iam",scope="Cluster" type User struct { - metav1.TypeMeta `json:",inline"` + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional metav1.ObjectMeta `json:"metadata,omitempty"` Spec UserSpec `json:"spec"` @@ -53,9 +85,9 @@ type UserSpec struct { // +optional DisplayName string `json:"displayName,omitempty"` // +optional - Groups []string `json:"groups,omitempty"` - EncryptedPassword string `json:"password"` - + Groups []string `json:"groups,omitempty"` + // password will be encrypted by mutating admission webhook + EncryptedPassword string `json:"password"` // Finalizers is an opaque list of values that must be empty to permanently remove object from storage. // +optional Finalizers []FinalizerName `json:"finalizers,omitempty"` @@ -102,7 +134,7 @@ type UserConditionType string // These are valid conditions of a user. const ( // UserLoginFailure contains information about user login. - UserLoginFailure UserConditionType = "UserLoginFailure" + LoginFailure UserConditionType = "LoginFailure" ) type ConditionStatus string @@ -121,6 +153,8 @@ const ( // UserList contains a list of User type UserList struct { metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional metav1.ListMeta `json:"metadata,omitempty"` Items []User `json:"items"` } @@ -129,128 +163,131 @@ type UserList struct { // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:printcolumn:name="Scope",type="string",JSONPath=".target.scope" -// +kubebuilder:printcolumn:name="Target",type="string",JSONPath=".target.name" // +kubebuilder:resource:categories="iam",scope="Cluster" -type Role struct { - metav1.TypeMeta `json:",inline"` +type GlobalRole struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional metav1.ObjectMeta `json:"metadata,omitempty"` - Target Target `json:"target"` - Rules []RuleRef `json:"rules"` + // Rules holds all the PolicyRules for this ClusterRole + Rules []rbacv1.PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"` + + // AggregationRule is an optional field that describes how to build the Rules for this GlobalRole. + // If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be + // stomped by the controller. + AggregationRule *AggregationRule `json:"aggregationRule,omitempty" protobuf:"bytes,3,opt,name=aggregationRule"` } -type Target struct { - Scope Scope `json:"scope"` - Name string `json:"name"` +// AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole +type AggregationRule struct { + // ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. + // If any of the selectors match, then the ClusterRole's permissions will be added + // +optional + RoleSelectors []metav1.LabelSelector `json:"roleSelectors,omitempty" protobuf:"bytes,1,rep,name=roleSelectors"` } -type Scope string +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -const ( - GlobalScope Scope = "Global" - ClusterScope Scope = "Cluster" - WorkspaceScope Scope = "Workspace" - NamespaceScope Scope = "Namespace" - UserKind = "User" - PolicyRuleKind = "PolicyRule" - RoleKind = "Role" - RoleBindingKind = "RoleBinding" -) +// GlobalRoleList contains a list of GlobalRole +type GlobalRoleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []GlobalRole `json:"items"` +} -// RuleRef contains information that points to the role being used -type RuleRef struct { - // APIGroup is the group for the resource being referenced - APIGroup string `json:"apiGroup"` - // Kind is the type of resource being referenced - Kind string `json:"kind"` - // Name is the name of resource being referenced - Name string `json:"name"` +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// GlobalRoleBinding is the Schema for the globalrolebindings API +// +kubebuilder:resource:categories="iam",scope="Cluster" +type GlobalRoleBinding struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Subjects holds references to the objects the role applies to. + // +optional + Subjects []rbacv1.Subject `json:"subjects,omitempty" protobuf:"bytes,2,rep,name=subjects"` + + // RoleRef can only reference a ClusterRole in the global namespace. + // If the RoleRef cannot be resolved, the Authorizer must return an error. + RoleRef rbacv1.RoleRef `json:"roleRef" protobuf:"bytes,3,opt,name=roleRef"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// RoleList contains a list of Role -type RoleList struct { +// GlobalRoleBindingList contains a list of GlobalRoleBinding +type GlobalRoleBindingList struct { metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional metav1.ListMeta `json:"metadata,omitempty"` - Items []Role `json:"items"` + Items []GlobalRoleBinding `json:"items"` } // +genclient // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:printcolumn:name="Scope",type="string",JSONPath=".scope" +// +kubebuilder:printcolumn:name="Workspace",type="string",JSONPath=".metadata.labels.kubesphere\\.io/workspace" +// +kubebuilder:printcolumn:name="Alias",type="string",JSONPath=".metadata.labels.kubesphere\\.io/alias-name" // +kubebuilder:resource:categories="iam",scope="Cluster" -type PolicyRule struct { - metav1.TypeMeta `json:",inline"` +type WorkspaceRole struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional metav1.ObjectMeta `json:"metadata,omitempty"` - Scope Scope `json:"scope"` - Rego string `json:"rego"` + // Rules holds all the PolicyRules for this ClusterRole + Rules []rbacv1.PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"` + // AggregationRule is an optional field that describes how to build the Rules for this WorkspaceRole. + // If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be + // stomped by the controller. + AggregationRule *AggregationRule `json:"aggregationRule,omitempty" protobuf:"bytes,3,opt,name=aggregationRule"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// PolicyRuleList contains a list of PolicyRule -type PolicyRuleList struct { +// WorkspaceRoleList contains a list of WorkspaceRole +type WorkspaceRoleList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []PolicyRule `json:"items"` + Items []WorkspaceRole `json:"items"` } // +genclient // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// RoleBinding is the Schema for the rolebindings API -// +kubebuilder:printcolumn:name="Scope",type="string",JSONPath=".scope" -// +kubebuilder:printcolumn:name="RoleRef",type="string",JSONPath=".roleRef.name" -// +kubebuilder:printcolumn:name="Subjects",type="string",JSONPath=".subjects[*].name" +// WorkspaceRoleBinding is the Schema for the workspacerolebindings API +// +kubebuilder:printcolumn:name="Workspace",type="string",JSONPath=".metadata.labels.kubesphere\\.io/workspace" // +kubebuilder:resource:categories="iam",scope="Cluster" -type RoleBinding struct { +type WorkspaceRoleBinding struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Scope Scope `json:"scope"` - RoleRef RoleRef `json:"roleRef"` - // Subjects holds references to the users the role applies to. + // Subjects holds references to the objects the role applies to. // +optional - Subjects []Subject `json:"subjects,omitempty"` -} - -// RoleRef contains information that points to the role being used -type RoleRef struct { - // APIGroup is the group for the resource being referenced - APIGroup string `json:"apiGroup"` - // Kind is the type of resource being referenced - Kind string `json:"kind"` - // Name is the name of resource being referenced - Name string `json:"name"` -} + Subjects []rbacv1.Subject `json:"subjects,omitempty" protobuf:"bytes,2,rep,name=subjects"` -// or a value for non-objects such as user and group names. -type Subject struct { - // Kind of object being referenced. Values defined by this API group are "User", "Group", and "ServiceAccount". - // If the Authorizer does not recognized the kind value, the Authorizer should report an error. - Kind string `json:"kind"` - // APIGroup holds the API group of the referenced subject. - APIGroup string `json:"apiGroup"` - // Name of the object being referenced. - Name string `json:"name"` + // RoleRef can only reference a ClusterRole in the global namespace. + // If the RoleRef cannot be resolved, the Authorizer must return an error. + RoleRef rbacv1.RoleRef `json:"roleRef" protobuf:"bytes,3,opt,name=roleRef"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// RoleBindingList contains a list of RoleBinding -type RoleBindingList struct { +// WorkspaceRoleBindingList contains a list of WorkspaceRoleBinding +type WorkspaceRoleBindingList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []RoleBinding `json:"items"` + Items []WorkspaceRoleBinding `json:"items"` } type UserDetail struct { *User - GlobalRole *Role `json:"globalRole"` + GlobalRole *GlobalRole `json:"globalRole"` } diff --git a/pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go index 8ac0e86bc75c57f6867d6e585812214176359fa9..8b27972de91be5bec33ecd19c6935fa5a6d2f2fd 100644 --- a/pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go +++ b/pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go @@ -21,91 +21,64 @@ limitations under the License. package v1alpha2 import ( + "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PolicyRule) DeepCopyInto(out *PolicyRule) { +func (in *AggregationRule) DeepCopyInto(out *AggregationRule) { *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyRule. -func (in *PolicyRule) DeepCopy() *PolicyRule { - if in == nil { - return nil - } - out := new(PolicyRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PolicyRule) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PolicyRuleList) DeepCopyInto(out *PolicyRuleList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]PolicyRule, len(*in)) + if in.RoleSelectors != nil { + in, out := &in.RoleSelectors, &out.RoleSelectors + *out = make([]metav1.LabelSelector, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyRuleList. -func (in *PolicyRuleList) DeepCopy() *PolicyRuleList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AggregationRule. +func (in *AggregationRule) DeepCopy() *AggregationRule { if in == nil { return nil } - out := new(PolicyRuleList) + out := new(AggregationRule) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PolicyRuleList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Role) DeepCopyInto(out *Role) { +func (in *GlobalRole) DeepCopyInto(out *GlobalRole) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Target = in.Target if in.Rules != nil { in, out := &in.Rules, &out.Rules - *out = make([]RuleRef, len(*in)) - copy(*out, *in) + *out = make([]v1.PolicyRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AggregationRule != nil { + in, out := &in.AggregationRule, &out.AggregationRule + *out = new(AggregationRule) + (*in).DeepCopyInto(*out) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Role. -func (in *Role) DeepCopy() *Role { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRole. +func (in *GlobalRole) DeepCopy() *GlobalRole { if in == nil { return nil } - out := new(Role) + out := new(GlobalRole) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Role) DeepCopyObject() runtime.Object { +func (in *GlobalRole) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -113,30 +86,30 @@ func (in *Role) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RoleBinding) DeepCopyInto(out *RoleBinding) { +func (in *GlobalRoleBinding) DeepCopyInto(out *GlobalRoleBinding) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.RoleRef = in.RoleRef if in.Subjects != nil { in, out := &in.Subjects, &out.Subjects - *out = make([]Subject, len(*in)) + *out = make([]v1.Subject, len(*in)) copy(*out, *in) } + out.RoleRef = in.RoleRef } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleBinding. -func (in *RoleBinding) DeepCopy() *RoleBinding { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRoleBinding. +func (in *GlobalRoleBinding) DeepCopy() *GlobalRoleBinding { if in == nil { return nil } - out := new(RoleBinding) + out := new(GlobalRoleBinding) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RoleBinding) DeepCopyObject() runtime.Object { +func (in *GlobalRoleBinding) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -144,31 +117,31 @@ func (in *RoleBinding) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RoleBindingList) DeepCopyInto(out *RoleBindingList) { +func (in *GlobalRoleBindingList) DeepCopyInto(out *GlobalRoleBindingList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]RoleBinding, len(*in)) + *out = make([]GlobalRoleBinding, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleBindingList. -func (in *RoleBindingList) DeepCopy() *RoleBindingList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRoleBindingList. +func (in *GlobalRoleBindingList) DeepCopy() *GlobalRoleBindingList { if in == nil { return nil } - out := new(RoleBindingList) + out := new(GlobalRoleBindingList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RoleBindingList) DeepCopyObject() runtime.Object { +func (in *GlobalRoleBindingList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -176,97 +149,37 @@ func (in *RoleBindingList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RoleList) DeepCopyInto(out *RoleList) { +func (in *GlobalRoleList) DeepCopyInto(out *GlobalRoleList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]Role, len(*in)) + *out = make([]GlobalRole, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleList. -func (in *RoleList) DeepCopy() *RoleList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRoleList. +func (in *GlobalRoleList) DeepCopy() *GlobalRoleList { if in == nil { return nil } - out := new(RoleList) + out := new(GlobalRoleList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RoleList) DeepCopyObject() runtime.Object { +func (in *GlobalRoleList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RoleRef) DeepCopyInto(out *RoleRef) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleRef. -func (in *RoleRef) DeepCopy() *RoleRef { - if in == nil { - return nil - } - out := new(RoleRef) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RuleRef) DeepCopyInto(out *RuleRef) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleRef. -func (in *RuleRef) DeepCopy() *RuleRef { - if in == nil { - return nil - } - out := new(RuleRef) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Subject) DeepCopyInto(out *Subject) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subject. -func (in *Subject) DeepCopy() *Subject { - if in == nil { - return nil - } - out := new(Subject) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Target) DeepCopyInto(out *Target) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Target. -func (in *Target) DeepCopy() *Target { - if in == nil { - return nil - } - out := new(Target) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *User) DeepCopyInto(out *User) { *out = *in @@ -320,7 +233,7 @@ func (in *UserDetail) DeepCopyInto(out *UserDetail) { } if in.GlobalRole != nil { in, out := &in.GlobalRole, &out.GlobalRole - *out = new(Role) + *out = new(GlobalRole) (*in).DeepCopyInto(*out) } } @@ -413,3 +326,135 @@ func (in *UserStatus) DeepCopy() *UserStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceRole) DeepCopyInto(out *WorkspaceRole) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]v1.PolicyRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AggregationRule != nil { + in, out := &in.AggregationRule, &out.AggregationRule + *out = new(AggregationRule) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRole. +func (in *WorkspaceRole) DeepCopy() *WorkspaceRole { + if in == nil { + return nil + } + out := new(WorkspaceRole) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkspaceRole) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceRoleBinding) DeepCopyInto(out *WorkspaceRoleBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Subjects != nil { + in, out := &in.Subjects, &out.Subjects + *out = make([]v1.Subject, len(*in)) + copy(*out, *in) + } + out.RoleRef = in.RoleRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRoleBinding. +func (in *WorkspaceRoleBinding) DeepCopy() *WorkspaceRoleBinding { + if in == nil { + return nil + } + out := new(WorkspaceRoleBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkspaceRoleBinding) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceRoleBindingList) DeepCopyInto(out *WorkspaceRoleBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]WorkspaceRoleBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRoleBindingList. +func (in *WorkspaceRoleBindingList) DeepCopy() *WorkspaceRoleBindingList { + if in == nil { + return nil + } + out := new(WorkspaceRoleBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkspaceRoleBindingList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceRoleList) DeepCopyInto(out *WorkspaceRoleList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]WorkspaceRole, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRoleList. +func (in *WorkspaceRoleList) DeepCopy() *WorkspaceRoleList { + if in == nil { + return nil + } + out := new(WorkspaceRoleList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkspaceRoleList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/pkg/apis/rbac/v1/evaluation_helpers.go b/pkg/apis/rbac/v1/evaluation_helpers.go new file mode 100644 index 0000000000000000000000000000000000000000..3707760bf5b8359d018280741e7182ae36fb7ddd --- /dev/null +++ b/pkg/apis/rbac/v1/evaluation_helpers.go @@ -0,0 +1,179 @@ +/* +Copyright 2018 The Kubernetes 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 v1 + +import ( + "fmt" + "strings" + + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func RoleRefGroupKind(roleRef rbacv1.RoleRef) schema.GroupKind { + return schema.GroupKind{Group: roleRef.APIGroup, Kind: roleRef.Kind} +} + +func VerbMatches(rule *rbacv1.PolicyRule, requestedVerb string) bool { + for _, ruleVerb := range rule.Verbs { + if ruleVerb == rbacv1.VerbAll { + return true + } + if ruleVerb == requestedVerb { + return true + } + } + + return false +} + +func APIGroupMatches(rule *rbacv1.PolicyRule, requestedGroup string) bool { + for _, ruleGroup := range rule.APIGroups { + if ruleGroup == rbacv1.APIGroupAll { + return true + } + if ruleGroup == requestedGroup { + return true + } + } + + return false +} + +func ResourceMatches(rule *rbacv1.PolicyRule, combinedRequestedResource, requestedSubresource string) bool { + for _, ruleResource := range rule.Resources { + // if everything is allowed, we match + if ruleResource == rbacv1.ResourceAll { + return true + } + // if we have an exact match, we match + if ruleResource == combinedRequestedResource { + return true + } + + // We can also match a */subresource. + // if there isn't a subresource, then continue + if len(requestedSubresource) == 0 { + continue + } + // if the rule isn't in the format */subresource, then we don't match, continue + if len(ruleResource) == len(requestedSubresource)+2 && + strings.HasPrefix(ruleResource, "*/") && + strings.HasSuffix(ruleResource, requestedSubresource) { + return true + + } + } + + return false +} + +func ResourceNameMatches(rule *rbacv1.PolicyRule, requestedName string) bool { + if len(rule.ResourceNames) == 0 { + return true + } + + for _, ruleName := range rule.ResourceNames { + if ruleName == requestedName { + return true + } + } + + return false +} + +func NonResourceURLMatches(rule *rbacv1.PolicyRule, requestedURL string) bool { + for _, ruleURL := range rule.NonResourceURLs { + if ruleURL == rbacv1.NonResourceAll { + return true + } + if ruleURL == requestedURL { + return true + } + if strings.HasSuffix(ruleURL, "*") && strings.HasPrefix(requestedURL, strings.TrimRight(ruleURL, "*")) { + return true + } + } + + return false +} + +// subjectsStrings returns users, groups, serviceaccounts, unknown for display purposes. +func SubjectsStrings(subjects []rbacv1.Subject) ([]string, []string, []string, []string) { + users := []string{} + groups := []string{} + sas := []string{} + others := []string{} + + for _, subject := range subjects { + switch subject.Kind { + case rbacv1.ServiceAccountKind: + sas = append(sas, fmt.Sprintf("%s/%s", subject.Namespace, subject.Name)) + + case rbacv1.UserKind: + users = append(users, subject.Name) + + case rbacv1.GroupKind: + groups = append(groups, subject.Name) + + default: + others = append(others, fmt.Sprintf("%s/%s/%s", subject.Kind, subject.Namespace, subject.Name)) + } + } + + return users, groups, sas, others +} + +func String(r rbacv1.PolicyRule) string { + return "PolicyRule" + CompactString(r) +} + +// CompactString exposes a compact string representation for use in escalation error messages +func CompactString(r rbacv1.PolicyRule) string { + formatStringParts := []string{} + formatArgs := []interface{}{} + if len(r.APIGroups) > 0 { + formatStringParts = append(formatStringParts, "APIGroups:%q") + formatArgs = append(formatArgs, r.APIGroups) + } + if len(r.Resources) > 0 { + formatStringParts = append(formatStringParts, "Resources:%q") + formatArgs = append(formatArgs, r.Resources) + } + if len(r.NonResourceURLs) > 0 { + formatStringParts = append(formatStringParts, "NonResourceURLs:%q") + formatArgs = append(formatArgs, r.NonResourceURLs) + } + if len(r.ResourceNames) > 0 { + formatStringParts = append(formatStringParts, "ResourceNames:%q") + formatArgs = append(formatArgs, r.ResourceNames) + } + if len(r.Verbs) > 0 { + formatStringParts = append(formatStringParts, "Verbs:%q") + formatArgs = append(formatArgs, r.Verbs) + } + formatString := "{" + strings.Join(formatStringParts, ", ") + "}" + return fmt.Sprintf(formatString, formatArgs...) +} + +type SortableRuleSlice []rbacv1.PolicyRule + +func (s SortableRuleSlice) Len() int { return len(s) } +func (s SortableRuleSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s SortableRuleSlice) Less(i, j int) bool { + return strings.Compare(s[i].String(), s[j].String()) < 0 +} diff --git a/pkg/apis/tenant/v1alpha1/workspace_types.go b/pkg/apis/tenant/v1alpha1/workspace_types.go index 876e87d5ec4a40510af2c057e1b187a7e3740c4e..b4a5fb4fff9e70328676e59e6f3476800efd03be 100644 --- a/pkg/apis/tenant/v1alpha1/workspace_types.go +++ b/pkg/apis/tenant/v1alpha1/workspace_types.go @@ -26,6 +26,7 @@ const ( ResourceKindWorkspace = "Workspace" ResourceSingularWorkspace = "workspace" ResourcePluralWorkspace = "workspaces" + WorkspaceLabel = "kubesphere.io/workspace" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index f71f7ce58a2c9f404ece04604b0d2d7edf62f1ff..bb5e2d840fa6e2125e104008477ef9f3a3676a04 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -11,13 +11,18 @@ import ( unionauth "k8s.io/apiserver/pkg/authentication/request/union" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" "k8s.io/klog" + clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/basic" "kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/jwttoken" "kubesphere.io/kubesphere/pkg/apiserver/authentication/request/anonymous" "kubesphere.io/kubesphere/pkg/apiserver/authentication/request/basictoken" "kubesphere.io/kubesphere/pkg/apiserver/authentication/request/bearertoken" "kubesphere.io/kubesphere/pkg/apiserver/authentication/token" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory" + authorizationoptions "kubesphere.io/kubesphere/pkg/apiserver/authorization/options" "kubesphere.io/kubesphere/pkg/apiserver/authorization/path" unionauthorizer "kubesphere.io/kubesphere/pkg/apiserver/authorization/union" apiserverconfig "kubesphere.io/kubesphere/pkg/apiserver/config" @@ -27,7 +32,7 @@ import ( "kubesphere.io/kubesphere/pkg/informers" configv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/config/v1alpha2" devopsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha2" - iamv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" + iamapi "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" loggingv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/logging/v1alpha2" monitoringv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha3" networkv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/network/v1alpha2" @@ -94,7 +99,7 @@ type APIServer struct { // monitoring client set MonitoringClient monitoring.Interface - // openpitrix client + // OpenpitrixClient openpitrix.Client // @@ -143,11 +148,10 @@ func (s *APIServer) installKubeSphereAPIs() { urlruntime.Must(networkv1alpha2.AddToContainer(s.container, s.Config.NetworkOptions.WeaveScopeHost)) urlruntime.Must(operationsv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes())) urlruntime.Must(resourcesv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.InformerFactory)) - urlruntime.Must(tenantv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.InformerFactory)) + urlruntime.Must(tenantv1alpha2.AddToContainer(s.container, s.InformerFactory)) urlruntime.Must(terminalv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.KubernetesClient.Config())) - urlruntime.Must(iamv1alpha2.AddToContainer(s.container, im.NewOperator(s.KubernetesClient.KubeSphere(), - s.InformerFactory.KubeSphereSharedInformerFactory()), - am.NewAMOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory.KubeSphereSharedInformerFactory()), + urlruntime.Must(iamapi.AddToContainer(s.container, im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory), + am.NewAMOperator(s.InformerFactory), s.Config.AuthenticationOptions)) urlruntime.Must(oauth.AddToContainer(s.container, token.NewJwtTokenIssuer(token.DefaultIssuerName, s.Config.AuthenticationOptions, s.CacheClient), s.Config.AuthenticationOptions)) urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.container)) @@ -183,6 +187,13 @@ func (s *APIServer) buildHandlerChain() { requestInfoResolver := &request.RequestInfoFactory{ APIPrefixes: sets.NewString("api", "apis", "kapis", "kapi"), GrouplessAPIPrefixes: sets.NewString("api", "kapi"), + GlobalResources: []schema.GroupResource{ + {Group: iamv1alpha2.SchemeGroupVersion.Group, Resource: iamv1alpha2.ResourcesPluralUser}, + {Group: iamv1alpha2.SchemeGroupVersion.Group, Resource: iamv1alpha2.ResourcesPluralGlobalRole}, + {Group: iamv1alpha2.SchemeGroupVersion.Group, Resource: iamv1alpha2.ResourcesPluralGlobalRoleBinding}, + {Group: tenantv1alpha1.SchemeGroupVersion.Group, Resource: tenantv1alpha1.ResourcePluralWorkspace}, + {Group: clusterv1alpha1.SchemeGroupVersion.Group, Resource: clusterv1alpha1.ResourcesPluralCluster}, + }, } handler := s.Server.Handler @@ -193,16 +204,26 @@ func (s *APIServer) buildHandlerChain() { handler = filters.WithMultipleClusterDispatcher(handler, clusterDispatcher) } - excludedPaths := []string{"/oauth/*", "/kapis/config.kubesphere.io/*"} - pathAuthorizer, _ := path.NewAuthorizer(excludedPaths) + var authorizers authorizer.Authorizer + + switch s.Config.AuthorizationOptions.Mode { + case authorizationoptions.AlwaysAllow: + authorizers = authorizerfactory.NewAlwaysAllowAuthorizer() + case authorizationoptions.AlwaysDeny: + authorizers = authorizerfactory.NewAlwaysDenyAuthorizer() + default: + fallthrough + case authorizationoptions.RBAC: + excludedPaths := []string{"/oauth/*", "/kapis/config.kubesphere.io/*"} + pathAuthorizer, _ := path.NewAuthorizer(excludedPaths) + authorizers = unionauthorizer.New(pathAuthorizer, authorizerfactory.NewOPAAuthorizer(am.NewAMOperator(s.InformerFactory)), authorizerfactory.NewRBACAuthorizer(am.NewAMOperator(s.InformerFactory))) + } - // union authorizers are ordered, don't change the order here - authorizers := unionauthorizer.New(pathAuthorizer, authorizerfactory.NewOPAAuthorizer(am.NewAMOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory.KubeSphereSharedInformerFactory()))) handler = filters.WithAuthorization(handler, authorizers) // authenticators are unordered authn := unionauth.New(anonymous.NewAuthenticator(), - basictoken.New(basic.NewBasicAuthenticator(im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory.KubeSphereSharedInformerFactory()))), + basictoken.New(basic.NewBasicAuthenticator(im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory))), bearertoken.New(jwttoken.NewTokenAuthenticator(token.NewJwtTokenIssuer(token.DefaultIssuerName, s.Config.AuthenticationOptions, s.CacheClient)))) handler = filters.WithAuthentication(handler, authn) handler = filters.WithRequestInfo(handler, requestInfoResolver) @@ -283,9 +304,10 @@ func (s *APIServer) waitForResourceSync(stopCh <-chan struct{}) error { ksGVRs := []schema.GroupVersionResource{ {Group: "tenant.kubesphere.io", Version: "v1alpha1", Resource: "workspaces"}, {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "users"}, - {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "roles"}, - {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "rolebindings"}, - {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "policyrules"}, + {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "globalroles"}, + {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "globalrolebindings"}, + {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "workspaceroles"}, + {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "workspacerolebindings"}, {Group: "cluster.kubesphere.io", Version: "v1alpha1", Resource: "clusters"}, } diff --git a/pkg/apiserver/authorization/authorizer/interfaces.go b/pkg/apiserver/authorization/authorizer/interfaces.go index 3c87713281f5119003122aa056265714a2d14f60..af67af2b52331bd417311d2d376bdab045ac33ba 100644 --- a/pkg/apiserver/authorization/authorizer/interfaces.go +++ b/pkg/apiserver/authorization/authorizer/interfaces.go @@ -68,6 +68,9 @@ type Attributes interface { // and false for non-resource endpoints like /api, /healthz IsResourceRequest() bool + // GetResourceScope returns the scope of the resource requested, if a request is for a REST object. + GetResourceScope() string + // GetPath returns the path of the request GetPath() string } @@ -111,6 +114,7 @@ type AttributesRecord struct { KubernetesRequest bool ResourceRequest bool Path string + ResourceScope string } func (a AttributesRecord) GetUser() user.Info { @@ -169,6 +173,10 @@ func (a AttributesRecord) GetPath() string { return a.Path } +func (a AttributesRecord) GetResourceScope() string { + return a.ResourceScope +} + type Decision int const ( diff --git a/pkg/apiserver/authorization/authorizerfactory/builtin.go b/pkg/apiserver/authorization/authorizerfactory/builtin.go new file mode 100644 index 0000000000000000000000000000000000000000..6d2f4d650c0bcdde447cdb8641d10e48f0f26206 --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/builtin.go @@ -0,0 +1,49 @@ +/* + * + * Copyright 2020 The KubeSphere 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 authorizerfactory + +import ( + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" +) + +// alwaysAllowAuthorizer is an implementation of authorizer.Attributes +// which always says yes to an authorization request. +// It is useful in tests and when using kubernetes in an open manner. +type alwaysAllowAuthorizer struct{} + +func (alwaysAllowAuthorizer) Authorize(authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { + return authorizer.DecisionAllow, "", nil +} + +func NewAlwaysAllowAuthorizer() *alwaysAllowAuthorizer { + return new(alwaysAllowAuthorizer) +} + +// alwaysDenyAuthorizer is an implementation of authorizer.Attributes +// which always says no to an authorization request. +// It is useful in unit tests to force an operation to be forbidden. +type alwaysDenyAuthorizer struct{} + +func (alwaysDenyAuthorizer) Authorize(a authorizer.Attributes) (decision authorizer.Decision, reason string, err error) { + return authorizer.DecisionNoOpinion, "Everything is forbidden.", nil +} + +func NewAlwaysDenyAuthorizer() *alwaysDenyAuthorizer { + return new(alwaysDenyAuthorizer) +} diff --git a/pkg/apiserver/authorization/authorizerfactory/builtin_test.go b/pkg/apiserver/authorization/authorizerfactory/builtin_test.go new file mode 100644 index 0000000000000000000000000000000000000000..67cd5595e33d4dc174ac28f55d314fcd6019a88a --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/builtin_test.go @@ -0,0 +1,38 @@ +/* + * + * Copyright 2020 The KubeSphere 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 authorizerfactory + +import ( + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "testing" +) + +func TestNewAlwaysAllowAuthorizer(t *testing.T) { + aaa := NewAlwaysAllowAuthorizer() + if decision, _, _ := aaa.Authorize(nil); decision != authorizer.DecisionAllow { + t.Errorf("AlwaysAllowAuthorizer.Authorize did not authorize successfully.") + } +} + +func TestNewAlwaysDenyAuthorizer(t *testing.T) { + ada := NewAlwaysDenyAuthorizer() + if decision, _, _ := ada.Authorize(nil); decision == authorizer.DecisionAllow { + t.Errorf("AlwaysDenyAuthorizer.Authorize returned nil instead of error.") + } +} diff --git a/pkg/apiserver/authorization/authorizerfactory/opa.go b/pkg/apiserver/authorization/authorizerfactory/opa.go index 5139344567805148b5d332552046fd4d5e353b6f..07b407d3abc5ed3aa1481d565d49c9e8c8ad25d7 100644 --- a/pkg/apiserver/authorization/authorizerfactory/opa.go +++ b/pkg/apiserver/authorization/authorizerfactory/opa.go @@ -21,6 +21,7 @@ package authorizerfactory import ( "context" "github.com/open-policy-agent/opa/rego" + rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/klog" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" @@ -33,125 +34,140 @@ type opaAuthorizer struct { } const ( - permissionUndefined = "permission undefined" - defaultRegoQuery = "data.authz.allow" + defaultRegoQuery = "data.authz.allow" ) // Make decision by request attributes func (o *opaAuthorizer) Authorize(attr authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { - // Make decisions based on the authorization policy of different levels of roles // Error returned when an internal error occurs // Reason must be returned when access is denied - globalRole, err := o.am.GetRoleOfUserInTargetScope(iamv1alpha2.GlobalScope, "", attr.GetUser().GetName()) + globalRole, err := o.am.GetGlobalRoleOfUser(attr.GetUser().GetName()) + if err != nil { if errors.IsNotFound(err) { - return authorizer.DecisionDeny, err.Error(), nil + return authorizer.DecisionNoOpinion, "", nil } - return authorizer.DecisionDeny, "", err + return authorizer.DecisionNoOpinion, "", err } - // check global role policy rules + // check global policy rules if authorized, reason, err = o.makeDecision(globalRole, attr); authorized == authorizer.DecisionAllow { return authorized, reason, nil } - // it's not in cluster resource, permission denied - if attr.GetCluster() == "" { - return authorizer.DecisionDeny, permissionUndefined, nil + // it's global resource, permission denied + if attr.GetResourceScope() == iamv1alpha2.GlobalScope { + return authorizer.DecisionNoOpinion, "", nil } - clusterRole, err := o.am.GetRoleOfUserInTargetScope(iamv1alpha2.ClusterScope, attr.GetCluster(), attr.GetUser().GetName()) - if err != nil { - if errors.IsNotFound(err) { - return authorizer.DecisionDeny, err.Error(), nil + if attr.GetResourceScope() == iamv1alpha2.WorkspaceScope { + workspaceRole, err := o.am.GetWorkspaceRoleOfUser(attr.GetUser().GetName(), attr.GetWorkspace()) + if err != nil { + if errors.IsNotFound(err) { + return authorizer.DecisionNoOpinion, "", nil + } + return authorizer.DecisionNoOpinion, "", err } - return authorizer.DecisionDeny, "", err - } - // check cluster role policy rules - if authorized, reason, err := o.makeDecision(clusterRole, attr); authorized == authorizer.DecisionAllow { - return authorized, reason, nil - } else if err != nil { - return authorizer.DecisionDeny, "", err - } + // check workspace role policy rules + if authorized, reason, err := o.makeDecision(workspaceRole, attr); authorized == authorizer.DecisionAllow { + return authorized, reason, err + } else if err != nil { + return authorizer.DecisionNoOpinion, "", err + } - // it's not in cluster resource, permission denied - if attr.GetWorkspace() == "" && attr.GetNamespace() == "" { - return authorizer.DecisionDeny, permissionUndefined, nil + return authorizer.DecisionNoOpinion, "", nil } - workspaceRole, err := o.am.GetRoleOfUserInTargetScope(iamv1alpha2.WorkspaceScope, attr.GetWorkspace(), attr.GetUser().GetName()) - if err != nil { - if errors.IsNotFound(err) { - return authorizer.DecisionDeny, err.Error(), nil + if attr.GetResourceScope() == iamv1alpha2.NamespaceScope { + role, err := o.am.GetNamespaceRoleOfUser(attr.GetUser().GetName(), attr.GetNamespace()) + if err != nil { + if errors.IsNotFound(err) { + return authorizer.DecisionNoOpinion, "", nil + } + return authorizer.DecisionNoOpinion, "", err + } + // check namespace role policy rules + if authorized, reason, err := o.makeDecision(role, attr); authorized == authorizer.DecisionAllow { + return authorized, reason, err + } else if err != nil { + return authorizer.DecisionNoOpinion, "", err } - return authorizer.DecisionDeny, "", err - } - // check workspace role policy rules - if authorized, reason, err := o.makeDecision(workspaceRole, attr); authorized == authorizer.DecisionAllow { - return authorized, reason, err - } else if err != nil { - return authorizer.DecisionDeny, "", err + return authorizer.DecisionNoOpinion, "", nil } - // it's not in workspace resource, permission denied - if attr.GetNamespace() == "" { - return authorizer.DecisionDeny, permissionUndefined, nil + clusterRole, err := o.am.GetClusterRoleOfUser(attr.GetUser().GetName(), attr.GetCluster()) + + if errors.IsNotFound(err) { + return authorizer.DecisionNoOpinion, "", nil } - namespaceRole, err := o.am.GetRoleOfUserInTargetScope(iamv1alpha2.NamespaceScope, attr.GetNamespace(), attr.GetUser().GetName()) if err != nil { - if errors.IsNotFound(err) { - return authorizer.DecisionDeny, err.Error(), nil - } - return authorizer.DecisionDeny, "", err + return authorizer.DecisionNoOpinion, "", err } - // check namespace role policy rules - if authorized, reason, err := o.makeDecision(namespaceRole, attr); authorized == authorizer.DecisionAllow { - return authorized, reason, err + + // check cluster role policy rules + if authorized, reason, err := o.makeDecision(clusterRole, attr); authorized == authorizer.DecisionAllow { + return authorized, reason, nil } else if err != nil { - return authorizer.DecisionDeny, "", err + return authorizer.DecisionNoOpinion, "", err } - return authorizer.DecisionDeny, permissionUndefined, nil + return authorizer.DecisionNoOpinion, "", nil } // Make decision base on role -func (o *opaAuthorizer) makeDecision(role *iamv1alpha2.Role, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { +func (o *opaAuthorizer) makeDecision(role interface{}, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { - for _, ruleRef := range role.Rules { - rule, err := o.am.GetPolicyRule(ruleRef.Name) - if err != nil { - if errors.IsNotFound(err) { - continue - } - return authorizer.DecisionDeny, "", err - } - // Call the rego.New function to create an object that can be prepared or evaluated - // After constructing a new rego.Rego object you can call PrepareForEval() to obtain an executable query - query, err := rego.New(rego.Query(defaultRegoQuery), rego.Module("authz.rego", rule.Rego)).PrepareForEval(context.Background()) + regoPolicy := "" - if err != nil { - klog.Errorf("rule syntax error:%s", err) - continue + // override + if globalRole, ok := role.(*iamv1alpha2.GlobalRole); ok { + if overrideRego, ok := globalRole.Annotations[iamv1alpha2.RegoOverrideAnnotation]; ok { + regoPolicy = overrideRego + } + } else if workspaceRole, ok := role.(*iamv1alpha2.WorkspaceRole); ok { + if overrideRego, ok := workspaceRole.Annotations[iamv1alpha2.RegoOverrideAnnotation]; ok { + regoPolicy = overrideRego + } + } else if clusterRole, ok := role.(*rbacv1.ClusterRole); ok { + if overrideRego, ok := clusterRole.Annotations[iamv1alpha2.RegoOverrideAnnotation]; ok { + regoPolicy = overrideRego + } + } else if role, ok := role.(*rbacv1.Role); ok { + if overrideRego, ok := role.Annotations[iamv1alpha2.RegoOverrideAnnotation]; ok { + regoPolicy = overrideRego } + } - // The policy decision is contained in the results returned by the Eval() call. You can inspect the decision and handle it accordingly. - results, err := query.Eval(context.Background(), rego.EvalInput(a)) + if regoPolicy == "" { + return authorizer.DecisionNoOpinion, "", nil + } - if err != nil { - klog.Errorf("rule syntax error:%s", err) - continue - } + // Call the rego.New function to create an object that can be prepared or evaluated + // After constructing a new rego.Rego object you can call PrepareForEval() to obtain an executable query + query, err := rego.New(rego.Query(defaultRegoQuery), rego.Module("authz.rego", regoPolicy)).PrepareForEval(context.Background()) - if len(results) > 0 && results[0].Expressions[0].Value == true { - return authorizer.DecisionAllow, "", nil - } + if err != nil { + klog.Errorf("syntax error:%s,refer: %s+v", err, role) + return authorizer.DecisionNoOpinion, "", err + } + + // The policy decision is contained in the results returned by the Eval() call. You can inspect the decision and handle it accordingly. + results, err := query.Eval(context.Background(), rego.EvalInput(a)) + + if err != nil { + klog.Errorf("syntax error:%s,refer: %s+v", err, role) + return authorizer.DecisionNoOpinion, "", err + } + + if len(results) > 0 && results[0].Expressions[0].Value == true { + return authorizer.DecisionAllow, "", nil } - return authorizer.DecisionDeny, permissionUndefined, nil + return authorizer.DecisionNoOpinion, "", nil } func NewOPAAuthorizer(am am.AccessManagementInterface) *opaAuthorizer { diff --git a/pkg/apiserver/authorization/authorizerfactory/opa_test.go b/pkg/apiserver/authorization/authorizerfactory/opa_test.go index 13bbe74e8b93e183fb6a929719f67c3a1989eee6..c7206df992a6f761fc76e7b3b34e4ed8a470d221 100644 --- a/pkg/apiserver/authorization/authorizerfactory/opa_test.go +++ b/pkg/apiserver/authorization/authorizerfactory/opa_test.go @@ -20,209 +20,22 @@ package authorizerfactory import ( "fmt" + rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apiserver/pkg/authentication/user" + fakek8s "k8s.io/client-go/kubernetes/fake" iamvealpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" - "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + factory "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models/iam/am" "testing" ) -func prepare() (am.AccessManagementInterface, error) { - rules := []*iamvealpha2.PolicyRule{ - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.PolicyRuleKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "always-allow", - }, - Rego: "package authz\ndefault allow = true", - }, { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.PolicyRuleKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "always-deny", - }, - Rego: "package authz\ndefault allow = false", - }, { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.PolicyRuleKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "manage-cluster1-resources", - }, - Rego: `package authz -default allow = false -allow { - resources_in_cluster1 -} -resources_in_cluster1 { - input.Cluster == "cluster1" -}`, - }, - } - - roles := []*iamvealpha2.Role{ - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.RoleKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "global-admin", - }, - Target: iamvealpha2.Target{ - Scope: iamvealpha2.GlobalScope, - Name: "", - }, - Rules: []iamvealpha2.RuleRef{ - { - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Kind: iamvealpha2.PolicyRuleKind, - Name: "always-allow", - }, - }, - }, - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.RoleKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "anonymous", - }, - Target: iamvealpha2.Target{ - Scope: iamvealpha2.GlobalScope, - Name: "", - }, - Rules: []iamvealpha2.RuleRef{ - { - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Kind: iamvealpha2.PolicyRuleKind, - Name: "always-deny", - }, - }, - }, - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.RoleKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "cluster1-admin", - }, - Target: iamvealpha2.Target{ - Scope: iamvealpha2.GlobalScope, - Name: "", - }, - Rules: []iamvealpha2.RuleRef{ - { - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Kind: iamvealpha2.PolicyRuleKind, - Name: "manage-cluster1-resources", - }, - }, - }, - } - - roleBindings := []*iamvealpha2.RoleBinding{ - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.RoleBindingKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "global-admin", - }, - Scope: iamvealpha2.GlobalScope, - RoleRef: iamvealpha2.RoleRef{ - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Kind: iamvealpha2.RoleKind, - Name: "global-admin", - }, - Subjects: []iamvealpha2.Subject{ - { - Kind: iamvealpha2.UserKind, - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Name: "admin", - }, - }, - }, - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.RoleBindingKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "anonymous", - }, - Scope: iamvealpha2.GlobalScope, - RoleRef: iamvealpha2.RoleRef{ - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Kind: iamvealpha2.RoleKind, - Name: "anonymous", - }, - Subjects: []iamvealpha2.Subject{ - { - Kind: iamvealpha2.UserKind, - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Name: user.Anonymous, - }, - }, - }, - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.RoleBindingKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "cluster1-admin", - }, - Scope: iamvealpha2.GlobalScope, - RoleRef: iamvealpha2.RoleRef{ - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Kind: iamvealpha2.RoleKind, - Name: "cluster1-admin", - }, - Subjects: []iamvealpha2.Subject{ - { - Kind: iamvealpha2.UserKind, - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Name: "tom", - }, - }, - }, - } - - ksClient := fake.NewSimpleClientset() - informerFactory := externalversions.NewSharedInformerFactory(ksClient, 0) - - for _, rule := range rules { - err := informerFactory.Iam().V1alpha2().PolicyRules().Informer().GetIndexer().Add(rule) - if err != nil { - return nil, fmt.Errorf("add rule:%s", err) - } - } - for _, role := range roles { - err := informerFactory.Iam().V1alpha2().Roles().Informer().GetIndexer().Add(role) - if err != nil { - return nil, fmt.Errorf("add role:%s", err) - } - } - for _, roleBinding := range roleBindings { - err := informerFactory.Iam().V1alpha2().RoleBindings().Informer().GetIndexer().Add(roleBinding) - if err != nil { - return nil, fmt.Errorf("add role binding:%s", err) - } - } - - operator := am.NewAMOperator(ksClient, informerFactory) - - return operator, nil -} - func TestGlobalRole(t *testing.T) { operator, err := prepare() + if err != nil { t.Fatal(err) } @@ -244,14 +57,8 @@ func TestGlobalRole(t *testing.T) { Extra: nil, }, Verb: "list", - Cluster: "", - Workspace: "", - Namespace: "", - APIGroup: "", APIVersion: "v1", Resource: "nodes", - Subresource: "", - Name: "", KubernetesRequest: true, ResourceRequest: true, Path: "/api/v1/nodes", @@ -268,19 +75,13 @@ func TestGlobalRole(t *testing.T) { Extra: nil, }, Verb: "list", - Cluster: "", - Workspace: "", - Namespace: "", - APIGroup: "", APIVersion: "v1", Resource: "nodes", - Subresource: "", - Name: "", KubernetesRequest: true, ResourceRequest: true, Path: "/api/v1/nodes", }, - expectedDecision: authorizer.DecisionDeny, + expectedDecision: authorizer.DecisionNoOpinion, }, { name: "tom can list nodes in cluster1", request: authorizer.AttributesRecord{ @@ -289,13 +90,8 @@ func TestGlobalRole(t *testing.T) { }, Verb: "list", Cluster: "cluster1", - Workspace: "", - Namespace: "", - APIGroup: "", APIVersion: "v1", Resource: "nodes", - Subresource: "", - Name: "", KubernetesRequest: true, ResourceRequest: true, Path: "/api/v1/clusters/cluster1/nodes", @@ -310,18 +106,13 @@ func TestGlobalRole(t *testing.T) { }, Verb: "list", Cluster: "cluster2", - Workspace: "", - Namespace: "", - APIGroup: "", APIVersion: "v1", Resource: "nodes", - Subresource: "", - Name: "", KubernetesRequest: true, ResourceRequest: true, Path: "/api/v1/clusters/cluster2/nodes", }, - expectedDecision: authorizer.DecisionDeny, + expectedDecision: authorizer.DecisionNoOpinion, }, } @@ -335,3 +126,127 @@ func TestGlobalRole(t *testing.T) { } } } + +func prepare() (am.AccessManagementInterface, error) { + globalRoles := []*iamvealpha2.GlobalRole{ + { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRole, + APIVersion: iamvealpha2.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "global-admin", + Annotations: map[string]string{iamvealpha2.RegoOverrideAnnotation: "package authz\ndefault allow = true"}, + }, + }, { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRole, + APIVersion: iamvealpha2.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "anonymous", + Annotations: map[string]string{iamvealpha2.RegoOverrideAnnotation: "package authz\ndefault allow = false"}, + }, + }, { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRole, + APIVersion: iamvealpha2.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster1-admin", + Annotations: map[string]string{iamvealpha2.RegoOverrideAnnotation: `package authz +default allow = false +allow { + resources_in_cluster1 +} +resources_in_cluster1 { + input.Cluster == "cluster1" +}`}, + }, + }, + } + + roleBindings := []*iamvealpha2.GlobalRoleBinding{ + { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRoleBinding, + APIVersion: iamvealpha2.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: "global-admin", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Kind: iamvealpha2.ResourceKindGlobalRole, + Name: "global-admin", + }, + Subjects: []rbacv1.Subject{ + { + Kind: iamvealpha2.ResourceKindUser, + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Name: "admin", + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRoleBinding, + APIVersion: iamvealpha2.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: "anonymous", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Kind: iamvealpha2.ResourceKindGlobalRole, + Name: "anonymous", + }, + Subjects: []rbacv1.Subject{ + { + Kind: iamvealpha2.ResourceKindUser, + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Name: user.Anonymous, + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRoleBinding, + APIVersion: iamvealpha2.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster1-admin", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Kind: iamvealpha2.ResourceKindGlobalRole, + Name: "cluster1-admin", + }, + Subjects: []rbacv1.Subject{ + { + Kind: iamvealpha2.ResourceKindUser, + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Name: "tom", + }, + }, + }, + } + + ksClient := fake.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + factory := factory.NewInformerFactories(k8sClient, ksClient, nil, nil) + for _, role := range globalRoles { + err := factory.KubeSphereSharedInformerFactory().Iam().V1alpha2().GlobalRoles().Informer().GetIndexer().Add(role) + if err != nil { + return nil, fmt.Errorf("add role:%s", err) + } + } + + for _, roleBinding := range roleBindings { + err := factory.KubeSphereSharedInformerFactory().Iam().V1alpha2().GlobalRoleBindings().Informer().GetIndexer().Add(roleBinding) + if err != nil { + return nil, fmt.Errorf("add role binding:%s", err) + } + } + + operator := am.NewAMOperator(factory) + + return operator, nil +} diff --git a/pkg/apiserver/authorization/authorizerfactory/rbac.go b/pkg/apiserver/authorization/authorizerfactory/rbac.go new file mode 100644 index 0000000000000000000000000000000000000000..d5419987948af7b1467645f07574419264f24181 --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/rbac.go @@ -0,0 +1,384 @@ +/* + * + * Copyright 2020 The KubeSphere 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 authorizerfactory + +import ( + "bytes" + "fmt" + "k8s.io/apiserver/pkg/authentication/serviceaccount" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "kubesphere.io/kubesphere/pkg/models/iam/am" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" + + "k8s.io/klog" + + rbacv1 "k8s.io/api/rbac/v1" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apiserver/pkg/authentication/user" + rbacv1helpers "kubesphere.io/kubesphere/pkg/apis/rbac/v1" +) + +type RBACAuthorizer struct { + am am.AccessManagementInterface +} + +// authorizingVisitor short-circuits once allowed, and collects any resolution errors encountered +type authorizingVisitor struct { + requestAttributes authorizer.Attributes + + allowed bool + reason string + errors []error +} + +func (v *authorizingVisitor) visit(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool { + if rule != nil && ruleAllows(v.requestAttributes, rule) { + v.allowed = true + v.reason = fmt.Sprintf("RBAC: allowed by %s", source.String()) + return false + } + if err != nil { + v.errors = append(v.errors, err) + } + return true +} + +type ruleAccumulator struct { + rules []rbacv1.PolicyRule + errors []error +} + +func (r *ruleAccumulator) visit(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool { + if rule != nil { + r.rules = append(r.rules, *rule) + } + if err != nil { + r.errors = append(r.errors, err) + } + return true +} + +func (r *RBACAuthorizer) Authorize(requestAttributes authorizer.Attributes) (authorizer.Decision, string, error) { + ruleCheckingVisitor := &authorizingVisitor{requestAttributes: requestAttributes} + + r.visitRulesFor(requestAttributes, ruleCheckingVisitor.visit) + + if ruleCheckingVisitor.allowed { + return authorizer.DecisionAllow, ruleCheckingVisitor.reason, nil + } + + // Build a detailed log of the denial. + // Make the whole block conditional so we don't do a lot of string-building we won't use. + if klog.V(4) { + var operation string + if requestAttributes.IsResourceRequest() { + b := &bytes.Buffer{} + b.WriteString(`"`) + b.WriteString(requestAttributes.GetVerb()) + b.WriteString(`" resource "`) + b.WriteString(requestAttributes.GetResource()) + if len(requestAttributes.GetAPIGroup()) > 0 { + b.WriteString(`.`) + b.WriteString(requestAttributes.GetAPIGroup()) + } + if len(requestAttributes.GetSubresource()) > 0 { + b.WriteString(`/`) + b.WriteString(requestAttributes.GetSubresource()) + } + b.WriteString(`"`) + if len(requestAttributes.GetName()) > 0 { + b.WriteString(` named "`) + b.WriteString(requestAttributes.GetName()) + b.WriteString(`"`) + } + operation = b.String() + } else { + operation = fmt.Sprintf("%q nonResourceURL %q", requestAttributes.GetVerb(), requestAttributes.GetPath()) + } + + var scope string + if ns := requestAttributes.GetNamespace(); len(ns) > 0 { + scope = fmt.Sprintf("in namespace %q", ns) + } else if ws := requestAttributes.GetWorkspace(); len(ws) > 0 { + scope = fmt.Sprintf("in workspace %q", ws) + } else if cluster := requestAttributes.GetWorkspace(); len(cluster) > 0 { + scope = fmt.Sprintf("in cluster %q", cluster) + } else { + scope = "global-wide" + } + + klog.Infof("RBAC: no rules authorize user %q with groups %q to %s %s", requestAttributes.GetUser().GetName(), requestAttributes.GetUser().GetGroups(), operation, scope) + } + + reason := "" + if len(ruleCheckingVisitor.errors) > 0 { + reason = fmt.Sprintf("RBAC: %v", utilerrors.NewAggregate(ruleCheckingVisitor.errors)) + } + return authorizer.DecisionNoOpinion, reason, nil +} + +func NewRBACAuthorizer(am am.AccessManagementInterface) *RBACAuthorizer { + return &RBACAuthorizer{am: am} +} + +func ruleAllows(requestAttributes authorizer.Attributes, rule *rbacv1.PolicyRule) bool { + if requestAttributes.IsResourceRequest() { + combinedResource := requestAttributes.GetResource() + if len(requestAttributes.GetSubresource()) > 0 { + combinedResource = requestAttributes.GetResource() + "/" + requestAttributes.GetSubresource() + } + + return rbacv1helpers.VerbMatches(rule, requestAttributes.GetVerb()) && + rbacv1helpers.APIGroupMatches(rule, requestAttributes.GetAPIGroup()) && + rbacv1helpers.ResourceMatches(rule, combinedResource, requestAttributes.GetSubresource()) && + rbacv1helpers.ResourceNameMatches(rule, requestAttributes.GetName()) + } + + return rbacv1helpers.VerbMatches(rule, requestAttributes.GetVerb()) && + rbacv1helpers.NonResourceURLMatches(rule, requestAttributes.GetPath()) +} + +func (r *RBACAuthorizer) rulesFor(requestAttributes authorizer.Attributes) ([]rbacv1.PolicyRule, error) { + visitor := &ruleAccumulator{} + r.visitRulesFor(requestAttributes, visitor.visit) + return visitor.rules, utilerrors.NewAggregate(visitor.errors) +} + +func (r *RBACAuthorizer) visitRulesFor(requestAttributes authorizer.Attributes, visitor func(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool) { + if globalRoleBindings, err := r.am.ListGlobalRoleBindings(""); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &globalRoleBindingDescriber{} + for _, globalRoleBinding := range globalRoleBindings { + subjectIndex, applies := appliesTo(requestAttributes.GetUser(), globalRoleBinding.Subjects, "") + if !applies { + continue + } + rules, err := r.am.GetRoleReferenceRules(globalRoleBinding.RoleRef, "") + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = globalRoleBinding + sourceDescriber.subject = &globalRoleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } + + if requestAttributes.GetResourceScope() == iamv1alpha2.WorkspaceScope { + if workspaceRoleBindings, err := r.am.ListWorkspaceRoleBindings("", requestAttributes.GetWorkspace()); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &workspaceRoleBindingDescriber{} + for _, workspaceRoleBinding := range workspaceRoleBindings { + subjectIndex, applies := appliesTo(requestAttributes.GetUser(), workspaceRoleBinding.Subjects, "") + if !applies { + continue + } + rules, err := r.am.GetRoleReferenceRules(workspaceRoleBinding.RoleRef, "") + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = workspaceRoleBinding + sourceDescriber.subject = &workspaceRoleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } + } + + if requestAttributes.GetResourceScope() == iamv1alpha2.NamespaceScope { + if roleBindings, err := r.am.ListRoleBindings("", requestAttributes.GetNamespace()); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &roleBindingDescriber{} + for _, roleBinding := range roleBindings { + subjectIndex, applies := appliesTo(requestAttributes.GetUser(), roleBinding.Subjects, requestAttributes.GetNamespace()) + if !applies { + continue + } + rules, err := r.am.GetRoleReferenceRules(roleBinding.RoleRef, requestAttributes.GetNamespace()) + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = roleBinding + sourceDescriber.subject = &roleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } + } + + if clusterRoleBindings, err := r.am.ListClusterRoleBindings(""); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &clusterRoleBindingDescriber{} + for _, clusterRoleBinding := range clusterRoleBindings { + subjectIndex, applies := appliesTo(requestAttributes.GetUser(), clusterRoleBinding.Subjects, "") + if !applies { + continue + } + rules, err := r.am.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "") + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = clusterRoleBinding + sourceDescriber.subject = &clusterRoleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } +} + +// appliesTo returns whether any of the bindingSubjects applies to the specified subject, +// and if true, the index of the first subject that applies +func appliesTo(user user.Info, bindingSubjects []rbacv1.Subject, namespace string) (int, bool) { + for i, bindingSubject := range bindingSubjects { + if appliesToUser(user, bindingSubject, namespace) { + return i, true + } + } + return 0, false +} + +func appliesToUser(user user.Info, subject rbacv1.Subject, namespace string) bool { + switch subject.Kind { + case rbacv1.UserKind: + return user.GetName() == subject.Name + + case rbacv1.GroupKind: + return sliceutil.HasString(user.GetGroups(), subject.Name) + + case rbacv1.ServiceAccountKind: + // default the namespace to namespace we're working in if its available. This allows rolebindings that reference + // SAs in th local namespace to avoid having to qualify them. + saNamespace := namespace + if len(subject.Namespace) > 0 { + saNamespace = subject.Namespace + } + if len(saNamespace) == 0 { + return false + } + // use a more efficient comparison for RBAC checking + return serviceaccount.MatchesUsername(saNamespace, subject.Name, user.GetName()) + default: + return false + } +} + +type globalRoleBindingDescriber struct { + binding *iamv1alpha2.GlobalRoleBinding + subject *rbacv1.Subject +} + +func (d *globalRoleBindingDescriber) String() string { + return fmt.Sprintf("GlobalRoleBinding %q of %s %q to %s", + d.binding.Name, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, ""), + ) +} + +type clusterRoleBindingDescriber struct { + binding *rbacv1.ClusterRoleBinding + subject *rbacv1.Subject +} + +func (d *clusterRoleBindingDescriber) String() string { + return fmt.Sprintf("ClusterRoleBinding %q of %s %q to %s", + d.binding.Name, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, ""), + ) +} + +type workspaceRoleBindingDescriber struct { + binding *iamv1alpha2.WorkspaceRoleBinding + subject *rbacv1.Subject +} + +func (d *workspaceRoleBindingDescriber) String() string { + return fmt.Sprintf("GlobalRoleBinding %q of %s %q to %s", + d.binding.Name, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, ""), + ) +} + +type roleBindingDescriber struct { + binding *rbacv1.RoleBinding + subject *rbacv1.Subject +} + +func (d *roleBindingDescriber) String() string { + return fmt.Sprintf("RoleBinding %q of %s %q to %s", + d.binding.Name+"/"+d.binding.Namespace, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, d.binding.Namespace), + ) +} + +func describeSubject(s *rbacv1.Subject, bindingNamespace string) string { + switch s.Kind { + case rbacv1.ServiceAccountKind: + if len(s.Namespace) > 0 { + return fmt.Sprintf("%s %q", s.Kind, s.Name+"/"+s.Namespace) + } + return fmt.Sprintf("%s %q", s.Kind, s.Name+"/"+bindingNamespace) + default: + return fmt.Sprintf("%s %q", s.Kind, s.Name) + } +} diff --git a/pkg/apiserver/authorization/authorizerfactory/rbac_test.go b/pkg/apiserver/authorization/authorizerfactory/rbac_test.go new file mode 100644 index 0000000000000000000000000000000000000000..cf2ff65709347fb512c369d88047486c85dc389e --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/rbac_test.go @@ -0,0 +1,389 @@ +/* +Copyright 2016 The Kubernetes 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 authorizerfactory + +import ( + "errors" + "github.com/google/go-cmp/cmp" + fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake" + "hash/fnv" + "io" + fakeistio "istio.io/client-go/pkg/clientset/versioned/fake" + fakek8s "k8s.io/client-go/kubernetes/fake" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models/iam/am" + "sort" + "testing" + + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/authentication/user" +) + +// StaticRoles is a rule resolver that resolves from lists of role objects. +type StaticRoles struct { + roles []*rbacv1.Role + roleBindings []*rbacv1.RoleBinding + clusterRoles []*rbacv1.ClusterRole + clusterRoleBindings []*rbacv1.ClusterRoleBinding +} + +func (r *StaticRoles) GetRole(namespace, name string) (*rbacv1.Role, error) { + if len(namespace) == 0 { + return nil, errors.New("must provide namespace when getting role") + } + for _, role := range r.roles { + if role.Namespace == namespace && role.Name == name { + return role, nil + } + } + return nil, errors.New("role not found") +} + +func (r *StaticRoles) GetClusterRole(name string) (*rbacv1.ClusterRole, error) { + for _, clusterRole := range r.clusterRoles { + if clusterRole.Name == name { + return clusterRole, nil + } + } + return nil, errors.New("clusterrole not found") +} + +func (r *StaticRoles) ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error) { + if len(namespace) == 0 { + return nil, errors.New("must provide namespace when listing role bindings") + } + + roleBindingList := []*rbacv1.RoleBinding{} + for _, roleBinding := range r.roleBindings { + if roleBinding.Namespace != namespace { + continue + } + // TODO(ericchiang): need to implement label selectors? + roleBindingList = append(roleBindingList, roleBinding) + } + return roleBindingList, nil +} + +func (r *StaticRoles) ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error) { + return r.clusterRoleBindings, nil +} + +// compute a hash of a policy rule so we can sort in a deterministic order +func hashOf(p rbacv1.PolicyRule) string { + hash := fnv.New32() + writeStrings := func(slis ...[]string) { + for _, sli := range slis { + for _, s := range sli { + io.WriteString(hash, s) + } + } + } + writeStrings(p.Verbs, p.APIGroups, p.Resources, p.ResourceNames, p.NonResourceURLs) + return string(hash.Sum(nil)) +} + +// byHash sorts a set of policy rules by a hash of its fields +type byHash []rbacv1.PolicyRule + +func (b byHash) Len() int { return len(b) } +func (b byHash) Less(i, j int) bool { return hashOf(b[i]) < hashOf(b[j]) } +func (b byHash) Swap(i, j int) { b[i], b[j] = b[j], b[i] } + +func TestRBACAuthorizer(t *testing.T) { + ruleReadPods := rbacv1.PolicyRule{ + Verbs: []string{"GET", "WATCH"}, + APIGroups: []string{"v1"}, + Resources: []string{"pods"}, + } + ruleReadServices := rbacv1.PolicyRule{ + Verbs: []string{"GET", "WATCH"}, + APIGroups: []string{"v1"}, + Resources: []string{"services"}, + } + ruleWriteNodes := rbacv1.PolicyRule{ + Verbs: []string{"PUT", "CREATE", "UPDATE"}, + APIGroups: []string{"v1"}, + Resources: []string{"nodes"}, + } + ruleAdmin := rbacv1.PolicyRule{ + Verbs: []string{"*"}, + APIGroups: []string{"*"}, + Resources: []string{"*"}, + } + + staticRoles1 := StaticRoles{ + roles: []*rbacv1.Role{ + { + ObjectMeta: metav1.ObjectMeta{Namespace: "namespace1", Name: "readthings"}, + Rules: []rbacv1.PolicyRule{ruleReadPods, ruleReadServices}, + }, + }, + clusterRoles: []*rbacv1.ClusterRole{ + { + ObjectMeta: metav1.ObjectMeta{Name: "cluster-admin"}, + Rules: []rbacv1.PolicyRule{ruleAdmin}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "write-nodes"}, + Rules: []rbacv1.PolicyRule{ruleWriteNodes}, + }, + }, + roleBindings: []*rbacv1.RoleBinding{ + { + ObjectMeta: metav1.ObjectMeta{Namespace: "namespace1"}, + Subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "foobar"}, + {Kind: rbacv1.GroupKind, Name: "group1"}, + }, + RoleRef: rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "Role", Name: "readthings"}, + }, + }, + clusterRoleBindings: []*rbacv1.ClusterRoleBinding{ + { + Subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "admin"}, + {Kind: rbacv1.GroupKind, Name: "admin"}, + }, + RoleRef: rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "ClusterRole", Name: "cluster-admin"}, + }, + }, + } + + tests := []struct { + StaticRoles + + // For a given context, what are the rules that apply? + user user.Info + namespace string + effectiveRules []rbacv1.PolicyRule + }{ + { + StaticRoles: staticRoles1, + user: &user.DefaultInfo{Name: "foobar"}, + namespace: "namespace1", + effectiveRules: []rbacv1.PolicyRule{ruleReadPods, ruleReadServices}, + }, + { + StaticRoles: staticRoles1, + user: &user.DefaultInfo{Name: "foobar"}, + namespace: "namespace2", + effectiveRules: nil, + }, + { + StaticRoles: staticRoles1, + // Same as above but without a namespace. Only cluster rules should apply. + user: &user.DefaultInfo{Name: "foobar", Groups: []string{"admin"}}, + effectiveRules: []rbacv1.PolicyRule{ruleAdmin}, + }, + { + StaticRoles: staticRoles1, + user: &user.DefaultInfo{}, + effectiveRules: nil, + }, + } + + for i, tc := range tests { + ruleResolver, err := newMockRBACAuthorizer(&tc.StaticRoles) + + if err != nil { + t.Fatal(err) + } + + scope := iamv1alpha2.ClusterScope + + if tc.namespace != "" { + scope = iamv1alpha2.NamespaceScope + } + + rules, err := ruleResolver.rulesFor(authorizer.AttributesRecord{ + User: tc.user, + Namespace: tc.namespace, + ResourceScope: scope, + }) + + if err != nil { + t.Errorf("case %d: GetEffectivePolicyRules(context)=%v", i, err) + continue + } + + // Sort for deep equals + sort.Sort(byHash(rules)) + sort.Sort(byHash(tc.effectiveRules)) + + if diff := cmp.Diff(rules, tc.effectiveRules); diff != "" { + t.Errorf("case %d: %s", i, diff) + } + } +} + +func newMockRBACAuthorizer(staticRoles *StaticRoles) (*RBACAuthorizer, error) { + + ksClient := fakeks.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + istioClient := fakeistio.NewSimpleClientset() + appClient := fakeapp.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, istioClient, appClient) + + k8sInformerFactory := fakeInformerFactory.KubernetesSharedInformerFactory() + + for _, role := range staticRoles.roles { + err := k8sInformerFactory.Rbac().V1().Roles().Informer().GetIndexer().Add(role) + if err != nil { + return nil, err + } + } + + for _, roleBinding := range staticRoles.roleBindings { + err := k8sInformerFactory.Rbac().V1().RoleBindings().Informer().GetIndexer().Add(roleBinding) + if err != nil { + return nil, err + } + } + + for _, clusterRole := range staticRoles.clusterRoles { + err := k8sInformerFactory.Rbac().V1().ClusterRoles().Informer().GetIndexer().Add(clusterRole) + if err != nil { + return nil, err + } + } + + for _, clusterRoleBinding := range staticRoles.clusterRoleBindings { + err := k8sInformerFactory.Rbac().V1().ClusterRoleBindings().Informer().GetIndexer().Add(clusterRoleBinding) + if err != nil { + return nil, err + } + } + return NewRBACAuthorizer(am.NewAMOperator(fakeInformerFactory)), nil +} + +func TestAppliesTo(t *testing.T) { + tests := []struct { + subjects []rbacv1.Subject + user user.Info + namespace string + appliesTo bool + index int + testCase string + }{ + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "foobar"}, + appliesTo: true, + index: 0, + testCase: "single subject that matches username", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.UserKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "foobar"}, + appliesTo: true, + index: 1, + testCase: "multiple subjects, one that matches username", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.UserKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "zimzam"}, + appliesTo: false, + testCase: "multiple subjects, none that match username", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.GroupKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}, + appliesTo: true, + index: 1, + testCase: "multiple subjects, one that match group", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.GroupKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}, + namespace: "namespace1", + appliesTo: true, + index: 1, + testCase: "multiple subjects, one that match group, should ignore namespace", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.GroupKind, Name: "foobar"}, + {Kind: rbacv1.ServiceAccountKind, Namespace: "kube-system", Name: "default"}, + }, + user: &user.DefaultInfo{Name: "system:serviceaccount:kube-system:default"}, + namespace: "default", + appliesTo: true, + index: 2, + testCase: "multiple subjects with a service account that matches", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "*"}, + }, + user: &user.DefaultInfo{Name: "foobar"}, + namespace: "default", + appliesTo: false, + testCase: "* user subject name doesn't match all users", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.GroupKind, Name: user.AllAuthenticated}, + {Kind: rbacv1.GroupKind, Name: user.AllUnauthenticated}, + }, + user: &user.DefaultInfo{Name: "foobar", Groups: []string{user.AllAuthenticated}}, + namespace: "default", + appliesTo: true, + index: 0, + testCase: "binding to all authenticated and unauthenticated subjects matches authenticated user", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.GroupKind, Name: user.AllAuthenticated}, + {Kind: rbacv1.GroupKind, Name: user.AllUnauthenticated}, + }, + user: &user.DefaultInfo{Name: "system:anonymous", Groups: []string{user.AllUnauthenticated}}, + namespace: "default", + appliesTo: true, + index: 1, + testCase: "binding to all authenticated and unauthenticated subjects matches anonymous user", + }, + } + + for _, tc := range tests { + gotIndex, got := appliesTo(tc.user, tc.subjects, tc.namespace) + if got != tc.appliesTo { + t.Errorf("case %q want appliesTo=%t, got appliesTo=%t", tc.testCase, tc.appliesTo, got) + } + if gotIndex != tc.index { + t.Errorf("case %q want index %d, got %d", tc.testCase, tc.index, gotIndex) + } + } +} diff --git a/pkg/apiserver/authorization/options/authorization_options.go b/pkg/apiserver/authorization/options/authorization_options.go new file mode 100644 index 0000000000000000000000000000000000000000..0a2a5e8f2845cd4e032dee55d09404dc07367643 --- /dev/null +++ b/pkg/apiserver/authorization/options/authorization_options.go @@ -0,0 +1,54 @@ +/* + * + * Copyright 2020 The KubeSphere 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 options + +import ( + "fmt" + "github.com/spf13/pflag" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" +) + +type AuthorizationOptions struct { + Mode string `json:"mode" yaml:"mode"` +} + +func NewAuthorizationOptions() *AuthorizationOptions { + return &AuthorizationOptions{Mode: RBAC} +} + +var ( + AlwaysDeny = "AlwaysDeny" + AlwaysAllow = "AlwaysAllow" + RBAC = "RBAC" +) + +func (o *AuthorizationOptions) AddFlags(fs *pflag.FlagSet, s *AuthorizationOptions) { + fs.StringVar(&o.Mode, "authorization", s.Mode, "Authorization setting, allowed values: AlwaysDeny, AlwaysAllow, RBAC.") +} + +func (o AuthorizationOptions) Validate() []error { + errs := make([]error, 0) + if !sliceutil.HasString([]string{AlwaysAllow, AlwaysDeny, RBAC}, o.Mode) { + err := fmt.Errorf("authorization mode %s not support", o.Mode) + klog.Error(err) + errs = append(errs, err) + } + return errs +} diff --git a/pkg/apiserver/config/config.go b/pkg/apiserver/config/config.go index 1346699e32331e09858459c4abd7e8bf5085a844..af159a7fd4864e56e124192bdd37802e3819ddc4 100644 --- a/pkg/apiserver/config/config.go +++ b/pkg/apiserver/config/config.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/spf13/viper" authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + authorizationoptions "kubesphere.io/kubesphere/pkg/apiserver/authorization/options" "kubesphere.io/kubesphere/pkg/simple/client/alerting" "kubesphere.io/kubesphere/pkg/simple/client/cache" "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" @@ -59,19 +60,20 @@ const ( // Config defines everything needed for apiserver to deal with external services type Config struct { - DevopsOptions *jenkins.Options `json:"devops,omitempty" yaml:"devops,omitempty" mapstructure:"devops"` - SonarQubeOptions *sonarqube.Options `json:"sonarqube,omitempty" yaml:"sonarQube,omitempty" mapstructure:"sonarqube"` - KubernetesOptions *k8s.KubernetesOptions `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"` - ServiceMeshOptions *servicemesh.Options `json:"servicemesh,omitempty" yaml:"servicemesh,omitempty" mapstructure:"servicemesh"` - NetworkOptions *network.Options `json:"network,omitempty" yaml:"network,omitempty" mapstructure:"network"` - LdapOptions *ldap.Options `json:"-" yaml:"ldap,omitempty" mapstructure:"ldap"` - RedisOptions *cache.Options `json:"-" yaml:"redis,omitempty" mapstructure:"redis"` - S3Options *s3.Options `json:"s3,omitempty" yaml:"s3,omitempty" mapstructure:"s3"` - OpenPitrixOptions *openpitrix.Options `json:"openpitrix,omitempty" yaml:"openpitrix,omitempty" mapstructure:"openpitrix"` - MonitoringOptions *prometheus.Options `json:"monitoring,omitempty" yaml:"monitoring,omitempty" mapstructure:"monitoring"` - LoggingOptions *elasticsearch.Options `json:"logging,omitempty" yaml:"logging,omitempty" mapstructure:"logging"` - AuthenticationOptions *authoptions.AuthenticationOptions `json:"-" yaml:"authentication,omitempty" mapstructure:"authentication"` - MultiClusterOptions *multicluster.Options `json:"multicluster,omitempty" yaml:"multicluster,omitempty" mapstructure:"multicluster"` + DevopsOptions *jenkins.Options `json:"devops,omitempty" yaml:"devops,omitempty" mapstructure:"devops"` + SonarQubeOptions *sonarqube.Options `json:"sonarqube,omitempty" yaml:"sonarQube,omitempty" mapstructure:"sonarqube"` + KubernetesOptions *k8s.KubernetesOptions `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"` + ServiceMeshOptions *servicemesh.Options `json:"servicemesh,omitempty" yaml:"servicemesh,omitempty" mapstructure:"servicemesh"` + NetworkOptions *network.Options `json:"network,omitempty" yaml:"network,omitempty" mapstructure:"network"` + LdapOptions *ldap.Options `json:"-,omitempty" yaml:"ldap,omitempty" mapstructure:"ldap"` + RedisOptions *cache.Options `json:"redis,omitempty" yaml:"redis,omitempty" mapstructure:"redis"` + S3Options *s3.Options `json:"s3,omitempty" yaml:"s3,omitempty" mapstructure:"s3"` + OpenPitrixOptions *openpitrix.Options `json:"openpitrix,omitempty" yaml:"openpitrix,omitempty" mapstructure:"openpitrix"` + MonitoringOptions *prometheus.Options `json:"monitoring,omitempty" yaml:"monitoring,omitempty" mapstructure:"monitoring"` + LoggingOptions *elasticsearch.Options `json:"logging,omitempty" yaml:"logging,omitempty" mapstructure:"logging"` + AuthenticationOptions *authoptions.AuthenticationOptions `json:"authentication,omitempty" yaml:"authentication,omitempty" mapstructure:"authentication"` + AuthorizationOptions *authorizationoptions.AuthorizationOptions `json:"authorization,omitempty" yaml:"authorization,omitempty" mapstructure:"authorization"` + MultiClusterOptions *multicluster.Options `json:"multicluster,omitempty" yaml:"multicluster,omitempty" mapstructure:"multicluster"` // Options used for enabling components, not actually used now. Once we switch Alerting/Notification API to kubesphere, // we can add these options to kubesphere command lines AlertingOptions *alerting.Options `json:"alerting,omitempty" yaml:"alerting,omitempty" mapstructure:"alerting"` @@ -95,6 +97,8 @@ func New() *Config { NotificationOptions: notification.NewNotificationOptions(), LoggingOptions: elasticsearch.NewElasticSearchOptions(), AuthenticationOptions: authoptions.NewAuthenticateOptions(), + AuthorizationOptions: authorizationoptions.NewAuthorizationOptions(), + MultiClusterOptions: multicluster.NewOptions(), } } @@ -209,5 +213,4 @@ func (conf *Config) stripEmptyOptions() { if conf.MultiClusterOptions != nil && !conf.MultiClusterOptions.Enable { conf.MultiClusterOptions = nil } - } diff --git a/pkg/apiserver/config/config_test.go b/pkg/apiserver/config/config_test.go index 376d7658fae08866e47b21f84aedb4d116be7391..5bbc1f02826ba720a4abeb0b70be37cfdb5da77b 100644 --- a/pkg/apiserver/config/config_test.go +++ b/pkg/apiserver/config/config_test.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth" authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + authorizationoptions "kubesphere.io/kubesphere/pkg/apiserver/authorization/options" "kubesphere.io/kubesphere/pkg/simple/client/alerting" "kubesphere.io/kubesphere/pkg/simple/client/cache" "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" @@ -99,6 +100,7 @@ func newTestConfig() (*Config, error) { NotificationOptions: ¬ification.Options{ Endpoint: "http://notification.kubesphere-alerting-system.svc:9200", }, + AuthorizationOptions: authorizationoptions.NewAuthorizationOptions(), AuthenticationOptions: &authoptions.AuthenticationOptions{ AuthenticateRateLimiterMaxTries: 5, AuthenticateRateLimiterDuration: 30 * time.Minute, diff --git a/pkg/apiserver/filters/authorization.go b/pkg/apiserver/filters/authorization.go index 44c9ba4dbd9ae2581569594321f78cba7bf34dc5..588ce7bef16f7534e7082ade0fa8c96567374f05 100644 --- a/pkg/apiserver/filters/authorization.go +++ b/pkg/apiserver/filters/authorization.go @@ -59,6 +59,7 @@ func getAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error) } // Start with common attributes that apply to resource and non-resource requests + attribs.ResourceScope = requestInfo.ResourceScope attribs.ResourceRequest = requestInfo.IsResourceRequest attribs.Path = requestInfo.Path attribs.Verb = requestInfo.Verb diff --git a/pkg/apiserver/query/field.go b/pkg/apiserver/query/field.go index 0a151507202ad481711769cecca05f2838bd0009..3824f88683c94228e8426bc252b742d48b328418 100644 --- a/pkg/apiserver/query/field.go +++ b/pkg/apiserver/query/field.go @@ -7,7 +7,9 @@ const ( FieldName = "name" FieldUID = "uid" FieldCreationTimeStamp = "creationTimestamp" + FieldCreateTime = "createTime" FieldLastUpdateTimestamp = "lastUpdateTimestamp" + FieldUpdateTime = "updateTime" FieldLabel = "label" FieldAnnotation = "annotation" FieldNamespace = "namespace" @@ -18,6 +20,8 @@ const ( var SortableFields = []Field{ FieldCreationTimeStamp, + FieldCreateTime, + FieldUpdateTime, FieldLastUpdateTimestamp, FieldName, } diff --git a/pkg/apiserver/query/types.go b/pkg/apiserver/query/types.go index 91d13add5b54e83eba2f64258e298489fecd704e..2ad1ca36e9ceb321acc60d1fea49fd9a2767c3c7 100644 --- a/pkg/apiserver/query/types.go +++ b/pkg/apiserver/query/types.go @@ -2,6 +2,8 @@ package query import ( "github.com/emicklei/go-restful" + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" "strconv" ) @@ -26,7 +28,9 @@ type Query struct { Ascending bool // - Filters []Filter + Filters map[Field]Value + + LabelSelector string } type Pagination struct { @@ -47,17 +51,27 @@ func newPagination(limit int, offset int) *Pagination { } } +func (q *Query) Selector() labels.Selector { + if selector, err := labels.Parse(q.LabelSelector); err != nil { + return labels.Everything() + } else { + return selector + } +} + func (p *Pagination) GetValidPagination(total int) (startIndex, endIndex int) { + // no pagination if p.Limit == NoPagination.Limit { return 0, total } - if p.Limit < 0 || p.Offset < 0 || total == 0 { + // out of range + if p.Limit < 0 || p.Offset < 0 || p.Offset > total { return 0, 0 } - startIndex = p.Limit * p.Offset + startIndex = p.Offset endIndex = startIndex + p.Limit if endIndex > total { @@ -72,7 +86,7 @@ func New() *Query { Pagination: NoPagination, SortBy: "", Ascending: false, - Filters: []Filter{}, + Filters: map[Field]Value{}, } } @@ -84,35 +98,36 @@ type Filter struct { func ParseQueryParameter(request *restful.Request) *Query { query := New() - limit, err := strconv.Atoi(request.QueryParameter("limit")) + limit, err := strconv.Atoi(request.QueryParameter(ParameterLimit)) // equivalent to undefined, use the default value if err != nil { limit = -1 } - page, err := strconv.Atoi(request.QueryParameter("page")) + page, err := strconv.Atoi(request.QueryParameter(ParameterPage)) // equivalent to undefined, use the default value if err != nil { page = 1 } - query.Pagination = newPagination(limit, page-1) + query.Pagination = newPagination(limit, (page-1)*limit) - query.SortBy = Field(defaultString(request.QueryParameter("sortBy"), FieldCreationTimeStamp)) + query.SortBy = Field(defaultString(request.QueryParameter(ParameterOrderBy), FieldCreationTimeStamp)) - ascending, err := strconv.ParseBool(defaultString(request.QueryParameter("ascending"), "false")) + ascending, err := strconv.ParseBool(defaultString(request.QueryParameter(ParameterAscending), "false")) if err != nil { query.Ascending = false } else { query.Ascending = ascending } - for _, field := range ComparableFields { - f := request.QueryParameter(string(field)) - if len(f) != 0 { - query.Filters = append(query.Filters, Filter{ - Field: field, - Value: Value(f), - }) + query.LabelSelector = request.QueryParameter(ParameterLabelSelector) + + for key, values := range request.Request.URL.Query() { + if !sliceutil.HasString([]string{ParameterPage, ParameterLimit, ParameterOrderBy, ParameterAscending, ParameterLabelSelector}, key) { + // support multiple query condition + for _, value := range values { + query.Filters[Field(key)] = Value(value) + } } } diff --git a/pkg/apiserver/query/types_test.go b/pkg/apiserver/query/types_test.go index e473da2fd818955a8b33b03bdfe7d9f8a7ba9cc7..d4de9cb096475b5e2b25a4fc93eee59ec6b61e4e 100644 --- a/pkg/apiserver/query/types_test.go +++ b/pkg/apiserver/query/types_test.go @@ -16,24 +16,15 @@ func TestParseQueryParameter(t *testing.T) { }{ { "test normal case", - "label=app.kubernetes.io/name:book&name=foo&status=Running&page=1&limit=10&ascending=true", + "label=app.kubernetes.io/name=book&name=foo&status=Running&page=1&limit=10&ascending=true", &Query{ Pagination: newPagination(10, 0), SortBy: FieldCreationTimeStamp, Ascending: true, - Filters: []Filter{ - { - FieldName, - Value("foo"), - }, - { - FieldLabel, - Value("app.kubernetes.io/name:book"), - }, - { - FieldStatus, - Value("Running"), - }, + Filters: map[Field]Value{ + FieldLabel: Value("app.kubernetes.io/name=book"), + FieldName: Value("foo"), + FieldStatus: Value("Running"), }, }, }, @@ -44,7 +35,10 @@ func TestParseQueryParameter(t *testing.T) { Pagination: NoPagination, SortBy: FieldCreationTimeStamp, Ascending: false, - Filters: []Filter{}, + Filters: map[Field]Value{ + Field("xxxx"): Value("xxxx"), + Field("dsfsw"): Value("xxxx"), + }, }, }, } @@ -61,6 +55,7 @@ func TestParseQueryParameter(t *testing.T) { got := ParseQueryParameter(request) if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) return } diff --git a/pkg/apiserver/request/requestinfo.go b/pkg/apiserver/request/requestinfo.go index ca94487eecb16daa77710ea26910a98eb884b073..c7a9baf122017c7e801e8d174c0543a20d3c5915 100644 --- a/pkg/apiserver/request/requestinfo.go +++ b/pkg/apiserver/request/requestinfo.go @@ -6,9 +6,11 @@ import ( "k8s.io/apimachinery/pkg/api/validation/path" metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" "net/http" "strings" @@ -46,11 +48,15 @@ type RequestInfo struct { // Cluster of requested resource, this is empty in single-cluster environment Cluster string + + // Scope of requested resource. + ResourceScope string } type RequestInfoFactory struct { APIPrefixes sets.String GrouplessAPIPrefixes sets.String + GlobalResources []schema.GroupResource } // NewRequestInfo returns the information from the http request. If error is not nil, RequestInfo holds the information as best it is known before the failure @@ -188,6 +194,8 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er // parsing successful, so we now know the proper value for .Parts requestInfo.Parts = currentParts + requestInfo.ResourceScope = r.resolveResourceScope(requestInfo) + // parts look like: resource/resourceName/subresource/other/stuff/we/don't/interpret switch { case len(requestInfo.Parts) >= 3 && !specialVerbsNoSubresources.Has(requestInfo.Verb): @@ -264,3 +272,21 @@ func splitPath(path string) []string { } return strings.Split(path, "/") } + +func (r *RequestInfoFactory) resolveResourceScope(request RequestInfo) string { + for _, globalResource := range r.GlobalResources { + if globalResource.Group == request.APIGroup && + globalResource.Resource == request.Resource { + return iamv1alpha2.GlobalScope + } + } + if request.Namespace != "" { + return iamv1alpha2.NamespaceScope + } + + if request.Workspace != "" { + return iamv1alpha2.WorkspaceScope + } + + return iamv1alpha2.ClusterScope +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrole.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrole.go new file mode 100644 index 0000000000000000000000000000000000000000..5393ec2d2376f4001f492dc633f57b682454483f --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrole.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// FakeGlobalRoles implements GlobalRoleInterface +type FakeGlobalRoles struct { + Fake *FakeIamV1alpha2 +} + +var globalrolesResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "globalroles"} + +var globalrolesKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "GlobalRole"} + +// Get takes name of the globalRole, and returns the corresponding globalRole object, and an error if there is any. +func (c *FakeGlobalRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.GlobalRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(globalrolesResource, name), &v1alpha2.GlobalRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRole), err +} + +// List takes label and field selectors, and returns the list of GlobalRoles that match those selectors. +func (c *FakeGlobalRoles) List(opts v1.ListOptions) (result *v1alpha2.GlobalRoleList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(globalrolesResource, globalrolesKind, opts), &v1alpha2.GlobalRoleList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.GlobalRoleList{ListMeta: obj.(*v1alpha2.GlobalRoleList).ListMeta} + for _, item := range obj.(*v1alpha2.GlobalRoleList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested globalRoles. +func (c *FakeGlobalRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(globalrolesResource, opts)) +} + +// Create takes the representation of a globalRole and creates it. Returns the server's representation of the globalRole, and an error, if there is any. +func (c *FakeGlobalRoles) Create(globalRole *v1alpha2.GlobalRole) (result *v1alpha2.GlobalRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(globalrolesResource, globalRole), &v1alpha2.GlobalRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRole), err +} + +// Update takes the representation of a globalRole and updates it. Returns the server's representation of the globalRole, and an error, if there is any. +func (c *FakeGlobalRoles) Update(globalRole *v1alpha2.GlobalRole) (result *v1alpha2.GlobalRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(globalrolesResource, globalRole), &v1alpha2.GlobalRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRole), err +} + +// Delete takes name of the globalRole and deletes it. Returns an error if one occurs. +func (c *FakeGlobalRoles) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(globalrolesResource, name), &v1alpha2.GlobalRole{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeGlobalRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(globalrolesResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha2.GlobalRoleList{}) + return err +} + +// Patch applies the patch and returns the patched globalRole. +func (c *FakeGlobalRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(globalrolesResource, name, pt, data, subresources...), &v1alpha2.GlobalRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRole), err +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrolebinding.go new file mode 100644 index 0000000000000000000000000000000000000000..161de682ac26209efd40d703b5d0b3ed3f206c3d --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrolebinding.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// FakeGlobalRoleBindings implements GlobalRoleBindingInterface +type FakeGlobalRoleBindings struct { + Fake *FakeIamV1alpha2 +} + +var globalrolebindingsResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "globalrolebindings"} + +var globalrolebindingsKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "GlobalRoleBinding"} + +// Get takes name of the globalRoleBinding, and returns the corresponding globalRoleBinding object, and an error if there is any. +func (c *FakeGlobalRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.GlobalRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(globalrolebindingsResource, name), &v1alpha2.GlobalRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRoleBinding), err +} + +// List takes label and field selectors, and returns the list of GlobalRoleBindings that match those selectors. +func (c *FakeGlobalRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.GlobalRoleBindingList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(globalrolebindingsResource, globalrolebindingsKind, opts), &v1alpha2.GlobalRoleBindingList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.GlobalRoleBindingList{ListMeta: obj.(*v1alpha2.GlobalRoleBindingList).ListMeta} + for _, item := range obj.(*v1alpha2.GlobalRoleBindingList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested globalRoleBindings. +func (c *FakeGlobalRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(globalrolebindingsResource, opts)) +} + +// Create takes the representation of a globalRoleBinding and creates it. Returns the server's representation of the globalRoleBinding, and an error, if there is any. +func (c *FakeGlobalRoleBindings) Create(globalRoleBinding *v1alpha2.GlobalRoleBinding) (result *v1alpha2.GlobalRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(globalrolebindingsResource, globalRoleBinding), &v1alpha2.GlobalRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRoleBinding), err +} + +// Update takes the representation of a globalRoleBinding and updates it. Returns the server's representation of the globalRoleBinding, and an error, if there is any. +func (c *FakeGlobalRoleBindings) Update(globalRoleBinding *v1alpha2.GlobalRoleBinding) (result *v1alpha2.GlobalRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(globalrolebindingsResource, globalRoleBinding), &v1alpha2.GlobalRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRoleBinding), err +} + +// Delete takes name of the globalRoleBinding and deletes it. Returns an error if one occurs. +func (c *FakeGlobalRoleBindings) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(globalrolebindingsResource, name), &v1alpha2.GlobalRoleBinding{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeGlobalRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(globalrolebindingsResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha2.GlobalRoleBindingList{}) + return err +} + +// Patch applies the patch and returns the patched globalRoleBinding. +func (c *FakeGlobalRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(globalrolebindingsResource, name, pt, data, subresources...), &v1alpha2.GlobalRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRoleBinding), err +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_iam_client.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_iam_client.go index 7b8ccc866bac17092b3af8382f87009bee1be391..ce41b4a89c12486708182e415a11e3a891c238bd 100644 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_iam_client.go +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_iam_client.go @@ -28,22 +28,26 @@ type FakeIamV1alpha2 struct { *testing.Fake } -func (c *FakeIamV1alpha2) PolicyRules() v1alpha2.PolicyRuleInterface { - return &FakePolicyRules{c} +func (c *FakeIamV1alpha2) GlobalRoles() v1alpha2.GlobalRoleInterface { + return &FakeGlobalRoles{c} } -func (c *FakeIamV1alpha2) Roles() v1alpha2.RoleInterface { - return &FakeRoles{c} -} - -func (c *FakeIamV1alpha2) RoleBindings() v1alpha2.RoleBindingInterface { - return &FakeRoleBindings{c} +func (c *FakeIamV1alpha2) GlobalRoleBindings() v1alpha2.GlobalRoleBindingInterface { + return &FakeGlobalRoleBindings{c} } func (c *FakeIamV1alpha2) Users() v1alpha2.UserInterface { return &FakeUsers{c} } +func (c *FakeIamV1alpha2) WorkspaceRoles() v1alpha2.WorkspaceRoleInterface { + return &FakeWorkspaceRoles{c} +} + +func (c *FakeIamV1alpha2) WorkspaceRoleBindings() v1alpha2.WorkspaceRoleBindingInterface { + return &FakeWorkspaceRoleBindings{c} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeIamV1alpha2) RESTClient() rest.Interface { diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_policyrule.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_policyrule.go deleted file mode 100644 index b9e63645b4a7715d00d4354917187d5e897ffffe..0000000000000000000000000000000000000000 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_policyrule.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" -) - -// FakePolicyRules implements PolicyRuleInterface -type FakePolicyRules struct { - Fake *FakeIamV1alpha2 -} - -var policyrulesResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "policyrules"} - -var policyrulesKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "PolicyRule"} - -// Get takes name of the policyRule, and returns the corresponding policyRule object, and an error if there is any. -func (c *FakePolicyRules) Get(name string, options v1.GetOptions) (result *v1alpha2.PolicyRule, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(policyrulesResource, name), &v1alpha2.PolicyRule{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.PolicyRule), err -} - -// List takes label and field selectors, and returns the list of PolicyRules that match those selectors. -func (c *FakePolicyRules) List(opts v1.ListOptions) (result *v1alpha2.PolicyRuleList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(policyrulesResource, policyrulesKind, opts), &v1alpha2.PolicyRuleList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha2.PolicyRuleList{ListMeta: obj.(*v1alpha2.PolicyRuleList).ListMeta} - for _, item := range obj.(*v1alpha2.PolicyRuleList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested policyRules. -func (c *FakePolicyRules) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(policyrulesResource, opts)) -} - -// Create takes the representation of a policyRule and creates it. Returns the server's representation of the policyRule, and an error, if there is any. -func (c *FakePolicyRules) Create(policyRule *v1alpha2.PolicyRule) (result *v1alpha2.PolicyRule, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(policyrulesResource, policyRule), &v1alpha2.PolicyRule{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.PolicyRule), err -} - -// Update takes the representation of a policyRule and updates it. Returns the server's representation of the policyRule, and an error, if there is any. -func (c *FakePolicyRules) Update(policyRule *v1alpha2.PolicyRule) (result *v1alpha2.PolicyRule, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(policyrulesResource, policyRule), &v1alpha2.PolicyRule{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.PolicyRule), err -} - -// Delete takes name of the policyRule and deletes it. Returns an error if one occurs. -func (c *FakePolicyRules) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(policyrulesResource, name), &v1alpha2.PolicyRule{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakePolicyRules) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(policyrulesResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha2.PolicyRuleList{}) - return err -} - -// Patch applies the patch and returns the patched policyRule. -func (c *FakePolicyRules) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.PolicyRule, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(policyrulesResource, name, pt, data, subresources...), &v1alpha2.PolicyRule{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.PolicyRule), err -} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_role.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_role.go deleted file mode 100644 index eef005e6db89cf020b28a1c3e3c8f68d38e45ae6..0000000000000000000000000000000000000000 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_role.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" -) - -// FakeRoles implements RoleInterface -type FakeRoles struct { - Fake *FakeIamV1alpha2 -} - -var rolesResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "roles"} - -var rolesKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "Role"} - -// Get takes name of the role, and returns the corresponding role object, and an error if there is any. -func (c *FakeRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.Role, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(rolesResource, name), &v1alpha2.Role{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.Role), err -} - -// List takes label and field selectors, and returns the list of Roles that match those selectors. -func (c *FakeRoles) List(opts v1.ListOptions) (result *v1alpha2.RoleList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(rolesResource, rolesKind, opts), &v1alpha2.RoleList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha2.RoleList{ListMeta: obj.(*v1alpha2.RoleList).ListMeta} - for _, item := range obj.(*v1alpha2.RoleList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested roles. -func (c *FakeRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(rolesResource, opts)) -} - -// Create takes the representation of a role and creates it. Returns the server's representation of the role, and an error, if there is any. -func (c *FakeRoles) Create(role *v1alpha2.Role) (result *v1alpha2.Role, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(rolesResource, role), &v1alpha2.Role{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.Role), err -} - -// Update takes the representation of a role and updates it. Returns the server's representation of the role, and an error, if there is any. -func (c *FakeRoles) Update(role *v1alpha2.Role) (result *v1alpha2.Role, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(rolesResource, role), &v1alpha2.Role{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.Role), err -} - -// Delete takes name of the role and deletes it. Returns an error if one occurs. -func (c *FakeRoles) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(rolesResource, name), &v1alpha2.Role{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(rolesResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha2.RoleList{}) - return err -} - -// Patch applies the patch and returns the patched role. -func (c *FakeRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.Role, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(rolesResource, name, pt, data, subresources...), &v1alpha2.Role{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.Role), err -} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_rolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_rolebinding.go deleted file mode 100644 index 55966eb016eecc40e633b6d761c403a67814a046..0000000000000000000000000000000000000000 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_rolebinding.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" -) - -// FakeRoleBindings implements RoleBindingInterface -type FakeRoleBindings struct { - Fake *FakeIamV1alpha2 -} - -var rolebindingsResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "rolebindings"} - -var rolebindingsKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "RoleBinding"} - -// Get takes name of the roleBinding, and returns the corresponding roleBinding object, and an error if there is any. -func (c *FakeRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.RoleBinding, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(rolebindingsResource, name), &v1alpha2.RoleBinding{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.RoleBinding), err -} - -// List takes label and field selectors, and returns the list of RoleBindings that match those selectors. -func (c *FakeRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.RoleBindingList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(rolebindingsResource, rolebindingsKind, opts), &v1alpha2.RoleBindingList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha2.RoleBindingList{ListMeta: obj.(*v1alpha2.RoleBindingList).ListMeta} - for _, item := range obj.(*v1alpha2.RoleBindingList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested roleBindings. -func (c *FakeRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(rolebindingsResource, opts)) -} - -// Create takes the representation of a roleBinding and creates it. Returns the server's representation of the roleBinding, and an error, if there is any. -func (c *FakeRoleBindings) Create(roleBinding *v1alpha2.RoleBinding) (result *v1alpha2.RoleBinding, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(rolebindingsResource, roleBinding), &v1alpha2.RoleBinding{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.RoleBinding), err -} - -// Update takes the representation of a roleBinding and updates it. Returns the server's representation of the roleBinding, and an error, if there is any. -func (c *FakeRoleBindings) Update(roleBinding *v1alpha2.RoleBinding) (result *v1alpha2.RoleBinding, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(rolebindingsResource, roleBinding), &v1alpha2.RoleBinding{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.RoleBinding), err -} - -// Delete takes name of the roleBinding and deletes it. Returns an error if one occurs. -func (c *FakeRoleBindings) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(rolebindingsResource, name), &v1alpha2.RoleBinding{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(rolebindingsResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha2.RoleBindingList{}) - return err -} - -// Patch applies the patch and returns the patched roleBinding. -func (c *FakeRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.RoleBinding, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(rolebindingsResource, name, pt, data, subresources...), &v1alpha2.RoleBinding{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.RoleBinding), err -} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerole.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerole.go new file mode 100644 index 0000000000000000000000000000000000000000..b7f81ed2ecb2bf523c920c3aef3f118a5fb2d05a --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerole.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// FakeWorkspaceRoles implements WorkspaceRoleInterface +type FakeWorkspaceRoles struct { + Fake *FakeIamV1alpha2 +} + +var workspacerolesResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "workspaceroles"} + +var workspacerolesKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "WorkspaceRole"} + +// Get takes name of the workspaceRole, and returns the corresponding workspaceRole object, and an error if there is any. +func (c *FakeWorkspaceRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.WorkspaceRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(workspacerolesResource, name), &v1alpha2.WorkspaceRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRole), err +} + +// List takes label and field selectors, and returns the list of WorkspaceRoles that match those selectors. +func (c *FakeWorkspaceRoles) List(opts v1.ListOptions) (result *v1alpha2.WorkspaceRoleList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(workspacerolesResource, workspacerolesKind, opts), &v1alpha2.WorkspaceRoleList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.WorkspaceRoleList{ListMeta: obj.(*v1alpha2.WorkspaceRoleList).ListMeta} + for _, item := range obj.(*v1alpha2.WorkspaceRoleList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested workspaceRoles. +func (c *FakeWorkspaceRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(workspacerolesResource, opts)) +} + +// Create takes the representation of a workspaceRole and creates it. Returns the server's representation of the workspaceRole, and an error, if there is any. +func (c *FakeWorkspaceRoles) Create(workspaceRole *v1alpha2.WorkspaceRole) (result *v1alpha2.WorkspaceRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(workspacerolesResource, workspaceRole), &v1alpha2.WorkspaceRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRole), err +} + +// Update takes the representation of a workspaceRole and updates it. Returns the server's representation of the workspaceRole, and an error, if there is any. +func (c *FakeWorkspaceRoles) Update(workspaceRole *v1alpha2.WorkspaceRole) (result *v1alpha2.WorkspaceRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(workspacerolesResource, workspaceRole), &v1alpha2.WorkspaceRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRole), err +} + +// Delete takes name of the workspaceRole and deletes it. Returns an error if one occurs. +func (c *FakeWorkspaceRoles) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(workspacerolesResource, name), &v1alpha2.WorkspaceRole{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeWorkspaceRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(workspacerolesResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha2.WorkspaceRoleList{}) + return err +} + +// Patch applies the patch and returns the patched workspaceRole. +func (c *FakeWorkspaceRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(workspacerolesResource, name, pt, data, subresources...), &v1alpha2.WorkspaceRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRole), err +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerolebinding.go new file mode 100644 index 0000000000000000000000000000000000000000..96504cdcb5fe77a42def469fde669a5d5d802ecf --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerolebinding.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// FakeWorkspaceRoleBindings implements WorkspaceRoleBindingInterface +type FakeWorkspaceRoleBindings struct { + Fake *FakeIamV1alpha2 +} + +var workspacerolebindingsResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "workspacerolebindings"} + +var workspacerolebindingsKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "WorkspaceRoleBinding"} + +// Get takes name of the workspaceRoleBinding, and returns the corresponding workspaceRoleBinding object, and an error if there is any. +func (c *FakeWorkspaceRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.WorkspaceRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(workspacerolebindingsResource, name), &v1alpha2.WorkspaceRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRoleBinding), err +} + +// List takes label and field selectors, and returns the list of WorkspaceRoleBindings that match those selectors. +func (c *FakeWorkspaceRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.WorkspaceRoleBindingList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(workspacerolebindingsResource, workspacerolebindingsKind, opts), &v1alpha2.WorkspaceRoleBindingList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.WorkspaceRoleBindingList{ListMeta: obj.(*v1alpha2.WorkspaceRoleBindingList).ListMeta} + for _, item := range obj.(*v1alpha2.WorkspaceRoleBindingList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested workspaceRoleBindings. +func (c *FakeWorkspaceRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(workspacerolebindingsResource, opts)) +} + +// Create takes the representation of a workspaceRoleBinding and creates it. Returns the server's representation of the workspaceRoleBinding, and an error, if there is any. +func (c *FakeWorkspaceRoleBindings) Create(workspaceRoleBinding *v1alpha2.WorkspaceRoleBinding) (result *v1alpha2.WorkspaceRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(workspacerolebindingsResource, workspaceRoleBinding), &v1alpha2.WorkspaceRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRoleBinding), err +} + +// Update takes the representation of a workspaceRoleBinding and updates it. Returns the server's representation of the workspaceRoleBinding, and an error, if there is any. +func (c *FakeWorkspaceRoleBindings) Update(workspaceRoleBinding *v1alpha2.WorkspaceRoleBinding) (result *v1alpha2.WorkspaceRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(workspacerolebindingsResource, workspaceRoleBinding), &v1alpha2.WorkspaceRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRoleBinding), err +} + +// Delete takes name of the workspaceRoleBinding and deletes it. Returns an error if one occurs. +func (c *FakeWorkspaceRoleBindings) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(workspacerolebindingsResource, name), &v1alpha2.WorkspaceRoleBinding{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeWorkspaceRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(workspacerolebindingsResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha2.WorkspaceRoleBindingList{}) + return err +} + +// Patch applies the patch and returns the patched workspaceRoleBinding. +func (c *FakeWorkspaceRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(workspacerolebindingsResource, name, pt, data, subresources...), &v1alpha2.WorkspaceRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRoleBinding), err +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/generated_expansion.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/generated_expansion.go index cae484a26e2a9f471b32c576e3cd6874cb2bb72f..edc5b88b55b61f6f20e907437715df47a5bbc7fc 100644 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/generated_expansion.go @@ -18,10 +18,12 @@ limitations under the License. package v1alpha2 -type PolicyRuleExpansion interface{} +type GlobalRoleExpansion interface{} -type RoleExpansion interface{} - -type RoleBindingExpansion interface{} +type GlobalRoleBindingExpansion interface{} type UserExpansion interface{} + +type WorkspaceRoleExpansion interface{} + +type WorkspaceRoleBindingExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/policyrule.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrole.go similarity index 52% rename from pkg/client/clientset/versioned/typed/iam/v1alpha2/policyrule.go rename to pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrole.go index 61515de5a7007025474cc11437e4e5f2f7be0e07..ae6e9eeb3f6d32652ed27c46b4d6a8fda8020f33 100644 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/policyrule.go +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrole.go @@ -29,42 +29,42 @@ import ( scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" ) -// PolicyRulesGetter has a method to return a PolicyRuleInterface. +// GlobalRolesGetter has a method to return a GlobalRoleInterface. // A group's client should implement this interface. -type PolicyRulesGetter interface { - PolicyRules() PolicyRuleInterface +type GlobalRolesGetter interface { + GlobalRoles() GlobalRoleInterface } -// PolicyRuleInterface has methods to work with PolicyRule resources. -type PolicyRuleInterface interface { - Create(*v1alpha2.PolicyRule) (*v1alpha2.PolicyRule, error) - Update(*v1alpha2.PolicyRule) (*v1alpha2.PolicyRule, error) +// GlobalRoleInterface has methods to work with GlobalRole resources. +type GlobalRoleInterface interface { + Create(*v1alpha2.GlobalRole) (*v1alpha2.GlobalRole, error) + Update(*v1alpha2.GlobalRole) (*v1alpha2.GlobalRole, error) Delete(name string, options *v1.DeleteOptions) error DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha2.PolicyRule, error) - List(opts v1.ListOptions) (*v1alpha2.PolicyRuleList, error) + Get(name string, options v1.GetOptions) (*v1alpha2.GlobalRole, error) + List(opts v1.ListOptions) (*v1alpha2.GlobalRoleList, error) Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.PolicyRule, err error) - PolicyRuleExpansion + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRole, err error) + GlobalRoleExpansion } -// policyRules implements PolicyRuleInterface -type policyRules struct { +// globalRoles implements GlobalRoleInterface +type globalRoles struct { client rest.Interface } -// newPolicyRules returns a PolicyRules -func newPolicyRules(c *IamV1alpha2Client) *policyRules { - return &policyRules{ +// newGlobalRoles returns a GlobalRoles +func newGlobalRoles(c *IamV1alpha2Client) *globalRoles { + return &globalRoles{ client: c.RESTClient(), } } -// Get takes name of the policyRule, and returns the corresponding policyRule object, and an error if there is any. -func (c *policyRules) Get(name string, options v1.GetOptions) (result *v1alpha2.PolicyRule, err error) { - result = &v1alpha2.PolicyRule{} +// Get takes name of the globalRole, and returns the corresponding globalRole object, and an error if there is any. +func (c *globalRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.GlobalRole, err error) { + result = &v1alpha2.GlobalRole{} err = c.client.Get(). - Resource("policyrules"). + Resource("globalroles"). Name(name). VersionedParams(&options, scheme.ParameterCodec). Do(). @@ -72,15 +72,15 @@ func (c *policyRules) Get(name string, options v1.GetOptions) (result *v1alpha2. return } -// List takes label and field selectors, and returns the list of PolicyRules that match those selectors. -func (c *policyRules) List(opts v1.ListOptions) (result *v1alpha2.PolicyRuleList, err error) { +// List takes label and field selectors, and returns the list of GlobalRoles that match those selectors. +func (c *globalRoles) List(opts v1.ListOptions) (result *v1alpha2.GlobalRoleList, err error) { var timeout time.Duration if opts.TimeoutSeconds != nil { timeout = time.Duration(*opts.TimeoutSeconds) * time.Second } - result = &v1alpha2.PolicyRuleList{} + result = &v1alpha2.GlobalRoleList{} err = c.client.Get(). - Resource("policyrules"). + Resource("globalroles"). VersionedParams(&opts, scheme.ParameterCodec). Timeout(timeout). Do(). @@ -88,47 +88,47 @@ func (c *policyRules) List(opts v1.ListOptions) (result *v1alpha2.PolicyRuleList return } -// Watch returns a watch.Interface that watches the requested policyRules. -func (c *policyRules) Watch(opts v1.ListOptions) (watch.Interface, error) { +// Watch returns a watch.Interface that watches the requested globalRoles. +func (c *globalRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { var timeout time.Duration if opts.TimeoutSeconds != nil { timeout = time.Duration(*opts.TimeoutSeconds) * time.Second } opts.Watch = true return c.client.Get(). - Resource("policyrules"). + Resource("globalroles"). VersionedParams(&opts, scheme.ParameterCodec). Timeout(timeout). Watch() } -// Create takes the representation of a policyRule and creates it. Returns the server's representation of the policyRule, and an error, if there is any. -func (c *policyRules) Create(policyRule *v1alpha2.PolicyRule) (result *v1alpha2.PolicyRule, err error) { - result = &v1alpha2.PolicyRule{} +// Create takes the representation of a globalRole and creates it. Returns the server's representation of the globalRole, and an error, if there is any. +func (c *globalRoles) Create(globalRole *v1alpha2.GlobalRole) (result *v1alpha2.GlobalRole, err error) { + result = &v1alpha2.GlobalRole{} err = c.client.Post(). - Resource("policyrules"). - Body(policyRule). + Resource("globalroles"). + Body(globalRole). Do(). Into(result) return } -// Update takes the representation of a policyRule and updates it. Returns the server's representation of the policyRule, and an error, if there is any. -func (c *policyRules) Update(policyRule *v1alpha2.PolicyRule) (result *v1alpha2.PolicyRule, err error) { - result = &v1alpha2.PolicyRule{} +// Update takes the representation of a globalRole and updates it. Returns the server's representation of the globalRole, and an error, if there is any. +func (c *globalRoles) Update(globalRole *v1alpha2.GlobalRole) (result *v1alpha2.GlobalRole, err error) { + result = &v1alpha2.GlobalRole{} err = c.client.Put(). - Resource("policyrules"). - Name(policyRule.Name). - Body(policyRule). + Resource("globalroles"). + Name(globalRole.Name). + Body(globalRole). Do(). Into(result) return } -// Delete takes name of the policyRule and deletes it. Returns an error if one occurs. -func (c *policyRules) Delete(name string, options *v1.DeleteOptions) error { +// Delete takes name of the globalRole and deletes it. Returns an error if one occurs. +func (c *globalRoles) Delete(name string, options *v1.DeleteOptions) error { return c.client.Delete(). - Resource("policyrules"). + Resource("globalroles"). Name(name). Body(options). Do(). @@ -136,13 +136,13 @@ func (c *policyRules) Delete(name string, options *v1.DeleteOptions) error { } // DeleteCollection deletes a collection of objects. -func (c *policyRules) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { +func (c *globalRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { var timeout time.Duration if listOptions.TimeoutSeconds != nil { timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second } return c.client.Delete(). - Resource("policyrules"). + Resource("globalroles"). VersionedParams(&listOptions, scheme.ParameterCodec). Timeout(timeout). Body(options). @@ -150,11 +150,11 @@ func (c *policyRules) DeleteCollection(options *v1.DeleteOptions, listOptions v1 Error() } -// Patch applies the patch and returns the patched policyRule. -func (c *policyRules) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.PolicyRule, err error) { - result = &v1alpha2.PolicyRule{} +// Patch applies the patch and returns the patched globalRole. +func (c *globalRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRole, err error) { + result = &v1alpha2.GlobalRole{} err = c.client.Patch(pt). - Resource("policyrules"). + Resource("globalroles"). SubResource(subresources...). Name(name). Body(data). diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrolebinding.go new file mode 100644 index 0000000000000000000000000000000000000000..8fdec09aa64f1073db0ea0c1be37849cad782d25 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrolebinding.go @@ -0,0 +1,164 @@ +/* +Copyright 2019 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// GlobalRoleBindingsGetter has a method to return a GlobalRoleBindingInterface. +// A group's client should implement this interface. +type GlobalRoleBindingsGetter interface { + GlobalRoleBindings() GlobalRoleBindingInterface +} + +// GlobalRoleBindingInterface has methods to work with GlobalRoleBinding resources. +type GlobalRoleBindingInterface interface { + Create(*v1alpha2.GlobalRoleBinding) (*v1alpha2.GlobalRoleBinding, error) + Update(*v1alpha2.GlobalRoleBinding) (*v1alpha2.GlobalRoleBinding, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha2.GlobalRoleBinding, error) + List(opts v1.ListOptions) (*v1alpha2.GlobalRoleBindingList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRoleBinding, err error) + GlobalRoleBindingExpansion +} + +// globalRoleBindings implements GlobalRoleBindingInterface +type globalRoleBindings struct { + client rest.Interface +} + +// newGlobalRoleBindings returns a GlobalRoleBindings +func newGlobalRoleBindings(c *IamV1alpha2Client) *globalRoleBindings { + return &globalRoleBindings{ + client: c.RESTClient(), + } +} + +// Get takes name of the globalRoleBinding, and returns the corresponding globalRoleBinding object, and an error if there is any. +func (c *globalRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.GlobalRoleBinding, err error) { + result = &v1alpha2.GlobalRoleBinding{} + err = c.client.Get(). + Resource("globalrolebindings"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of GlobalRoleBindings that match those selectors. +func (c *globalRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.GlobalRoleBindingList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.GlobalRoleBindingList{} + err = c.client.Get(). + Resource("globalrolebindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested globalRoleBindings. +func (c *globalRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("globalrolebindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a globalRoleBinding and creates it. Returns the server's representation of the globalRoleBinding, and an error, if there is any. +func (c *globalRoleBindings) Create(globalRoleBinding *v1alpha2.GlobalRoleBinding) (result *v1alpha2.GlobalRoleBinding, err error) { + result = &v1alpha2.GlobalRoleBinding{} + err = c.client.Post(). + Resource("globalrolebindings"). + Body(globalRoleBinding). + Do(). + Into(result) + return +} + +// Update takes the representation of a globalRoleBinding and updates it. Returns the server's representation of the globalRoleBinding, and an error, if there is any. +func (c *globalRoleBindings) Update(globalRoleBinding *v1alpha2.GlobalRoleBinding) (result *v1alpha2.GlobalRoleBinding, err error) { + result = &v1alpha2.GlobalRoleBinding{} + err = c.client.Put(). + Resource("globalrolebindings"). + Name(globalRoleBinding.Name). + Body(globalRoleBinding). + Do(). + Into(result) + return +} + +// Delete takes name of the globalRoleBinding and deletes it. Returns an error if one occurs. +func (c *globalRoleBindings) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("globalrolebindings"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *globalRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("globalrolebindings"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched globalRoleBinding. +func (c *globalRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRoleBinding, err error) { + result = &v1alpha2.GlobalRoleBinding{} + err = c.client.Patch(pt). + Resource("globalrolebindings"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/iam_client.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/iam_client.go index 7bd5bafdf9245374c51668295776795ca8b5456b..be20644fb5854d80ea10bf2f925a21357bb1f31f 100644 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/iam_client.go +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/iam_client.go @@ -26,10 +26,11 @@ import ( type IamV1alpha2Interface interface { RESTClient() rest.Interface - PolicyRulesGetter - RolesGetter - RoleBindingsGetter + GlobalRolesGetter + GlobalRoleBindingsGetter UsersGetter + WorkspaceRolesGetter + WorkspaceRoleBindingsGetter } // IamV1alpha2Client is used to interact with features provided by the iam.kubesphere.io group. @@ -37,22 +38,26 @@ type IamV1alpha2Client struct { restClient rest.Interface } -func (c *IamV1alpha2Client) PolicyRules() PolicyRuleInterface { - return newPolicyRules(c) +func (c *IamV1alpha2Client) GlobalRoles() GlobalRoleInterface { + return newGlobalRoles(c) } -func (c *IamV1alpha2Client) Roles() RoleInterface { - return newRoles(c) -} - -func (c *IamV1alpha2Client) RoleBindings() RoleBindingInterface { - return newRoleBindings(c) +func (c *IamV1alpha2Client) GlobalRoleBindings() GlobalRoleBindingInterface { + return newGlobalRoleBindings(c) } func (c *IamV1alpha2Client) Users() UserInterface { return newUsers(c) } +func (c *IamV1alpha2Client) WorkspaceRoles() WorkspaceRoleInterface { + return newWorkspaceRoles(c) +} + +func (c *IamV1alpha2Client) WorkspaceRoleBindings() WorkspaceRoleBindingInterface { + return newWorkspaceRoleBindings(c) +} + // NewForConfig creates a new IamV1alpha2Client for the given config. func NewForConfig(c *rest.Config) (*IamV1alpha2Client, error) { config := *c diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/role.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/role.go deleted file mode 100644 index 35b3a84acefab9ddaf12bb96235e4a354fa1020b..0000000000000000000000000000000000000000 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/role.go +++ /dev/null @@ -1,164 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha2 - -import ( - "time" - - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" - scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" -) - -// RolesGetter has a method to return a RoleInterface. -// A group's client should implement this interface. -type RolesGetter interface { - Roles() RoleInterface -} - -// RoleInterface has methods to work with Role resources. -type RoleInterface interface { - Create(*v1alpha2.Role) (*v1alpha2.Role, error) - Update(*v1alpha2.Role) (*v1alpha2.Role, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha2.Role, error) - List(opts v1.ListOptions) (*v1alpha2.RoleList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.Role, err error) - RoleExpansion -} - -// roles implements RoleInterface -type roles struct { - client rest.Interface -} - -// newRoles returns a Roles -func newRoles(c *IamV1alpha2Client) *roles { - return &roles{ - client: c.RESTClient(), - } -} - -// Get takes name of the role, and returns the corresponding role object, and an error if there is any. -func (c *roles) Get(name string, options v1.GetOptions) (result *v1alpha2.Role, err error) { - result = &v1alpha2.Role{} - err = c.client.Get(). - Resource("roles"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Roles that match those selectors. -func (c *roles) List(opts v1.ListOptions) (result *v1alpha2.RoleList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha2.RoleList{} - err = c.client.Get(). - Resource("roles"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested roles. -func (c *roles) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Resource("roles"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a role and creates it. Returns the server's representation of the role, and an error, if there is any. -func (c *roles) Create(role *v1alpha2.Role) (result *v1alpha2.Role, err error) { - result = &v1alpha2.Role{} - err = c.client.Post(). - Resource("roles"). - Body(role). - Do(). - Into(result) - return -} - -// Update takes the representation of a role and updates it. Returns the server's representation of the role, and an error, if there is any. -func (c *roles) Update(role *v1alpha2.Role) (result *v1alpha2.Role, err error) { - result = &v1alpha2.Role{} - err = c.client.Put(). - Resource("roles"). - Name(role.Name). - Body(role). - Do(). - Into(result) - return -} - -// Delete takes name of the role and deletes it. Returns an error if one occurs. -func (c *roles) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("roles"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *roles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Resource("roles"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched role. -func (c *roles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.Role, err error) { - result = &v1alpha2.Role{} - err = c.client.Patch(pt). - Resource("roles"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/rolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/rolebinding.go deleted file mode 100644 index 7dffa28a4499968e09928b9b0f1e5d848da52ae6..0000000000000000000000000000000000000000 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/rolebinding.go +++ /dev/null @@ -1,164 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha2 - -import ( - "time" - - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" - scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" -) - -// RoleBindingsGetter has a method to return a RoleBindingInterface. -// A group's client should implement this interface. -type RoleBindingsGetter interface { - RoleBindings() RoleBindingInterface -} - -// RoleBindingInterface has methods to work with RoleBinding resources. -type RoleBindingInterface interface { - Create(*v1alpha2.RoleBinding) (*v1alpha2.RoleBinding, error) - Update(*v1alpha2.RoleBinding) (*v1alpha2.RoleBinding, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha2.RoleBinding, error) - List(opts v1.ListOptions) (*v1alpha2.RoleBindingList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.RoleBinding, err error) - RoleBindingExpansion -} - -// roleBindings implements RoleBindingInterface -type roleBindings struct { - client rest.Interface -} - -// newRoleBindings returns a RoleBindings -func newRoleBindings(c *IamV1alpha2Client) *roleBindings { - return &roleBindings{ - client: c.RESTClient(), - } -} - -// Get takes name of the roleBinding, and returns the corresponding roleBinding object, and an error if there is any. -func (c *roleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.RoleBinding, err error) { - result = &v1alpha2.RoleBinding{} - err = c.client.Get(). - Resource("rolebindings"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of RoleBindings that match those selectors. -func (c *roleBindings) List(opts v1.ListOptions) (result *v1alpha2.RoleBindingList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha2.RoleBindingList{} - err = c.client.Get(). - Resource("rolebindings"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested roleBindings. -func (c *roleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Resource("rolebindings"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a roleBinding and creates it. Returns the server's representation of the roleBinding, and an error, if there is any. -func (c *roleBindings) Create(roleBinding *v1alpha2.RoleBinding) (result *v1alpha2.RoleBinding, err error) { - result = &v1alpha2.RoleBinding{} - err = c.client.Post(). - Resource("rolebindings"). - Body(roleBinding). - Do(). - Into(result) - return -} - -// Update takes the representation of a roleBinding and updates it. Returns the server's representation of the roleBinding, and an error, if there is any. -func (c *roleBindings) Update(roleBinding *v1alpha2.RoleBinding) (result *v1alpha2.RoleBinding, err error) { - result = &v1alpha2.RoleBinding{} - err = c.client.Put(). - Resource("rolebindings"). - Name(roleBinding.Name). - Body(roleBinding). - Do(). - Into(result) - return -} - -// Delete takes name of the roleBinding and deletes it. Returns an error if one occurs. -func (c *roleBindings) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("rolebindings"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *roleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Resource("rolebindings"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched roleBinding. -func (c *roleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.RoleBinding, err error) { - result = &v1alpha2.RoleBinding{} - err = c.client.Patch(pt). - Resource("rolebindings"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerole.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerole.go new file mode 100644 index 0000000000000000000000000000000000000000..e47e4c8719d6f3dd6f4c1f0f3200238b8b02bd71 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerole.go @@ -0,0 +1,164 @@ +/* +Copyright 2019 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// WorkspaceRolesGetter has a method to return a WorkspaceRoleInterface. +// A group's client should implement this interface. +type WorkspaceRolesGetter interface { + WorkspaceRoles() WorkspaceRoleInterface +} + +// WorkspaceRoleInterface has methods to work with WorkspaceRole resources. +type WorkspaceRoleInterface interface { + Create(*v1alpha2.WorkspaceRole) (*v1alpha2.WorkspaceRole, error) + Update(*v1alpha2.WorkspaceRole) (*v1alpha2.WorkspaceRole, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha2.WorkspaceRole, error) + List(opts v1.ListOptions) (*v1alpha2.WorkspaceRoleList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRole, err error) + WorkspaceRoleExpansion +} + +// workspaceRoles implements WorkspaceRoleInterface +type workspaceRoles struct { + client rest.Interface +} + +// newWorkspaceRoles returns a WorkspaceRoles +func newWorkspaceRoles(c *IamV1alpha2Client) *workspaceRoles { + return &workspaceRoles{ + client: c.RESTClient(), + } +} + +// Get takes name of the workspaceRole, and returns the corresponding workspaceRole object, and an error if there is any. +func (c *workspaceRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.WorkspaceRole, err error) { + result = &v1alpha2.WorkspaceRole{} + err = c.client.Get(). + Resource("workspaceroles"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of WorkspaceRoles that match those selectors. +func (c *workspaceRoles) List(opts v1.ListOptions) (result *v1alpha2.WorkspaceRoleList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.WorkspaceRoleList{} + err = c.client.Get(). + Resource("workspaceroles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested workspaceRoles. +func (c *workspaceRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("workspaceroles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a workspaceRole and creates it. Returns the server's representation of the workspaceRole, and an error, if there is any. +func (c *workspaceRoles) Create(workspaceRole *v1alpha2.WorkspaceRole) (result *v1alpha2.WorkspaceRole, err error) { + result = &v1alpha2.WorkspaceRole{} + err = c.client.Post(). + Resource("workspaceroles"). + Body(workspaceRole). + Do(). + Into(result) + return +} + +// Update takes the representation of a workspaceRole and updates it. Returns the server's representation of the workspaceRole, and an error, if there is any. +func (c *workspaceRoles) Update(workspaceRole *v1alpha2.WorkspaceRole) (result *v1alpha2.WorkspaceRole, err error) { + result = &v1alpha2.WorkspaceRole{} + err = c.client.Put(). + Resource("workspaceroles"). + Name(workspaceRole.Name). + Body(workspaceRole). + Do(). + Into(result) + return +} + +// Delete takes name of the workspaceRole and deletes it. Returns an error if one occurs. +func (c *workspaceRoles) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("workspaceroles"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *workspaceRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("workspaceroles"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched workspaceRole. +func (c *workspaceRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRole, err error) { + result = &v1alpha2.WorkspaceRole{} + err = c.client.Patch(pt). + Resource("workspaceroles"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerolebinding.go new file mode 100644 index 0000000000000000000000000000000000000000..17c4406e40201594a6253972d7088edbfa2dc80e --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerolebinding.go @@ -0,0 +1,164 @@ +/* +Copyright 2019 The KubeSphere 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// WorkspaceRoleBindingsGetter has a method to return a WorkspaceRoleBindingInterface. +// A group's client should implement this interface. +type WorkspaceRoleBindingsGetter interface { + WorkspaceRoleBindings() WorkspaceRoleBindingInterface +} + +// WorkspaceRoleBindingInterface has methods to work with WorkspaceRoleBinding resources. +type WorkspaceRoleBindingInterface interface { + Create(*v1alpha2.WorkspaceRoleBinding) (*v1alpha2.WorkspaceRoleBinding, error) + Update(*v1alpha2.WorkspaceRoleBinding) (*v1alpha2.WorkspaceRoleBinding, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha2.WorkspaceRoleBinding, error) + List(opts v1.ListOptions) (*v1alpha2.WorkspaceRoleBindingList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRoleBinding, err error) + WorkspaceRoleBindingExpansion +} + +// workspaceRoleBindings implements WorkspaceRoleBindingInterface +type workspaceRoleBindings struct { + client rest.Interface +} + +// newWorkspaceRoleBindings returns a WorkspaceRoleBindings +func newWorkspaceRoleBindings(c *IamV1alpha2Client) *workspaceRoleBindings { + return &workspaceRoleBindings{ + client: c.RESTClient(), + } +} + +// Get takes name of the workspaceRoleBinding, and returns the corresponding workspaceRoleBinding object, and an error if there is any. +func (c *workspaceRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.WorkspaceRoleBinding, err error) { + result = &v1alpha2.WorkspaceRoleBinding{} + err = c.client.Get(). + Resource("workspacerolebindings"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of WorkspaceRoleBindings that match those selectors. +func (c *workspaceRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.WorkspaceRoleBindingList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.WorkspaceRoleBindingList{} + err = c.client.Get(). + Resource("workspacerolebindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested workspaceRoleBindings. +func (c *workspaceRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("workspacerolebindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a workspaceRoleBinding and creates it. Returns the server's representation of the workspaceRoleBinding, and an error, if there is any. +func (c *workspaceRoleBindings) Create(workspaceRoleBinding *v1alpha2.WorkspaceRoleBinding) (result *v1alpha2.WorkspaceRoleBinding, err error) { + result = &v1alpha2.WorkspaceRoleBinding{} + err = c.client.Post(). + Resource("workspacerolebindings"). + Body(workspaceRoleBinding). + Do(). + Into(result) + return +} + +// Update takes the representation of a workspaceRoleBinding and updates it. Returns the server's representation of the workspaceRoleBinding, and an error, if there is any. +func (c *workspaceRoleBindings) Update(workspaceRoleBinding *v1alpha2.WorkspaceRoleBinding) (result *v1alpha2.WorkspaceRoleBinding, err error) { + result = &v1alpha2.WorkspaceRoleBinding{} + err = c.client.Put(). + Resource("workspacerolebindings"). + Name(workspaceRoleBinding.Name). + Body(workspaceRoleBinding). + Do(). + Into(result) + return +} + +// Delete takes name of the workspaceRoleBinding and deletes it. Returns an error if one occurs. +func (c *workspaceRoleBindings) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("workspacerolebindings"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *workspaceRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("workspacerolebindings"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched workspaceRoleBinding. +func (c *workspaceRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRoleBinding, err error) { + result = &v1alpha2.WorkspaceRoleBinding{} + err = c.client.Patch(pt). + Resource("workspacerolebindings"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 34fb32f12d15b7aa4f9baed654ec899373615865..83831c6ba0546bad09a2b96e54aae0a73a27f21d 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -79,14 +79,16 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha3().Pipelines().Informer()}, nil // Group=iam.kubesphere.io, Version=v1alpha2 - case v1alpha2.SchemeGroupVersion.WithResource("policyrules"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().PolicyRules().Informer()}, nil - case v1alpha2.SchemeGroupVersion.WithResource("roles"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().Roles().Informer()}, nil - case v1alpha2.SchemeGroupVersion.WithResource("rolebindings"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().RoleBindings().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("globalroles"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().GlobalRoles().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("globalrolebindings"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().GlobalRoleBindings().Informer()}, nil case v1alpha2.SchemeGroupVersion.WithResource("users"): return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().Users().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("workspaceroles"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().WorkspaceRoles().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("workspacerolebindings"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().WorkspaceRoleBindings().Informer()}, nil // Group=network.kubesphere.io, Version=v1alpha1 case networkv1alpha1.SchemeGroupVersion.WithResource("namespacenetworkpolicies"): diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/policyrule.go b/pkg/client/informers/externalversions/iam/v1alpha2/globalrole.go similarity index 69% rename from pkg/client/informers/externalversions/iam/v1alpha2/policyrule.go rename to pkg/client/informers/externalversions/iam/v1alpha2/globalrole.go index c5a2fd756b1ed0e5495822066549c0bb51777553..c02b920319c3e1ff7488f63227a3db2e3e7247da 100644 --- a/pkg/client/informers/externalversions/iam/v1alpha2/policyrule.go +++ b/pkg/client/informers/externalversions/iam/v1alpha2/globalrole.go @@ -31,58 +31,58 @@ import ( v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" ) -// PolicyRuleInformer provides access to a shared informer and lister for -// PolicyRules. -type PolicyRuleInformer interface { +// GlobalRoleInformer provides access to a shared informer and lister for +// GlobalRoles. +type GlobalRoleInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha2.PolicyRuleLister + Lister() v1alpha2.GlobalRoleLister } -type policyRuleInformer struct { +type globalRoleInformer struct { factory internalinterfaces.SharedInformerFactory tweakListOptions internalinterfaces.TweakListOptionsFunc } -// NewPolicyRuleInformer constructs a new informer for PolicyRule type. +// NewGlobalRoleInformer constructs a new informer for GlobalRole type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewPolicyRuleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredPolicyRuleInformer(client, resyncPeriod, indexers, nil) +func NewGlobalRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredGlobalRoleInformer(client, resyncPeriod, indexers, nil) } -// NewFilteredPolicyRuleInformer constructs a new informer for PolicyRule type. +// NewFilteredGlobalRoleInformer constructs a new informer for GlobalRole type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewFilteredPolicyRuleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { +func NewFilteredGlobalRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { return cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func(options v1.ListOptions) (runtime.Object, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.IamV1alpha2().PolicyRules().List(options) + return client.IamV1alpha2().GlobalRoles().List(options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.IamV1alpha2().PolicyRules().Watch(options) + return client.IamV1alpha2().GlobalRoles().Watch(options) }, }, - &iamv1alpha2.PolicyRule{}, + &iamv1alpha2.GlobalRole{}, resyncPeriod, indexers, ) } -func (f *policyRuleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredPolicyRuleInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +func (f *globalRoleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredGlobalRoleInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) } -func (f *policyRuleInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&iamv1alpha2.PolicyRule{}, f.defaultInformer) +func (f *globalRoleInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&iamv1alpha2.GlobalRole{}, f.defaultInformer) } -func (f *policyRuleInformer) Lister() v1alpha2.PolicyRuleLister { - return v1alpha2.NewPolicyRuleLister(f.Informer().GetIndexer()) +func (f *globalRoleInformer) Lister() v1alpha2.GlobalRoleLister { + return v1alpha2.NewGlobalRoleLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/role.go b/pkg/client/informers/externalversions/iam/v1alpha2/globalrolebinding.go similarity index 55% rename from pkg/client/informers/externalversions/iam/v1alpha2/role.go rename to pkg/client/informers/externalversions/iam/v1alpha2/globalrolebinding.go index af36316edf771fd61559bfb33115843452ff01fd..d5376608b0fe8097bc76985df3493e236af2fc08 100644 --- a/pkg/client/informers/externalversions/iam/v1alpha2/role.go +++ b/pkg/client/informers/externalversions/iam/v1alpha2/globalrolebinding.go @@ -31,58 +31,58 @@ import ( v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" ) -// RoleInformer provides access to a shared informer and lister for -// Roles. -type RoleInformer interface { +// GlobalRoleBindingInformer provides access to a shared informer and lister for +// GlobalRoleBindings. +type GlobalRoleBindingInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha2.RoleLister + Lister() v1alpha2.GlobalRoleBindingLister } -type roleInformer struct { +type globalRoleBindingInformer struct { factory internalinterfaces.SharedInformerFactory tweakListOptions internalinterfaces.TweakListOptionsFunc } -// NewRoleInformer constructs a new informer for Role type. +// NewGlobalRoleBindingInformer constructs a new informer for GlobalRoleBinding type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredRoleInformer(client, resyncPeriod, indexers, nil) +func NewGlobalRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredGlobalRoleBindingInformer(client, resyncPeriod, indexers, nil) } -// NewFilteredRoleInformer constructs a new informer for Role type. +// NewFilteredGlobalRoleBindingInformer constructs a new informer for GlobalRoleBinding type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewFilteredRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { +func NewFilteredGlobalRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { return cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func(options v1.ListOptions) (runtime.Object, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.IamV1alpha2().Roles().List(options) + return client.IamV1alpha2().GlobalRoleBindings().List(options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.IamV1alpha2().Roles().Watch(options) + return client.IamV1alpha2().GlobalRoleBindings().Watch(options) }, }, - &iamv1alpha2.Role{}, + &iamv1alpha2.GlobalRoleBinding{}, resyncPeriod, indexers, ) } -func (f *roleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredRoleInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +func (f *globalRoleBindingInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredGlobalRoleBindingInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) } -func (f *roleInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&iamv1alpha2.Role{}, f.defaultInformer) +func (f *globalRoleBindingInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&iamv1alpha2.GlobalRoleBinding{}, f.defaultInformer) } -func (f *roleInformer) Lister() v1alpha2.RoleLister { - return v1alpha2.NewRoleLister(f.Informer().GetIndexer()) +func (f *globalRoleBindingInformer) Lister() v1alpha2.GlobalRoleBindingLister { + return v1alpha2.NewGlobalRoleBindingLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/interface.go b/pkg/client/informers/externalversions/iam/v1alpha2/interface.go index 674febe6497781be856583829e7aa457d763d0d5..f6765fd6189f0e38e62976bee5336770c857cf6c 100644 --- a/pkg/client/informers/externalversions/iam/v1alpha2/interface.go +++ b/pkg/client/informers/externalversions/iam/v1alpha2/interface.go @@ -24,14 +24,16 @@ import ( // Interface provides access to all the informers in this group version. type Interface interface { - // PolicyRules returns a PolicyRuleInformer. - PolicyRules() PolicyRuleInformer - // Roles returns a RoleInformer. - Roles() RoleInformer - // RoleBindings returns a RoleBindingInformer. - RoleBindings() RoleBindingInformer + // GlobalRoles returns a GlobalRoleInformer. + GlobalRoles() GlobalRoleInformer + // GlobalRoleBindings returns a GlobalRoleBindingInformer. + GlobalRoleBindings() GlobalRoleBindingInformer // Users returns a UserInformer. Users() UserInformer + // WorkspaceRoles returns a WorkspaceRoleInformer. + WorkspaceRoles() WorkspaceRoleInformer + // WorkspaceRoleBindings returns a WorkspaceRoleBindingInformer. + WorkspaceRoleBindings() WorkspaceRoleBindingInformer } type version struct { @@ -45,22 +47,27 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} } -// PolicyRules returns a PolicyRuleInformer. -func (v *version) PolicyRules() PolicyRuleInformer { - return &policyRuleInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +// GlobalRoles returns a GlobalRoleInformer. +func (v *version) GlobalRoles() GlobalRoleInformer { + return &globalRoleInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } -// Roles returns a RoleInformer. -func (v *version) Roles() RoleInformer { - return &roleInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} -} - -// RoleBindings returns a RoleBindingInformer. -func (v *version) RoleBindings() RoleBindingInformer { - return &roleBindingInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +// GlobalRoleBindings returns a GlobalRoleBindingInformer. +func (v *version) GlobalRoleBindings() GlobalRoleBindingInformer { + return &globalRoleBindingInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } // Users returns a UserInformer. func (v *version) Users() UserInformer { return &userInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } + +// WorkspaceRoles returns a WorkspaceRoleInformer. +func (v *version) WorkspaceRoles() WorkspaceRoleInformer { + return &workspaceRoleInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// WorkspaceRoleBindings returns a WorkspaceRoleBindingInformer. +func (v *version) WorkspaceRoleBindings() WorkspaceRoleBindingInformer { + return &workspaceRoleBindingInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/rolebinding.go b/pkg/client/informers/externalversions/iam/v1alpha2/workspacerole.go similarity index 57% rename from pkg/client/informers/externalversions/iam/v1alpha2/rolebinding.go rename to pkg/client/informers/externalversions/iam/v1alpha2/workspacerole.go index 5cc80d0d294ee49a55d7e41a6a4bbb60d8e90f91..be8b37540341e983fcbadc8679a2d9f3d2ee404d 100644 --- a/pkg/client/informers/externalversions/iam/v1alpha2/rolebinding.go +++ b/pkg/client/informers/externalversions/iam/v1alpha2/workspacerole.go @@ -31,58 +31,58 @@ import ( v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" ) -// RoleBindingInformer provides access to a shared informer and lister for -// RoleBindings. -type RoleBindingInformer interface { +// WorkspaceRoleInformer provides access to a shared informer and lister for +// WorkspaceRoles. +type WorkspaceRoleInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha2.RoleBindingLister + Lister() v1alpha2.WorkspaceRoleLister } -type roleBindingInformer struct { +type workspaceRoleInformer struct { factory internalinterfaces.SharedInformerFactory tweakListOptions internalinterfaces.TweakListOptionsFunc } -// NewRoleBindingInformer constructs a new informer for RoleBinding type. +// NewWorkspaceRoleInformer constructs a new informer for WorkspaceRole type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredRoleBindingInformer(client, resyncPeriod, indexers, nil) +func NewWorkspaceRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredWorkspaceRoleInformer(client, resyncPeriod, indexers, nil) } -// NewFilteredRoleBindingInformer constructs a new informer for RoleBinding type. +// NewFilteredWorkspaceRoleInformer constructs a new informer for WorkspaceRole type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewFilteredRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { +func NewFilteredWorkspaceRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { return cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func(options v1.ListOptions) (runtime.Object, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.IamV1alpha2().RoleBindings().List(options) + return client.IamV1alpha2().WorkspaceRoles().List(options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.IamV1alpha2().RoleBindings().Watch(options) + return client.IamV1alpha2().WorkspaceRoles().Watch(options) }, }, - &iamv1alpha2.RoleBinding{}, + &iamv1alpha2.WorkspaceRole{}, resyncPeriod, indexers, ) } -func (f *roleBindingInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredRoleBindingInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +func (f *workspaceRoleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredWorkspaceRoleInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) } -func (f *roleBindingInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&iamv1alpha2.RoleBinding{}, f.defaultInformer) +func (f *workspaceRoleInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&iamv1alpha2.WorkspaceRole{}, f.defaultInformer) } -func (f *roleBindingInformer) Lister() v1alpha2.RoleBindingLister { - return v1alpha2.NewRoleBindingLister(f.Informer().GetIndexer()) +func (f *workspaceRoleInformer) Lister() v1alpha2.WorkspaceRoleLister { + return v1alpha2.NewWorkspaceRoleLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/workspacerolebinding.go b/pkg/client/informers/externalversions/iam/v1alpha2/workspacerolebinding.go new file mode 100644 index 0000000000000000000000000000000000000000..18b18b4d7485013c0866dc69632512576d2078f4 --- /dev/null +++ b/pkg/client/informers/externalversions/iam/v1alpha2/workspacerolebinding.go @@ -0,0 +1,88 @@ +/* +Copyright 2019 The KubeSphere 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" +) + +// WorkspaceRoleBindingInformer provides access to a shared informer and lister for +// WorkspaceRoleBindings. +type WorkspaceRoleBindingInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha2.WorkspaceRoleBindingLister +} + +type workspaceRoleBindingInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewWorkspaceRoleBindingInformer constructs a new informer for WorkspaceRoleBinding type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewWorkspaceRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredWorkspaceRoleBindingInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredWorkspaceRoleBindingInformer constructs a new informer for WorkspaceRoleBinding type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredWorkspaceRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.IamV1alpha2().WorkspaceRoleBindings().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.IamV1alpha2().WorkspaceRoleBindings().Watch(options) + }, + }, + &iamv1alpha2.WorkspaceRoleBinding{}, + resyncPeriod, + indexers, + ) +} + +func (f *workspaceRoleBindingInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredWorkspaceRoleBindingInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *workspaceRoleBindingInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&iamv1alpha2.WorkspaceRoleBinding{}, f.defaultInformer) +} + +func (f *workspaceRoleBindingInformer) Lister() v1alpha2.WorkspaceRoleBindingLister { + return v1alpha2.NewWorkspaceRoleBindingLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/listers/iam/v1alpha2/expansion_generated.go b/pkg/client/listers/iam/v1alpha2/expansion_generated.go index 5262c5697f6253c1a2b872355aa06dba58374f16..7d88b069d5e73ccc84ce78672789a738961b0db4 100644 --- a/pkg/client/listers/iam/v1alpha2/expansion_generated.go +++ b/pkg/client/listers/iam/v1alpha2/expansion_generated.go @@ -18,18 +18,22 @@ limitations under the License. package v1alpha2 -// PolicyRuleListerExpansion allows custom methods to be added to -// PolicyRuleLister. -type PolicyRuleListerExpansion interface{} +// GlobalRoleListerExpansion allows custom methods to be added to +// GlobalRoleLister. +type GlobalRoleListerExpansion interface{} -// RoleListerExpansion allows custom methods to be added to -// RoleLister. -type RoleListerExpansion interface{} - -// RoleBindingListerExpansion allows custom methods to be added to -// RoleBindingLister. -type RoleBindingListerExpansion interface{} +// GlobalRoleBindingListerExpansion allows custom methods to be added to +// GlobalRoleBindingLister. +type GlobalRoleBindingListerExpansion interface{} // UserListerExpansion allows custom methods to be added to // UserLister. type UserListerExpansion interface{} + +// WorkspaceRoleListerExpansion allows custom methods to be added to +// WorkspaceRoleLister. +type WorkspaceRoleListerExpansion interface{} + +// WorkspaceRoleBindingListerExpansion allows custom methods to be added to +// WorkspaceRoleBindingLister. +type WorkspaceRoleBindingListerExpansion interface{} diff --git a/pkg/client/listers/iam/v1alpha2/policyrule.go b/pkg/client/listers/iam/v1alpha2/globalrole.go similarity index 51% rename from pkg/client/listers/iam/v1alpha2/policyrule.go rename to pkg/client/listers/iam/v1alpha2/globalrole.go index ec9d01ea3728e54dabc1c6a830fd1a355945be21..b601db3130b1b467cf35cfb08a132bb987f85996 100644 --- a/pkg/client/listers/iam/v1alpha2/policyrule.go +++ b/pkg/client/listers/iam/v1alpha2/globalrole.go @@ -25,41 +25,41 @@ import ( v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" ) -// PolicyRuleLister helps list PolicyRules. -type PolicyRuleLister interface { - // List lists all PolicyRules in the indexer. - List(selector labels.Selector) (ret []*v1alpha2.PolicyRule, err error) - // Get retrieves the PolicyRule from the index for a given name. - Get(name string) (*v1alpha2.PolicyRule, error) - PolicyRuleListerExpansion +// GlobalRoleLister helps list GlobalRoles. +type GlobalRoleLister interface { + // List lists all GlobalRoles in the indexer. + List(selector labels.Selector) (ret []*v1alpha2.GlobalRole, err error) + // Get retrieves the GlobalRole from the index for a given name. + Get(name string) (*v1alpha2.GlobalRole, error) + GlobalRoleListerExpansion } -// policyRuleLister implements the PolicyRuleLister interface. -type policyRuleLister struct { +// globalRoleLister implements the GlobalRoleLister interface. +type globalRoleLister struct { indexer cache.Indexer } -// NewPolicyRuleLister returns a new PolicyRuleLister. -func NewPolicyRuleLister(indexer cache.Indexer) PolicyRuleLister { - return &policyRuleLister{indexer: indexer} +// NewGlobalRoleLister returns a new GlobalRoleLister. +func NewGlobalRoleLister(indexer cache.Indexer) GlobalRoleLister { + return &globalRoleLister{indexer: indexer} } -// List lists all PolicyRules in the indexer. -func (s *policyRuleLister) List(selector labels.Selector) (ret []*v1alpha2.PolicyRule, err error) { +// List lists all GlobalRoles in the indexer. +func (s *globalRoleLister) List(selector labels.Selector) (ret []*v1alpha2.GlobalRole, err error) { err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha2.PolicyRule)) + ret = append(ret, m.(*v1alpha2.GlobalRole)) }) return ret, err } -// Get retrieves the PolicyRule from the index for a given name. -func (s *policyRuleLister) Get(name string) (*v1alpha2.PolicyRule, error) { +// Get retrieves the GlobalRole from the index for a given name. +func (s *globalRoleLister) Get(name string) (*v1alpha2.GlobalRole, error) { obj, exists, err := s.indexer.GetByKey(name) if err != nil { return nil, err } if !exists { - return nil, errors.NewNotFound(v1alpha2.Resource("policyrule"), name) + return nil, errors.NewNotFound(v1alpha2.Resource("globalrole"), name) } - return obj.(*v1alpha2.PolicyRule), nil + return obj.(*v1alpha2.GlobalRole), nil } diff --git a/pkg/client/listers/iam/v1alpha2/globalrolebinding.go b/pkg/client/listers/iam/v1alpha2/globalrolebinding.go new file mode 100644 index 0000000000000000000000000000000000000000..ea549adc1842c65d957a578e7d28a13ba562f130 --- /dev/null +++ b/pkg/client/listers/iam/v1alpha2/globalrolebinding.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// GlobalRoleBindingLister helps list GlobalRoleBindings. +type GlobalRoleBindingLister interface { + // List lists all GlobalRoleBindings in the indexer. + List(selector labels.Selector) (ret []*v1alpha2.GlobalRoleBinding, err error) + // Get retrieves the GlobalRoleBinding from the index for a given name. + Get(name string) (*v1alpha2.GlobalRoleBinding, error) + GlobalRoleBindingListerExpansion +} + +// globalRoleBindingLister implements the GlobalRoleBindingLister interface. +type globalRoleBindingLister struct { + indexer cache.Indexer +} + +// NewGlobalRoleBindingLister returns a new GlobalRoleBindingLister. +func NewGlobalRoleBindingLister(indexer cache.Indexer) GlobalRoleBindingLister { + return &globalRoleBindingLister{indexer: indexer} +} + +// List lists all GlobalRoleBindings in the indexer. +func (s *globalRoleBindingLister) List(selector labels.Selector) (ret []*v1alpha2.GlobalRoleBinding, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.GlobalRoleBinding)) + }) + return ret, err +} + +// Get retrieves the GlobalRoleBinding from the index for a given name. +func (s *globalRoleBindingLister) Get(name string) (*v1alpha2.GlobalRoleBinding, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("globalrolebinding"), name) + } + return obj.(*v1alpha2.GlobalRoleBinding), nil +} diff --git a/pkg/client/listers/iam/v1alpha2/role.go b/pkg/client/listers/iam/v1alpha2/role.go deleted file mode 100644 index 15dd8f0532a573c7d85033b6f7ed78db51115ecb..0000000000000000000000000000000000000000 --- a/pkg/client/listers/iam/v1alpha2/role.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha2 - -import ( - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" -) - -// RoleLister helps list Roles. -type RoleLister interface { - // List lists all Roles in the indexer. - List(selector labels.Selector) (ret []*v1alpha2.Role, err error) - // Get retrieves the Role from the index for a given name. - Get(name string) (*v1alpha2.Role, error) - RoleListerExpansion -} - -// roleLister implements the RoleLister interface. -type roleLister struct { - indexer cache.Indexer -} - -// NewRoleLister returns a new RoleLister. -func NewRoleLister(indexer cache.Indexer) RoleLister { - return &roleLister{indexer: indexer} -} - -// List lists all Roles in the indexer. -func (s *roleLister) List(selector labels.Selector) (ret []*v1alpha2.Role, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha2.Role)) - }) - return ret, err -} - -// Get retrieves the Role from the index for a given name. -func (s *roleLister) Get(name string) (*v1alpha2.Role, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha2.Resource("role"), name) - } - return obj.(*v1alpha2.Role), nil -} diff --git a/pkg/client/listers/iam/v1alpha2/rolebinding.go b/pkg/client/listers/iam/v1alpha2/rolebinding.go deleted file mode 100644 index d3704c2969921923407af851fb3b09e5b06365f3..0000000000000000000000000000000000000000 --- a/pkg/client/listers/iam/v1alpha2/rolebinding.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha2 - -import ( - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" -) - -// RoleBindingLister helps list RoleBindings. -type RoleBindingLister interface { - // List lists all RoleBindings in the indexer. - List(selector labels.Selector) (ret []*v1alpha2.RoleBinding, err error) - // Get retrieves the RoleBinding from the index for a given name. - Get(name string) (*v1alpha2.RoleBinding, error) - RoleBindingListerExpansion -} - -// roleBindingLister implements the RoleBindingLister interface. -type roleBindingLister struct { - indexer cache.Indexer -} - -// NewRoleBindingLister returns a new RoleBindingLister. -func NewRoleBindingLister(indexer cache.Indexer) RoleBindingLister { - return &roleBindingLister{indexer: indexer} -} - -// List lists all RoleBindings in the indexer. -func (s *roleBindingLister) List(selector labels.Selector) (ret []*v1alpha2.RoleBinding, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha2.RoleBinding)) - }) - return ret, err -} - -// Get retrieves the RoleBinding from the index for a given name. -func (s *roleBindingLister) Get(name string) (*v1alpha2.RoleBinding, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha2.Resource("rolebinding"), name) - } - return obj.(*v1alpha2.RoleBinding), nil -} diff --git a/pkg/client/listers/iam/v1alpha2/workspacerole.go b/pkg/client/listers/iam/v1alpha2/workspacerole.go new file mode 100644 index 0000000000000000000000000000000000000000..8e9e74965b64de4336020bc6155c2ce632d6c91f --- /dev/null +++ b/pkg/client/listers/iam/v1alpha2/workspacerole.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// WorkspaceRoleLister helps list WorkspaceRoles. +type WorkspaceRoleLister interface { + // List lists all WorkspaceRoles in the indexer. + List(selector labels.Selector) (ret []*v1alpha2.WorkspaceRole, err error) + // Get retrieves the WorkspaceRole from the index for a given name. + Get(name string) (*v1alpha2.WorkspaceRole, error) + WorkspaceRoleListerExpansion +} + +// workspaceRoleLister implements the WorkspaceRoleLister interface. +type workspaceRoleLister struct { + indexer cache.Indexer +} + +// NewWorkspaceRoleLister returns a new WorkspaceRoleLister. +func NewWorkspaceRoleLister(indexer cache.Indexer) WorkspaceRoleLister { + return &workspaceRoleLister{indexer: indexer} +} + +// List lists all WorkspaceRoles in the indexer. +func (s *workspaceRoleLister) List(selector labels.Selector) (ret []*v1alpha2.WorkspaceRole, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.WorkspaceRole)) + }) + return ret, err +} + +// Get retrieves the WorkspaceRole from the index for a given name. +func (s *workspaceRoleLister) Get(name string) (*v1alpha2.WorkspaceRole, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("workspacerole"), name) + } + return obj.(*v1alpha2.WorkspaceRole), nil +} diff --git a/pkg/client/listers/iam/v1alpha2/workspacerolebinding.go b/pkg/client/listers/iam/v1alpha2/workspacerolebinding.go new file mode 100644 index 0000000000000000000000000000000000000000..175f17d31b1eaf6d852e29eff35375606c3a76bb --- /dev/null +++ b/pkg/client/listers/iam/v1alpha2/workspacerolebinding.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// WorkspaceRoleBindingLister helps list WorkspaceRoleBindings. +type WorkspaceRoleBindingLister interface { + // List lists all WorkspaceRoleBindings in the indexer. + List(selector labels.Selector) (ret []*v1alpha2.WorkspaceRoleBinding, err error) + // Get retrieves the WorkspaceRoleBinding from the index for a given name. + Get(name string) (*v1alpha2.WorkspaceRoleBinding, error) + WorkspaceRoleBindingListerExpansion +} + +// workspaceRoleBindingLister implements the WorkspaceRoleBindingLister interface. +type workspaceRoleBindingLister struct { + indexer cache.Indexer +} + +// NewWorkspaceRoleBindingLister returns a new WorkspaceRoleBindingLister. +func NewWorkspaceRoleBindingLister(indexer cache.Indexer) WorkspaceRoleBindingLister { + return &workspaceRoleBindingLister{indexer: indexer} +} + +// List lists all WorkspaceRoleBindings in the indexer. +func (s *workspaceRoleBindingLister) List(selector labels.Selector) (ret []*v1alpha2.WorkspaceRoleBinding, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.WorkspaceRoleBinding)) + }) + return ret, err +} + +// Get retrieves the WorkspaceRoleBinding from the index for a given name. +func (s *workspaceRoleBindingLister) Get(name string) (*v1alpha2.WorkspaceRoleBinding, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("workspacerolebinding"), name) + } + return obj.(*v1alpha2.WorkspaceRoleBinding), nil +} diff --git a/pkg/kapis/iam/v1alpha2/handler.go b/pkg/kapis/iam/v1alpha2/handler.go index 37836f233e5da78df174454a788d6b48eafacb61..7a1a0cb9aabaad58398cbae4cc8fb3aec5871c6c 100644 --- a/pkg/kapis/iam/v1alpha2/handler.go +++ b/pkg/kapis/iam/v1alpha2/handler.go @@ -1,14 +1,20 @@ package v1alpha2 import ( + "fmt" "github.com/emicklei/go-restful" - "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/models/iam/am" "kubesphere.io/kubesphere/pkg/models/iam/im" - "strings" + resources "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" ) type iamHandler struct { @@ -23,18 +29,6 @@ func newIAMHandler(im im.IdentityManagementInterface, am am.AccessManagementInte } } -func (h *iamHandler) CreateUser(req *restful.Request, resp *restful.Response) { - panic("implement me") -} - -func (h *iamHandler) DeleteUser(req *restful.Request, resp *restful.Response) { - panic("implement me") -} - -func (h *iamHandler) ModifyUser(request *restful.Request, response *restful.Response) { - panic("implement me") -} - func (h *iamHandler) DescribeUser(req *restful.Request, resp *restful.Response) { username := req.PathParameter("user") user, err := h.im.DescribeUser(username) @@ -43,108 +37,196 @@ func (h *iamHandler) DescribeUser(req *restful.Request, resp *restful.Response) return } - globalRole, err := h.am.GetRoleOfUserInTargetScope(iamv1alpha2.GlobalScope, "", username) + globalRole, err := h.am.GetGlobalRoleOfUser(username) if err != nil { api.HandleInternalError(resp, req, err) return } + result := iamv1alpha2.UserDetail{User: user, GlobalRole: globalRole} resp.WriteEntity(result) } func (h *iamHandler) ListUsers(req *restful.Request, resp *restful.Response) { - panic("implement me") -} + queryParam := query.ParseQueryParameter(req) + result, err := h.im.ListUsers(queryParam) + if err != nil { + api.HandleInternalError(resp, req, err) + return + } + for i, item := range result.Items { + user := item.(*iamv1alpha2.User) + user = user.DeepCopy() + role, err := h.am.GetGlobalRoleOfUser(user.Name) + if err != nil && !errors.IsNotFound(err) { + klog.Error(err) + api.HandleInternalError(resp, req, err) + return + } + + if user.Annotations == nil { + user.Annotations = make(map[string]string, 0) + } + + if role != nil { + user.Annotations["iam.kubesphere.io/global-role"] = role.Name + } else { + user.Annotations["iam.kubesphere.io/global-role"] = "" + } + + result.Items[i] = user + } -func (h *iamHandler) ListUserRoles(req *restful.Request, resp *restful.Response) { - panic("implement me") -} + resp.WriteEntity(result) -func (h *iamHandler) ListRoles(req *restful.Request, resp *restful.Response) { - panic("implement me") } -func (h *iamHandler) ListRolesOfUser(req *restful.Request, resp *restful.Response) { - username := req.PathParameter("user") - - var roles []iamv1alpha2.Role - var err error - - if strings.HasSuffix(req.Request.URL.Path, "workspaceroles") { - roles, err = h.am.ListRolesOfUser(iamv1alpha2.WorkspaceScope, username) - } else if strings.HasSuffix(req.Request.URL.Path, "clusterroles") { - roles, err = h.am.ListRolesOfUser(iamv1alpha2.ClusterScope, username) - } else if strings.HasSuffix(req.Request.URL.Path, "namespaceroles") { - roles, err = h.am.ListRolesOfUser(iamv1alpha2.NamespaceScope, username) +func (h *iamHandler) ListRoles(req *restful.Request, resp *restful.Response) { + namespace := req.PathParameter("namespace") + queryParam := query.ParseQueryParameter(req) + result, err := h.am.ListRoles(namespace, queryParam) + if err != nil { + api.HandleInternalError(resp, req, err) + return } + resp.WriteEntity(result) +} +func (h *iamHandler) ListClusterRoles(req *restful.Request, resp *restful.Response) { + queryParam := query.ParseQueryParameter(req) + result, err := h.am.ListClusterRoles(queryParam) if err != nil { api.HandleInternalError(resp, req, err) return } + resp.WriteEntity(result) +} - result := iamv1alpha2.RoleList{ - TypeMeta: v1.TypeMeta{ - Kind: "List", - APIVersion: "v1", - }, - ListMeta: v1.ListMeta{}, - Items: roles, +func (h *iamHandler) ListGlobalRoles(req *restful.Request, resp *restful.Response) { + queryParam := query.ParseQueryParameter(req) + result, err := h.am.ListGlobalRoles(queryParam) + if err != nil { + api.HandleInternalError(resp, req, err) + return } - resp.WriteEntity(result) } -func (h *iamHandler) ListClusterRoles(req *restful.Request, resp *restful.Response) { - panic("implement me") -} func (h *iamHandler) ListRoleUsers(req *restful.Request, resp *restful.Response) { panic("implement me") } -// List users by namespace func (h *iamHandler) ListNamespaceUsers(req *restful.Request, resp *restful.Response) { - panic("implement me") -} + queryParam := query.ParseQueryParameter(req) + namespace := req.PathParameter("namespace") -func (h *iamHandler) ListClusterRoleUsers(req *restful.Request, resp *restful.Response) { - panic("implement me") -} + roleBindings, err := h.am.ListRoleBindings("", namespace) -func (h *iamHandler) ListClusterRoleRules(req *restful.Request, resp *restful.Response) { - panic("implement me") -} + if err != nil { + api.HandleInternalError(resp, req, err) + return + } -func (h *iamHandler) ListRoleRules(req *restful.Request, resp *restful.Response) { - panic("implement me") -} + users := make([]runtime.Object, 0) -func (h *iamHandler) ListWorkspaceRoles(request *restful.Request, response *restful.Response) { - panic("implement me") -} + for _, roleBinding := range roleBindings { + for _, subject := range roleBinding.Subjects { + if subject.Kind == iamv1alpha2.ResourceKindUser { + user, err := h.im.DescribeUser(subject.Name) -func (h *iamHandler) DescribeWorkspaceRole(request *restful.Request, response *restful.Response) { - panic("implement me") + if err != nil { + if errors.IsNotFound(err) { + klog.Errorf("orphan subject: %+v", subject) + continue + } + api.HandleInternalError(resp, req, err) + return + } + + user = user.DeepCopy() + + if user.Annotations == nil { + user.Annotations = make(map[string]string, 0) + } + + user.Annotations["iam.kubesphere.io/role"] = roleBinding.RoleRef.Name + + users = append(users, user) + } + } + } + + result := resources.DefaultList(users, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool { + return resources.DefaultObjectMetaCompare(left.(*corev1.Namespace).ObjectMeta, right.(*corev1.Namespace).ObjectMeta, field) + }, func(object runtime.Object, filter query.Filter) bool { + user := object.(*iamv1alpha2.User).ObjectMeta + return resources.DefaultObjectMetaFilter(user, filter) + }) + + resp.WriteEntity(result) } -func (h *iamHandler) ListWorkspaceRoleRules(request *restful.Request, response *restful.Response) { - panic("implement me") +func (h *iamHandler) ListWorkspaceRoles(request *restful.Request, response *restful.Response) { + queryParam := query.ParseQueryParameter(request) + workspace := request.PathParameter("workspace") + queryParam.Filters[query.FieldLabel] = query.Value(fmt.Sprintf("%s:%s", tenantv1alpha1.WorkspaceLabel, workspace)) + + result, err := h.am.ListWorkspaceRoles(queryParam) + if err != nil { + api.HandleInternalError(response, request, err) + return + } + response.WriteEntity(result) } func (h *iamHandler) ListWorkspaceUsers(request *restful.Request, response *restful.Response) { - panic("implement me") -} + queryParam := query.ParseQueryParameter(request) + workspace := request.PathParameter("workspace") -func (h *iamHandler) InviteUser(request *restful.Request, response *restful.Response) { - panic("implement me") -} + roleBindings, err := h.am.ListWorkspaceRoleBindings("", workspace) -func (h *iamHandler) RemoveUser(request *restful.Request, response *restful.Response) { - panic("implement me") -} + if err != nil { + api.HandleInternalError(response, request, err) + return + } -func (h *iamHandler) DescribeWorkspaceUser(request *restful.Request, response *restful.Response) { - panic("implement me") + users := make([]runtime.Object, 0) + + for _, roleBinding := range roleBindings { + for _, subject := range roleBinding.Subjects { + if subject.Kind == iamv1alpha2.ResourceKindUser { + user, err := h.im.DescribeUser(subject.Name) + + if err != nil { + if errors.IsNotFound(err) { + klog.Errorf("orphan subject: %+v", subject) + continue + } + api.HandleInternalError(response, request, err) + return + } + + user = user.DeepCopy() + + if user.Annotations == nil { + user.Annotations = make(map[string]string, 0) + } + + user.Annotations["iam.kubesphere.io/workspace-role"] = roleBinding.RoleRef.Name + + users = append(users, user) + } + } + } + + result := resources.DefaultList(users, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool { + return resources.DefaultObjectMetaCompare(left.(*corev1.Namespace).ObjectMeta, right.(*corev1.Namespace).ObjectMeta, field) + }, func(object runtime.Object, filter query.Filter) bool { + user := object.(*iamv1alpha2.User).ObjectMeta + return resources.DefaultObjectMetaFilter(user, filter) + }) + + response.WriteEntity(result) } diff --git a/pkg/kapis/iam/v1alpha2/register.go b/pkg/kapis/iam/v1alpha2/register.go index 9a9ce7b09fe22edf23ae3c465f579261ac5d8f91..3687b2f37018e0cfdd683a08dcd405f1048743c8 100644 --- a/pkg/kapis/iam/v1alpha2/register.go +++ b/pkg/kapis/iam/v1alpha2/register.go @@ -29,97 +29,69 @@ import ( "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models/iam/am" "kubesphere.io/kubesphere/pkg/models/iam/im" - "kubesphere.io/kubesphere/pkg/server/errors" "net/http" ) -const groupName = "iam.kubesphere.io" +const ( + GroupName = "iam.kubesphere.io" +) -var GroupVersion = schema.GroupVersion{Group: groupName, Version: "v1alpha2"} +var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} func AddToContainer(container *restful.Container, im im.IdentityManagementInterface, am am.AccessManagementInterface, options *authoptions.AuthenticationOptions) error { ws := runtime.NewWebService(GroupVersion) handler := newIAMHandler(im, am, options) + // global resource + ws.Route(ws.GET("/users"). + To(handler.ListUsers). + Doc("List all users."). + Returns(http.StatusOK, api.StatusOK, api.ListResult{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) + // global resource ws.Route(ws.GET("/users/{user}"). To(handler.DescribeUser). Doc("Retrieve user details."). Param(ws.PathParameter("user", "username")). Returns(http.StatusOK, api.StatusOK, iamv1alpha2.UserDetail{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - - ws.Route(ws.GET("/users/{user}/workspaceroles"). - To(handler.ListRolesOfUser). - Doc("Retrieve user roles in workspaces."). - Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, api.StatusOK, iamv1alpha2.RoleList{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - - ws.Route(ws.GET("/users/{user}/clusterroles"). - To(handler.ListRolesOfUser). - Doc("Retrieve user roles in clusters."). - Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, api.StatusOK, iamv1alpha2.RoleList{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - - ws.Route(ws.GET("/users/{user}/namespaceroles"). - To(handler.ListRolesOfUser). - Doc("Retrieve user roles in namespaces."). - Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, api.StatusOK, iamv1alpha2.RoleList{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - - ws.Route(ws.GET("/namespaces/{namespace}/roles"). - To(handler.ListRoles). - Doc("Retrieve the roles that are assigned to the user in the specified namespace."). - Param(ws.PathParameter("namespace", "kubernetes namespace")). + // global resource + ws.Route(ws.GET("/globalroles"). + To(handler.ListGlobalRoles). + Doc("List all cluster roles."). Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/clusterroles"). To(handler.ListClusterRoles). - Doc("List all cluster roles."). + Doc("List cluster roles."). Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - // TODO merge - //ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/users")) - ws.Route(ws.GET("/namespaces/{namespace}/users"). - To(handler.ListNamespaceUsers). - Doc("List all users in the specified namespace."). - Param(ws.PathParameter("namespace", "kubernetes namespace")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/clusterroles/{clusterrole}/users"). - To(handler.ListClusterRoleUsers). - Doc("List all users that are bound to the specified cluster role."). - Param(ws.PathParameter("clusterrole", "cluster role name")). + ws.Route(ws.GET("/workspaces/{workspace}/users"). + To(handler.ListWorkspaceUsers). + Doc("List all members in the specified workspace."). + Param(ws.PathParameter("workspace", "workspace name")). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/workspaces/{workspace}/roles"). + ws.Route(ws.GET("/workspaces/{workspace}/workspaceroles"). To(handler.ListWorkspaceRoles). Doc("List all workspace roles."). Param(ws.PathParameter("workspace", "workspace name")). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/workspaces/{workspace}/members"). - To(handler.ListWorkspaceUsers). - Doc("List all members in the specified workspace."). - Param(ws.PathParameter("workspace", "workspace name")). + ws.Route(ws.GET("/namespaces/{namespace}/users"). + To(handler.ListNamespaceUsers). + Doc("List all users in the specified namespace."). + Param(ws.PathParameter("namespace", "kubernetes namespace")). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - // TODO re-design - ws.Route(ws.POST("/workspaces/{workspace}/members"). - To(handler.InviteUser). - Doc("Invite a member to the specified workspace."). - Param(ws.PathParameter("workspace", "workspace name")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.DELETE("/workspaces/{workspace}/members/{member}"). - To(handler.RemoveUser). - Doc("Remove the specified member from the workspace."). - Param(ws.PathParameter("workspace", "workspace name")). - Param(ws.PathParameter("member", "username")). - Returns(http.StatusOK, api.StatusOK, errors.Error{}). + ws.Route(ws.GET("/namespaces/{namespace}/roles"). + To(handler.ListRoles). + Doc("List all roles in the specified namespace."). + Param(ws.PathParameter("namespace", "namespace")). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) container.Add(ws) diff --git a/pkg/kapis/resources/v1alpha2/register.go b/pkg/kapis/resources/v1alpha2/register.go index 557e46cea48fd9f5b1b8a49def6742dc732e2092..e635e0a88646b33283fe85af24130c67f87dfe96 100644 --- a/pkg/kapis/resources/v1alpha2/register.go +++ b/pkg/kapis/resources/v1alpha2/register.go @@ -71,7 +71,7 @@ func AddToContainer(c *restful.Container, client kubernetes.Interface, factory i Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterResourcesTag}). Doc("Cluster level resources"). - Param(webservice.PathParameter("resources", "cluster level resource type, e.g. nodes,workspaces,storageclasses,clusterroles.")). + Param(webservice.PathParameter("resources", "cluster level resource type, e.g. nodes,workspaces,storageclasses,clusterrole.")). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions, connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). Required(false). DataFormat("key=value,key~value"). diff --git a/pkg/kapis/resources/v1alpha3/handler.go b/pkg/kapis/resources/v1alpha3/handler.go index 38316c3e502d4e4c1c6dca123fa1704ab91df9fe..75a8f1f67925efab4a306bc6838be3a46f80a2e0 100644 --- a/pkg/kapis/resources/v1alpha3/handler.go +++ b/pkg/kapis/resources/v1alpha3/handler.go @@ -9,20 +9,20 @@ import ( "kubesphere.io/kubesphere/pkg/models/components" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" resourcev1alpha2 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/resource" - resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" "kubesphere.io/kubesphere/pkg/server/params" "strings" ) type Handler struct { - resourceGetterV1alpha3 *resourcev1alpha3.ResourceGetter + resourceGetterV1alpha3 *resource.ResourceGetter resourcesGetterV1alpha2 *resourcev1alpha2.ResourceGetter componentsGetter components.ComponentsGetter } func New(factory informers.InformerFactory) *Handler { return &Handler{ - resourceGetterV1alpha3: resourcev1alpha3.NewResourceGetter(factory), + resourceGetterV1alpha3: resource.NewResourceGetter(factory), resourcesGetterV1alpha2: resourcev1alpha2.NewResourceGetter(factory), componentsGetter: components.NewComponentsGetter(factory.KubernetesSharedInformerFactory()), } @@ -41,7 +41,7 @@ func (h *Handler) handleListResources(request *restful.Request, response *restfu return } - if err != resourcev1alpha3.ErrResourceNotSupported { + if err != resource.ErrResourceNotSupported { klog.Error(err) api.HandleInternalError(response, nil, err) return @@ -63,20 +63,20 @@ func (h *Handler) fallback(resourceType string, namespace string, q *query.Query orderBy := string(q.SortBy) limit, offset := q.Pagination.Limit, q.Pagination.Offset reverse := !q.Ascending - conditions := ¶ms.Conditions{} - for _, filter := range q.Filters { - switch filter.Field { + conditions := ¶ms.Conditions{Match: make(map[string]string, 0), Fuzzy: make(map[string]string, 0)} + for field, value := range q.Filters { + switch field { case query.FieldName: - conditions.Match[v1alpha2.Name] = string(filter.Value) + conditions.Match[v1alpha2.Name] = string(value) break case query.FieldCreationTimeStamp: - conditions.Match[v1alpha2.CreateTime] = string(filter.Value) + conditions.Match[v1alpha2.CreateTime] = string(value) break case query.FieldLastUpdateTimestamp: - conditions.Match[v1alpha2.UpdateTime] = string(filter.Value) + conditions.Match[v1alpha2.UpdateTime] = string(value) break case query.FieldLabel: - values := strings.SplitN(string(filter.Value), ":", 2) + values := strings.SplitN(string(value), ":", 2) if len(values) == 2 { conditions.Match[values[0]] = values[1] } else { @@ -84,7 +84,7 @@ func (h *Handler) fallback(resourceType string, namespace string, q *query.Query } break case query.FieldAnnotation: - values := strings.SplitN(string(filter.Value), ":", 2) + values := strings.SplitN(string(value), ":", 2) if len(values) == 2 { conditions.Match[v1alpha2.Annotation] = values[1] } else { @@ -92,13 +92,15 @@ func (h *Handler) fallback(resourceType string, namespace string, q *query.Query } break case query.FieldStatus: - conditions.Match[v1alpha2.Status] = string(filter.Value) + conditions.Match[v1alpha2.Status] = string(value) break case query.FieldOwnerReference: - conditions.Match[v1alpha2.Owner] = string(filter.Value) + conditions.Match[v1alpha2.Owner] = string(value) + break + default: + conditions.Match[string(field)] = string(value) break } - } result, err := h.resourcesGetterV1alpha2.ListResources(namespace, resourceType, conditions, orderBy, reverse, limit, offset) diff --git a/pkg/kapis/resources/v1alpha3/handler_test.go b/pkg/kapis/resources/v1alpha3/handler_test.go index 1fb67ebad342879d7f651e038e0bc8ba5919d42c..4fa8b2f2a7b929c74266270bc9e8e94e2a135d10 100644 --- a/pkg/kapis/resources/v1alpha3/handler_test.go +++ b/pkg/kapis/resources/v1alpha3/handler_test.go @@ -12,7 +12,7 @@ import ( "kubesphere.io/kubesphere/pkg/apiserver/query" fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" "kubesphere.io/kubesphere/pkg/informers" - resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" "testing" ) @@ -92,7 +92,7 @@ func listResources(namespace, resourceType string, query *query.Query, h *Handle return result, nil } - if err != resourcev1alpha3.ErrResourceNotSupported { + if err != resource.ErrResourceNotSupported { return nil, err } diff --git a/pkg/kapis/tenant/v1alpha2/handler.go b/pkg/kapis/tenant/v1alpha2/handler.go index ebbe12d8078dd35d5c4b7be0b21471a2f8f17805..04d1f9df68559d1b94873ef56f2b70341f39cd4a 100644 --- a/pkg/kapis/tenant/v1alpha2/handler.go +++ b/pkg/kapis/tenant/v1alpha2/handler.go @@ -5,25 +5,26 @@ import ( "github.com/emicklei/go-restful" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/apiserver/request" "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models/tenant" - "kubesphere.io/kubesphere/pkg/simple/client/k8s" ) type tenantHandler struct { tenant tenant.Interface } -func newTenantHandler(k8sClient k8s.Client, factory informers.InformerFactory) *tenantHandler { +func newTenantHandler(factory informers.InformerFactory) *tenantHandler { return &tenantHandler{ - tenant: tenant.New(k8sClient, factory), + tenant: tenant.New(factory), } } func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Response) { user, ok := request.UserFrom(req.Request.Context()) + queryParam := query.ParseQueryParameter(req) if !ok { err := errors.New("cannot obtain user info") @@ -32,7 +33,7 @@ func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Respo return } - result, err := h.tenant.ListWorkspaces(user) + result, err := h.tenant.ListWorkspaces(user, queryParam) if err != nil { api.HandleInternalError(resp, nil, err) @@ -44,6 +45,7 @@ func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Respo func (h *tenantHandler) ListNamespaces(req *restful.Request, resp *restful.Response) { user, ok := request.UserFrom(req.Request.Context()) + queryParam := query.ParseQueryParameter(req) if !ok { err := errors.New("cannot obtain user info") @@ -54,7 +56,7 @@ func (h *tenantHandler) ListNamespaces(req *restful.Request, resp *restful.Respo workspace := req.PathParameter("workspace") - result, err := h.tenant.ListNamespaces(user, workspace) + result, err := h.tenant.ListNamespaces(user, workspace, queryParam) if err != nil { api.HandleInternalError(resp, nil, err) diff --git a/pkg/kapis/tenant/v1alpha2/register.go b/pkg/kapis/tenant/v1alpha2/register.go index 6b5e845187c6d24c3f6428665dcf0614b89165b2..b0d38d53db78aba15fdcb838b9b9b3038a425d56 100644 --- a/pkg/kapis/tenant/v1alpha2/register.go +++ b/pkg/kapis/tenant/v1alpha2/register.go @@ -27,7 +27,6 @@ import ( "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/simple/client/k8s" "net/http" ) @@ -37,9 +36,9 @@ const ( var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} -func AddToContainer(c *restful.Container, k8sClient k8s.Client, factory informers.InformerFactory) error { +func AddToContainer(c *restful.Container, factory informers.InformerFactory) error { ws := runtime.NewWebService(GroupVersion) - handler := newTenantHandler(k8sClient, factory) + handler := newTenantHandler(factory) ws.Route(ws.GET("/workspaces"). To(handler.ListWorkspaces). diff --git a/pkg/models/iam/am/am.go b/pkg/models/iam/am/am.go index 5fbc6952313a3b7d5cbbe48c2260cdcf6a557e12..c0f8eee53370a782f9a39ff1456692eae87e442f 100644 --- a/pkg/models/iam/am/am.go +++ b/pkg/models/iam/am/am.go @@ -19,116 +19,356 @@ package am import ( "fmt" + rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + k8sinformers "k8s.io/client-go/informers" "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" - kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" - informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/query" + ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/informers" + resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" "net/http" ) type AccessManagementInterface interface { - ListRolesOfUser(scope iamv1alpha2.Scope, username string) ([]iamv1alpha2.Role, error) - GetRoleOfUserInTargetScope(scope iamv1alpha2.Scope, target string, username string) (*iamv1alpha2.Role, error) - GetPolicyRule(name string) (*iamv1alpha2.PolicyRule, error) + GetGlobalRoleOfUser(username string) (*iamv1alpha2.GlobalRole, error) + GetWorkspaceRoleOfUser(username, workspace string) (*iamv1alpha2.WorkspaceRole, error) + GetClusterRoleOfUser(username, cluster string) (*rbacv1.ClusterRole, error) + GetNamespaceRoleOfUser(username, namespace string) (*rbacv1.Role, error) + ListRoles(username string, query *query.Query) (*api.ListResult, error) + ListClusterRoles(query *query.Query) (*api.ListResult, error) + ListWorkspaceRoles(query *query.Query) (*api.ListResult, error) + ListGlobalRoles(query *query.Query) (*api.ListResult, error) + + ListGlobalRoleBindings(username string) ([]*iamv1alpha2.GlobalRoleBinding, error) + ListClusterRoleBindings(username string) ([]*rbacv1.ClusterRoleBinding, error) + ListWorkspaceRoleBindings(username, workspace string) ([]*iamv1alpha2.WorkspaceRoleBinding, error) + ListRoleBindings(username, namespace string) ([]*rbacv1.RoleBinding, error) + + GetRoleReferenceRules(roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error) } type amOperator struct { - informers informers.SharedInformerFactory - ksClient kubesphere.Interface + ksinformer ksinformers.SharedInformerFactory + k8sinformer k8sinformers.SharedInformerFactory + resourceGetter *resourcev1alpha3.ResourceGetter } -func NewAMOperator(ksClient kubesphere.Interface, informers informers.SharedInformerFactory) AccessManagementInterface { +func NewAMOperator(factory informers.InformerFactory) AccessManagementInterface { return &amOperator{ - informers: informers, - ksClient: ksClient, + ksinformer: factory.KubeSphereSharedInformerFactory(), + k8sinformer: factory.KubernetesSharedInformerFactory(), + resourceGetter: resourcev1alpha3.NewResourceGetter(factory), } } -func containsUser(subjets []iamv1alpha2.Subject, username string) bool { - for _, sub := range subjets { - if sub.Kind == iamv1alpha2.UserKind && sub.Name == username { - return true +func (am *amOperator) GetGlobalRoleOfUser(username string) (*iamv1alpha2.GlobalRole, error) { + + roleBindings, err := am.ksinformer.Iam().V1alpha2().GlobalRoleBindings().Lister().List(labels.Everything()) + + if err != nil { + klog.Error(err) + return nil, err + } + + userRoleBindings := make([]*iamv1alpha2.GlobalRoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + userRoleBindings = append(userRoleBindings, roleBinding) } } - return false -} -func (am *amOperator) ListRolesOfUser(scope iamv1alpha2.Scope, username string) ([]iamv1alpha2.Role, error) { + if len(userRoleBindings) > 0 { + role, err := am.ksinformer.Iam().V1alpha2().GlobalRoles().Lister().Get(userRoleBindings[0].RoleRef.Name) + if err != nil { + klog.Error(err) + return nil, err + } + if len(roleBindings) > 1 { + klog.Warningf("conflict global role binding, username: %s", username) + } + return role, nil + } - lister := am.informers.Iam().V1alpha2().RoleBindings().Lister() + err = &errors.StatusError{ErrStatus: metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusNotFound, + Reason: metav1.StatusReasonNotFound, + Details: &metav1.StatusDetails{ + Group: iamv1alpha2.SchemeGroupVersion.Group, + Kind: iamv1alpha2.ResourceKindGlobalRoleBinding, + }, + Message: fmt.Sprintf("global role binding not found for %s", username), + }} + + return nil, err +} - roleBindings, err := lister.List(labels.Everything()) +func (am *amOperator) GetWorkspaceRoleOfUser(username, workspace string) (*iamv1alpha2.WorkspaceRole, error) { + + roleBindings, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoleBindings().Lister().List(labels.SelectorFromValidatedSet(labels.Set{tenantv1alpha1.WorkspaceLabel: workspace})) if err != nil { klog.Error(err) return nil, err } - roleBindingsInScope := filterRoleBindingByScope(roleBindings, scope) + userRoleBindings := make([]*iamv1alpha2.WorkspaceRoleBinding, 0) - roles := make([]iamv1alpha2.Role, 0) + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + userRoleBindings = append(userRoleBindings, roleBinding) + } + } - for _, roleBinding := range roleBindingsInScope { - if containsUser(roleBinding.Subjects, username) { - role, err := am.informers. - Iam().V1alpha2().Roles().Lister().Get(roleBinding.RoleRef.Name) + if len(userRoleBindings) > 0 { + role, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoles().Lister().Get(userRoleBindings[0].RoleRef.Name) - if err != nil { - if errors.IsNotFound(err) { - continue - } - return nil, err - } + if err != nil { + klog.Error(err) + return nil, err + } - roles = append(roles, *role) + if len(roleBindings) > 1 { + klog.Warningf("conflict workspace role binding, username: %s", username) } + + return role, nil } - return roles, nil + err = &errors.StatusError{ErrStatus: metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusNotFound, + Reason: metav1.StatusReasonNotFound, + Details: &metav1.StatusDetails{ + Group: iamv1alpha2.SchemeGroupVersion.Group, + Kind: iamv1alpha2.ResourceKindWorkspaceRoleBinding, + }, + Message: fmt.Sprintf("workspace role binding not found for %s", username), + }} + + return nil, err } -func filterRoleBindingByScope(roles []*iamv1alpha2.RoleBinding, scope iamv1alpha2.Scope) []*iamv1alpha2.RoleBinding { - result := make([]*iamv1alpha2.RoleBinding, 0) - for _, role := range roles { - if role.Scope == scope { - result = append(result, role) +func (am *amOperator) GetNamespaceRoleOfUser(username, namespace string) (*rbacv1.Role, error) { + roleBindings, err := am.k8sinformer.Rbac().V1().RoleBindings().Lister().List(labels.Everything()) + + if err != nil { + klog.Error(err) + return nil, err + } + + userRoleBindings := make([]*rbacv1.RoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + userRoleBindings = append(userRoleBindings, roleBinding) } } - return result -} -func (am *amOperator) GetPolicyRule(name string) (*iamv1alpha2.PolicyRule, error) { - lister := am.informers.Iam().V1alpha2().PolicyRules().Lister() - return lister.Get(name) + if len(userRoleBindings) > 0 { + role, err := am.k8sinformer.Rbac().V1().Roles().Lister().Roles(namespace).Get(userRoleBindings[0].RoleRef.Name) + if err != nil { + klog.Error(err) + return nil, err + } + if len(roleBindings) > 1 { + klog.Warningf("conflict role binding, username: %s", username) + } + return role, nil + } + + err = &errors.StatusError{ErrStatus: metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusNotFound, + Reason: metav1.StatusReasonNotFound, + Details: &metav1.StatusDetails{ + Group: rbacv1.SchemeGroupVersion.Group, + Kind: "RoleBinding", + }, + Message: fmt.Sprintf("role binding not found for %s in %s", username, namespace), + }} + + return nil, err } -// Users can only bind one role at each level -func (am *amOperator) GetRoleOfUserInTargetScope(scope iamv1alpha2.Scope, target, username string) (*iamv1alpha2.Role, error) { - roles, err := am.ListRolesOfUser(scope, username) +// Get federated clusterrole of user if cluster is not empty, if cluster is empty means current cluster +func (am *amOperator) GetClusterRoleOfUser(username, cluster string) (*rbacv1.ClusterRole, error) { + roleBindings, err := am.k8sinformer.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) + if err != nil { klog.Error(err) return nil, err } - for _, role := range roles { - if role.Target.Name == target { - return &role, nil + + userRoleBindings := make([]*rbacv1.ClusterRoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + userRoleBindings = append(userRoleBindings, roleBinding) } } + if len(userRoleBindings) > 0 { + role, err := am.k8sinformer.Rbac().V1().ClusterRoles().Lister().Get(userRoleBindings[0].RoleRef.Name) + if err != nil { + klog.Error(err) + return nil, err + } + if len(roleBindings) > 1 { + klog.Warningf("conflict cluster role binding, username: %s", username) + } + return role, nil + } err = &errors.StatusError{ErrStatus: metav1.Status{ Status: metav1.StatusFailure, Code: http.StatusNotFound, Reason: metav1.StatusReasonNotFound, Details: &metav1.StatusDetails{ - Group: iamv1alpha2.SchemeGroupVersion.Group, - Kind: iamv1alpha2.RoleBindingKind, + Group: rbacv1.SchemeGroupVersion.Group, + Kind: "ClusterRoleBinding", }, - Message: fmt.Sprintf("role bind not found in %s %s scope", target, scope), + Message: fmt.Sprintf("cluster role binding not found for %s in %s", username, cluster), }} - klog.Error(err) return nil, err } + +func (am *amOperator) ListWorkspaceRoleBindings(username, workspace string) ([]*iamv1alpha2.WorkspaceRoleBinding, error) { + roleBindings, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoleBindings().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*iamv1alpha2.WorkspaceRoleBinding, 0) + + for _, roleBinding := range roleBindings { + inSpecifiedWorkspace := workspace == "" || roleBinding.Labels[tenantv1alpha1.WorkspaceLabel] == workspace + if contains(roleBinding.Subjects, username) && inSpecifiedWorkspace { + result = append(result, roleBinding) + } + } + + return result, nil +} + +func (am *amOperator) ListClusterRoleBindings(username string) ([]*rbacv1.ClusterRoleBinding, error) { + + roleBindings, err := am.k8sinformer.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*rbacv1.ClusterRoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + result = append(result, roleBinding) + } + } + + return result, nil +} + +func (am *amOperator) ListGlobalRoleBindings(username string) ([]*iamv1alpha2.GlobalRoleBinding, error) { + roleBindings, err := am.ksinformer.Iam().V1alpha2().GlobalRoleBindings().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*iamv1alpha2.GlobalRoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + result = append(result, roleBinding) + } + } + + return result, nil +} + +func (am *amOperator) ListRoleBindings(username, namespace string) ([]*rbacv1.RoleBinding, error) { + + roleBindings, err := am.k8sinformer.Rbac().V1().RoleBindings().Lister().RoleBindings(namespace).List(labels.Everything()) + + if err != nil { + klog.Error(err) + return nil, err + } + + result := make([]*rbacv1.RoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + result = append(result, roleBinding) + } + } + + return result, nil +} + +func contains(subjects []rbacv1.Subject, username string) bool { + for _, subject := range subjects { + if subject.Kind == rbacv1.UserKind && (username == "" || subject.Name == username) { + return true + } + } + return false +} + +func (am *amOperator) ListRoles(namespace string, query *query.Query) (*api.ListResult, error) { + return am.resourceGetter.List("roles", namespace, query) +} + +func (am *amOperator) ListClusterRoles(query *query.Query) (*api.ListResult, error) { + return am.resourceGetter.List("clusterroles", "", query) +} + +func (am *amOperator) ListWorkspaceRoles(queryParam *query.Query) (*api.ListResult, error) { + return am.resourceGetter.List(iamv1alpha2.ResourcesPluralWorkspaceRole, "", queryParam) +} + +func (am *amOperator) ListGlobalRoles(query *query.Query) (*api.ListResult, error) { + return am.resourceGetter.List(iamv1alpha2.ResourcesPluralGlobalRole, "", query) +} + +// GetRoleReferenceRules attempts to resolve the RoleBinding or ClusterRoleBinding. +func (am *amOperator) GetRoleReferenceRules(roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error) { + switch roleRef.Kind { + case iamv1alpha2.ResourceKindRole: + role, err := am.k8sinformer.Rbac().V1().Roles().Lister().Roles(bindingNamespace).Get(roleRef.Name) + if err != nil { + return nil, err + } + return role.Rules, nil + case iamv1alpha2.ResourceKindClusterRole: + clusterRole, err := am.k8sinformer.Rbac().V1().ClusterRoles().Lister().Get(roleRef.Name) + if err != nil { + return nil, err + } + return clusterRole.Rules, nil + case iamv1alpha2.ResourceKindGlobalRole: + globalRole, err := am.ksinformer.Iam().V1alpha2().GlobalRoles().Lister().Get(roleRef.Name) + if err != nil { + return nil, err + } + return globalRole.Rules, nil + case iamv1alpha2.ResourceKindWorkspaceRole: + workspaceRole, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoles().Lister().Get(roleRef.Name) + if err != nil { + return nil, err + } + return workspaceRole.Rules, nil + default: + return nil, fmt.Errorf("unsupported role reference kind: %q", roleRef.Kind) + } +} diff --git a/pkg/models/iam/am/rbac/evaluation_helpers.go b/pkg/models/iam/am/rbac/evaluation_helpers.go new file mode 100644 index 0000000000000000000000000000000000000000..abe010bda2a664b5dd102c1507c4f6a09a83aa78 --- /dev/null +++ b/pkg/models/iam/am/rbac/evaluation_helpers.go @@ -0,0 +1,179 @@ +/* +Copyright 2018 The Kubernetes 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 authorizerfactory + +import ( + "fmt" + "strings" + + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func RoleRefGroupKind(roleRef rbacv1.RoleRef) schema.GroupKind { + return schema.GroupKind{Group: roleRef.APIGroup, Kind: roleRef.Kind} +} + +func VerbMatches(rule *rbacv1.PolicyRule, requestedVerb string) bool { + for _, ruleVerb := range rule.Verbs { + if ruleVerb == rbacv1.VerbAll { + return true + } + if ruleVerb == requestedVerb { + return true + } + } + + return false +} + +func APIGroupMatches(rule *rbacv1.PolicyRule, requestedGroup string) bool { + for _, ruleGroup := range rule.APIGroups { + if ruleGroup == rbacv1.APIGroupAll { + return true + } + if ruleGroup == requestedGroup { + return true + } + } + + return false +} + +func ResourceMatches(rule *rbacv1.PolicyRule, combinedRequestedResource, requestedSubresource string) bool { + for _, ruleResource := range rule.Resources { + // if everything is allowed, we match + if ruleResource == rbacv1.ResourceAll { + return true + } + // if we have an exact match, we match + if ruleResource == combinedRequestedResource { + return true + } + + // We can also match a */subresource. + // if there isn't a subresource, then continue + if len(requestedSubresource) == 0 { + continue + } + // if the rule isn't in the format */subresource, then we don't match, continue + if len(ruleResource) == len(requestedSubresource)+2 && + strings.HasPrefix(ruleResource, "*/") && + strings.HasSuffix(ruleResource, requestedSubresource) { + return true + + } + } + + return false +} + +func ResourceNameMatches(rule *rbacv1.PolicyRule, requestedName string) bool { + if len(rule.ResourceNames) == 0 { + return true + } + + for _, ruleName := range rule.ResourceNames { + if ruleName == requestedName { + return true + } + } + + return false +} + +func NonResourceURLMatches(rule *rbacv1.PolicyRule, requestedURL string) bool { + for _, ruleURL := range rule.NonResourceURLs { + if ruleURL == rbacv1.NonResourceAll { + return true + } + if ruleURL == requestedURL { + return true + } + if strings.HasSuffix(ruleURL, "*") && strings.HasPrefix(requestedURL, strings.TrimRight(ruleURL, "*")) { + return true + } + } + + return false +} + +// subjectsStrings returns users, groups, serviceaccounts, unknown for display purposes. +func SubjectsStrings(subjects []rbacv1.Subject) ([]string, []string, []string, []string) { + users := []string{} + groups := []string{} + sas := []string{} + others := []string{} + + for _, subject := range subjects { + switch subject.Kind { + case rbacv1.ServiceAccountKind: + sas = append(sas, fmt.Sprintf("%s/%s", subject.Namespace, subject.Name)) + + case rbacv1.UserKind: + users = append(users, subject.Name) + + case rbacv1.GroupKind: + groups = append(groups, subject.Name) + + default: + others = append(others, fmt.Sprintf("%s/%s/%s", subject.Kind, subject.Namespace, subject.Name)) + } + } + + return users, groups, sas, others +} + +func String(r rbacv1.PolicyRule) string { + return "PolicyRule" + CompactString(r) +} + +// CompactString exposes a compact string representation for use in escalation error messages +func CompactString(r rbacv1.PolicyRule) string { + formatStringParts := []string{} + formatArgs := []interface{}{} + if len(r.APIGroups) > 0 { + formatStringParts = append(formatStringParts, "APIGroups:%q") + formatArgs = append(formatArgs, r.APIGroups) + } + if len(r.Resources) > 0 { + formatStringParts = append(formatStringParts, "Resources:%q") + formatArgs = append(formatArgs, r.Resources) + } + if len(r.NonResourceURLs) > 0 { + formatStringParts = append(formatStringParts, "NonResourceURLs:%q") + formatArgs = append(formatArgs, r.NonResourceURLs) + } + if len(r.ResourceNames) > 0 { + formatStringParts = append(formatStringParts, "ResourceNames:%q") + formatArgs = append(formatArgs, r.ResourceNames) + } + if len(r.Verbs) > 0 { + formatStringParts = append(formatStringParts, "Verbs:%q") + formatArgs = append(formatArgs, r.Verbs) + } + formatString := "{" + strings.Join(formatStringParts, ", ") + "}" + return fmt.Sprintf(formatString, formatArgs...) +} + +type SortableRuleSlice []rbacv1.PolicyRule + +func (s SortableRuleSlice) Len() int { return len(s) } +func (s SortableRuleSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s SortableRuleSlice) Less(i, j int) bool { + return strings.Compare(s[i].String(), s[j].String()) < 0 +} diff --git a/pkg/models/iam/im/im.go b/pkg/models/iam/im/im.go index 66f3b6228ea2cc4059e3aaaebd3b0e08481ef785..c1002c2610bcc995f93a86452b55e24f8958a3a1 100644 --- a/pkg/models/iam/im/im.go +++ b/pkg/models/iam/im/im.go @@ -19,11 +19,20 @@ package im import ( "github.com/pkg/errors" + "golang.org/x/crypto/bcrypt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/informers" + resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" ) type IdentityManagementInterface interface { CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) + ListUsers(query *query.Query) (*api.ListResult, error) DeleteUser(username string) error ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) DescribeUser(username string) (*iamv1alpha2.User, error) @@ -36,3 +45,75 @@ var ( UserAlreadyExists = errors.New("user already exists") UserNotExists = errors.New("user not exists") ) + +func NewOperator(ksClient kubesphereclient.Interface, factory informers.InformerFactory) IdentityManagementInterface { + + return &defaultIMOperator{ + ksClient: ksClient, + resourceGetter: resourcev1alpha3.NewResourceGetter(factory), + } + +} + +type defaultIMOperator struct { + ksClient kubesphereclient.Interface + resourceGetter *resourcev1alpha3.ResourceGetter +} + +func (im *defaultIMOperator) ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { + return im.ksClient.IamV1alpha2().Users().Update(user) +} + +func (im *defaultIMOperator) Authenticate(username, password string) (*iamv1alpha2.User, error) { + + user, err := im.DescribeUser(username) + + if err != nil { + klog.Error(err) + return nil, err + } + if checkPasswordHash(password, user.Spec.EncryptedPassword) { + return user, nil + } + return nil, AuthFailedIncorrectPassword +} + +func (im *defaultIMOperator) ListUsers(query *query.Query) (*api.ListResult, error) { + + result, err := im.resourceGetter.List(iamv1alpha2.ResourcesPluralUser, "", query) + + if err != nil { + klog.Error(err) + return nil, err + } + + return result, nil +} + +func checkPasswordHash(password, hash string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + return err == nil +} + +func (im *defaultIMOperator) DescribeUser(username string) (*iamv1alpha2.User, error) { + user, err := im.resourceGetter.Get(iamv1alpha2.ResourcesPluralUser, "", username) + + if err != nil { + klog.Error(err) + return nil, err + } + return user.(*iamv1alpha2.User), nil +} + +func (im *defaultIMOperator) DeleteUser(username string) error { + return im.ksClient.IamV1alpha2().Users().Delete(username, metav1.NewDeleteOptions(0)) +} + +func (im *defaultIMOperator) CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { + user, err := im.ksClient.IamV1alpha2().Users().Create(user) + if err != nil { + klog.Error(err) + return nil, err + } + return user, nil +} diff --git a/pkg/models/iam/im/im_operator.go b/pkg/models/iam/im/im_operator.go deleted file mode 100644 index 8bf7f28ec8139691c46597717268a319c1e47ecb..0000000000000000000000000000000000000000 --- a/pkg/models/iam/im/im_operator.go +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * Copyright 2020 The KubeSphere 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 im - -import ( - "golang.org/x/crypto/bcrypt" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" - kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" - informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" -) - -func NewOperator(ksClient kubesphereclient.Interface, informer informers.SharedInformerFactory) IdentityManagementInterface { - - return &defaultIMOperator{ - ksClient: ksClient, - informer: informer, - } - -} - -type defaultIMOperator struct { - ksClient kubesphereclient.Interface - informer informers.SharedInformerFactory -} - -func (im *defaultIMOperator) ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { - return im.ksClient.IamV1alpha2().Users().Update(user) -} - -func (im *defaultIMOperator) Authenticate(username, password string) (*iamv1alpha2.User, error) { - - user, err := im.ksClient.IamV1alpha2().Users().Get(username, metav1.GetOptions{}) - - if err != nil { - return nil, err - } - if checkPasswordHash(password, user.Spec.EncryptedPassword) { - return user, nil - } - return nil, AuthFailedIncorrectPassword -} - -func checkPasswordHash(password, hash string) bool { - err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) - return err == nil -} - -func (im *defaultIMOperator) DescribeUser(username string) (*iamv1alpha2.User, error) { - user, err := im.ksClient.IamV1alpha2().Users().Get(username, metav1.GetOptions{}) - - if err != nil { - return nil, err - } - return user, nil -} - -func (im *defaultIMOperator) DeleteUser(username string) error { - return im.ksClient.IamV1alpha2().Users().Delete(username, metav1.NewDeleteOptions(0)) -} - -func (im *defaultIMOperator) CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { - user, err := im.ksClient.IamV1alpha2().Users().Create(user) - if err != nil { - return nil, err - } - - return user, nil -} diff --git a/pkg/models/iam/im/ldap_operator.go b/pkg/models/iam/im/ldap_operator.go index 10e40250ea7a8d1922064ee929432c809b45c1d6..b595563158141bf8ac761a67e58b7f27e062f474 100644 --- a/pkg/models/iam/im/ldap_operator.go +++ b/pkg/models/iam/im/ldap_operator.go @@ -19,7 +19,10 @@ package im import ( + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/simple/client/ldap" ) @@ -31,7 +34,6 @@ func NewLDAPOperator(ldapClient ldap.Interface) IdentityManagementInterface { return &ldapOperator{ ldapClient: ldapClient, } - } func (im *ldapOperator) ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { @@ -78,3 +80,14 @@ func (im *ldapOperator) CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, e return user, nil } + +func (im *ldapOperator) ListUsers(query *query.Query) (*api.ListResult, error) { + result, err := im.ldapClient.List(query) + + if err != nil { + klog.Error(err) + return nil, err + } + + return result, nil +} diff --git a/pkg/models/resources/v1alpha2/interface.go b/pkg/models/resources/v1alpha2/interface.go index 11f556e5d88dce448e55694407978249c829b887..803cefa0e32ed617086750b2cc15af268912e304 100644 --- a/pkg/models/resources/v1alpha2/interface.go +++ b/pkg/models/resources/v1alpha2/interface.go @@ -81,6 +81,13 @@ func ObjectMetaExactlyMath(key, value string, item metav1.ObjectMeta) bool { if !strings.Contains(item.Name, value) && !FuzzyMatch(item.Labels, "", value) && !FuzzyMatch(item.Annotations, "", value) { return false } + case Owner: + for _, ownerReference := range item.OwnerReferences { + if strings.Compare(string(ownerReference.UID), value) == 0 { + return true + } + } + return false default: // label not exist or value not equal if val, ok := item.Labels[key]; !ok || val != value { @@ -109,13 +116,6 @@ func ObjectMetaFuzzyMath(key, value string, item metav1.ObjectMeta) bool { if !strings.Contains(item.Labels[Chart], value) && !strings.Contains(item.Labels[Release], value) { return false } - case Owner: - for _, ownerReference := range item.OwnerReferences { - if strings.Compare(string(ownerReference.UID), value) == 0 { - return true - } - } - return false default: if !FuzzyMatch(item.Labels, key, value) { return false diff --git a/pkg/models/resources/v1alpha2/resource/resources.go b/pkg/models/resources/v1alpha2/resource/resources.go index ffdae390054f25c64274af70ef47d4a414cb65e1..ae0a709fd65a320775a1eb2eb302d364baa649e6 100644 --- a/pkg/models/resources/v1alpha2/resource/resources.go +++ b/pkg/models/resources/v1alpha2/resource/resources.go @@ -115,7 +115,7 @@ func (r *ResourceGetter) ListResources(namespace, resource string, conditions *p // none namespace resource if namespace != "" && sliceutil.HasString(clusterResources, resource) { - err = fmt.Errorf("namespaced resource %s not found", resource) + err = fmt.Errorf("resource %s is not supported", resource) klog.Errorln(err) return nil, err } @@ -123,7 +123,7 @@ func (r *ResourceGetter) ListResources(namespace, resource string, conditions *p if searcher, ok := r.resourcesGetters[resource]; ok { result, err = searcher.Search(namespace, conditions, orderBy, reverse) } else { - err = fmt.Errorf("namespaced resource %s not found", resource) + err = fmt.Errorf("resource %s is not supported", resource) klog.Errorln(err) return nil, err } diff --git a/pkg/models/resources/v1alpha3/application/applications.go b/pkg/models/resources/v1alpha3/application/applications.go index 433fdd9d5e1b1cf2b767e925a6c9f3f4cf0a2aac..aabc517b80d9f73d5169598493734e704700c5b9 100644 --- a/pkg/models/resources/v1alpha3/application/applications.go +++ b/pkg/models/resources/v1alpha3/application/applications.go @@ -20,11 +20,11 @@ package application import ( appv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1" "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "time" ) type applicationsGetter struct { @@ -40,7 +40,7 @@ func (d *applicationsGetter) Get(namespace, name string) (runtime.Object, error) } func (d *applicationsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { - all, err := d.informer.App().V1beta1().Applications().Lister().Applications(namespace).List(labels.Everything()) + all, err := d.informer.App().V1beta1().Applications().Lister().Applications(namespace).List(query.Selector()) if err != nil { return nil, err } @@ -64,8 +64,14 @@ func (d *applicationsGetter) compare(left runtime.Object, right runtime.Object, if !ok { return false } - - return v1alpha3.DefaultObjectMetaCompare(leftApplication.ObjectMeta, rightApplication.ObjectMeta, field) + switch field { + case query.FieldUpdateTime: + fallthrough + case query.FieldLastUpdateTimestamp: + return lastUpdateTime(leftApplication).After(lastUpdateTime(rightApplication)) + default: + return v1alpha3.DefaultObjectMetaCompare(leftApplication.ObjectMeta, rightApplication.ObjectMeta, field) + } } func (d *applicationsGetter) filter(object runtime.Object, filter query.Filter) bool { @@ -76,3 +82,13 @@ func (d *applicationsGetter) filter(object runtime.Object, filter query.Filter) return v1alpha3.DefaultObjectMetaFilter(application.ObjectMeta, filter) } + +func lastUpdateTime(application *appv1beta1.Application) time.Time { + lut := application.CreationTimestamp.Time + for _, condition := range application.Status.Conditions { + if condition.LastUpdateTime.After(lut) { + lut = condition.LastUpdateTime.Time + } + } + return lut +} diff --git a/pkg/models/resources/v1alpha3/application/applications_test.go b/pkg/models/resources/v1alpha3/application/applications_test.go index acb6c468ea23e4abefaaa4517972a14e7839e703..41c988e1e287ed4da36db1a7fb63f53198507e84 100644 --- a/pkg/models/resources/v1alpha3/application/applications_test.go +++ b/pkg/models/resources/v1alpha3/application/applications_test.go @@ -59,12 +59,7 @@ func TestListApplications(t *testing.T) { }, SortBy: query.FieldName, Ascending: false, - Filters: []query.Filter{ - { - Field: query.FieldNamespace, - Value: query.Value("bar2"), - }, - }, + Filters: map[query.Field]query.Value{query.FieldNamespace: query.Value("bar2")}, }, api.ListResult{ Items: []interface{}{ diff --git a/pkg/models/resources/v1alpha3/clusterrole/clusterroles.go b/pkg/models/resources/v1alpha3/clusterrole/clusterroles.go new file mode 100644 index 0000000000000000000000000000000000000000..239243120b0dfa88704cf7d7bd5ac347b2cf3ec4 --- /dev/null +++ b/pkg/models/resources/v1alpha3/clusterrole/clusterroles.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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 clusterrole + +import ( + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type rolesGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &rolesGetter{sharedInformers: sharedInformers} +} + +func (d *rolesGetter) Get(namespace, name string) (runtime.Object, error) { + return d.sharedInformers.Rbac().V1().ClusterRoles().Lister().Get(name) +} + +func (d *rolesGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Rbac().V1().ClusterRoles().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *rolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*rbacv1.ClusterRole) + if !ok { + return false + } + + rightRole, ok := right.(*rbacv1.ClusterRole) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *rolesGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*rbacv1.ClusterRole) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/clusterrole/clusterroles_test.go b/pkg/models/resources/v1alpha3/clusterrole/clusterroles_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7e8f60c2433627af0d23aff39b8e5022d294171b --- /dev/null +++ b/pkg/models/resources/v1alpha3/clusterrole/clusterroles_test.go @@ -0,0 +1,94 @@ +package clusterrole + +import ( + "github.com/google/go-cmp/cmp" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListClusterRoles(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + roles = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, role := range roles { + informer.Rbac().V1().ClusterRoles().Informer().GetIndexer().Add(role) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/configmap/configmaps.go b/pkg/models/resources/v1alpha3/configmap/configmaps.go index 9b5ee74faa7dd29caccedb9ab184eac323338df5..76715aa2bfad6bc36616149222fe52dafc307e06 100644 --- a/pkg/models/resources/v1alpha3/configmap/configmaps.go +++ b/pkg/models/resources/v1alpha3/configmap/configmaps.go @@ -19,7 +19,6 @@ package configmap import ( corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/informers" "kubesphere.io/kubesphere/pkg/api" @@ -40,7 +39,7 @@ func (d *configmapsGetter) Get(namespace, name string) (runtime.Object, error) { } func (d *configmapsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { - all, err := d.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).List(labels.Everything()) + all, err := d.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).List(query.Selector()) if err != nil { return nil, err } diff --git a/pkg/models/resources/v1alpha3/configmap/configmaps_test.go b/pkg/models/resources/v1alpha3/configmap/configmaps_test.go index f0c27ac6e6d777278358384fcab932105386bd6f..bc99a632cb95ad71fc3ec3c950e9be8aae19b24e 100644 --- a/pkg/models/resources/v1alpha3/configmap/configmaps_test.go +++ b/pkg/models/resources/v1alpha3/configmap/configmaps_test.go @@ -30,12 +30,7 @@ func TestListConfigMaps(t *testing.T) { }, SortBy: query.FieldName, Ascending: false, - Filters: []query.Filter{ - { - Field: query.FieldNamespace, - Value: query.Value("default"), - }, - }, + Filters: map[query.Field]query.Value{query.FieldNamespace: query.Value("default")}, }, &api.ListResult{ Items: []interface{}{foo3, foo2, foo1}, diff --git a/pkg/models/resources/v1alpha3/deployment/deployments.go b/pkg/models/resources/v1alpha3/deployment/deployments.go index e23bb4c4a20313d0ada351c627a888a8d2c22fda..b3083beb8ad38a260fe327e3100f9cdcf26fa08d 100644 --- a/pkg/models/resources/v1alpha3/deployment/deployments.go +++ b/pkg/models/resources/v1alpha3/deployment/deployments.go @@ -26,8 +26,6 @@ import ( "strings" "time" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/api/apps/v1" ) @@ -51,7 +49,7 @@ func (d *deploymentsGetter) Get(namespace, name string) (runtime.Object, error) func (d *deploymentsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { // first retrieves all deployments within given namespace - all, err := d.sharedInformers.Apps().V1().Deployments().Lister().Deployments(namespace).List(labels.Everything()) + all, err := d.sharedInformers.Apps().V1().Deployments().Lister().Deployments(namespace).List(query.Selector()) if err != nil { return nil, err } @@ -77,6 +75,8 @@ func (d *deploymentsGetter) compare(left runtime.Object, right runtime.Object, f } switch field { + case query.FieldUpdateTime: + fallthrough case query.FieldLastUpdateTimestamp: return lastUpdateTime(leftDeployment).After(lastUpdateTime(rightDeployment)) default: diff --git a/pkg/models/resources/v1alpha3/deployment/deployments_test.go b/pkg/models/resources/v1alpha3/deployment/deployments_test.go index 043c64e676b0de164329cca6feb90b45c76bb12d..caad84fadcd00fe41d754a1324763cd51d407648 100644 --- a/pkg/models/resources/v1alpha3/deployment/deployments_test.go +++ b/pkg/models/resources/v1alpha3/deployment/deployments_test.go @@ -30,12 +30,7 @@ func TestListDeployments(t *testing.T) { }, SortBy: query.FieldName, Ascending: false, - Filters: []query.Filter{ - { - Field: query.FieldName, - Value: query.Value("foo2"), - }, - }, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, }, &api.ListResult{ Items: []interface{}{ diff --git a/pkg/models/resources/v1alpha3/globalrole/globalroles.go b/pkg/models/resources/v1alpha3/globalrole/globalroles.go new file mode 100644 index 0000000000000000000000000000000000000000..750a04767c04d8e76fa3050485c66ef98fa7bf98 --- /dev/null +++ b/pkg/models/resources/v1alpha3/globalrole/globalroles.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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 globalrole + +import ( + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type globalrolesGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &globalrolesGetter{sharedInformers: sharedInformers} +} + +func (d *globalrolesGetter) Get(_, name string) (runtime.Object, error) { + return d.sharedInformers.Iam().V1alpha2().GlobalRoles().Lister().Get(name) +} + +func (d *globalrolesGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Iam().V1alpha2().GlobalRoles().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *globalrolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*iamv1alpha2.GlobalRole) + if !ok { + return false + } + + rightRole, ok := right.(*iamv1alpha2.GlobalRole) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *globalrolesGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*iamv1alpha2.GlobalRole) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/globalrole/globalroles_test.go b/pkg/models/resources/v1alpha3/globalrole/globalroles_test.go new file mode 100644 index 0000000000000000000000000000000000000000..85e32ce29039e99536201a0954c361321689c086 --- /dev/null +++ b/pkg/models/resources/v1alpha3/globalrole/globalroles_test.go @@ -0,0 +1,94 @@ +package globalrole + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListGlobalRoles(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &iamv1alpha2.GlobalRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &iamv1alpha2.GlobalRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &iamv1alpha2.GlobalRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + roles = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, role := range roles { + informer.Iam().V1alpha2().GlobalRoles().Informer().GetIndexer().Add(role) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/interface.go b/pkg/models/resources/v1alpha3/interface.go index 120fe0eb190a9790d17b1e60ef4b46583da24419..6e3040b143c7577a6e02ed43161d31e4b42f58d3 100644 --- a/pkg/models/resources/v1alpha3/interface.go +++ b/pkg/models/resources/v1alpha3/interface.go @@ -22,13 +22,13 @@ type CompareFunc func(runtime.Object, runtime.Object, query.Field) bool type FilterFunc func(runtime.Object, query.Filter) bool -func DefaultList(objects []runtime.Object, query *query.Query, compareFunc CompareFunc, filterFunc FilterFunc) *api.ListResult { +func DefaultList(objects []runtime.Object, q *query.Query, compareFunc CompareFunc, filterFunc FilterFunc) *api.ListResult { // selected matched ones var filtered []runtime.Object for _, object := range objects { selected := true - for _, filter := range query.Filters { - if !filterFunc(object, filter) { + for field, value := range q.Filters { + if !filterFunc(object, query.Filter{Field: field, Value: value}) { selected = false break } @@ -41,14 +41,14 @@ func DefaultList(objects []runtime.Object, query *query.Query, compareFunc Compa // sort by sortBy field sort.Slice(filtered, func(i, j int) bool { - if !query.Ascending { - return compareFunc(filtered[i], filtered[j], query.SortBy) + if !q.Ascending { + return compareFunc(filtered[i], filtered[j], q.SortBy) } - return !compareFunc(filtered[i], filtered[j], query.SortBy) + return !compareFunc(filtered[i], filtered[j], q.SortBy) }) total := len(filtered) - start, end := query.Pagination.GetValidPagination(total) + start, end := q.Pagination.GetValidPagination(total) return &api.ListResult{ TotalItems: len(filtered), @@ -63,6 +63,8 @@ func DefaultObjectMetaCompare(left, right metav1.ObjectMeta, sortBy query.Field) case query.FieldName: return strings.Compare(left.Name, right.Name) > 0 // ?sortBy=creationTimestamp + case query.FieldCreateTime: + fallthrough case query.FieldCreationTimeStamp: // compare by name if creation timestamp is equal if left.CreationTimestamp.Equal(&right.CreationTimestamp) { @@ -104,30 +106,44 @@ func DefaultObjectMetaFilter(item metav1.ObjectMeta, filter query.Filter) bool { return false // /namespaces?page=1&limit=10&annotation=openpitrix_runtime case query.FieldAnnotation: - return containsAnyValue(item.Annotations, string(filter.Value)) + return labelMatch(item.Annotations, string(filter.Value)) // /namespaces?page=1&limit=10&label=kubesphere.io/workspace:system-workspace case query.FieldLabel: - return containsAnyValue(item.Labels, string(filter.Value)) + return labelMatch(item.Labels, string(filter.Value)) default: return false } } -// Filter format ,if the key is defined, the key must match exactly, value match according to strings.Contains. -func containsAnyValue(keyValues map[string]string, filter string) bool { - fields := strings.SplitN(filter, ":", 2) - var keyFilter, valueFilter string +// Filter format (key!?=)?value,if the key is defined, the key must match exactly, value match according to strings.Contains. +func labelMatch(labels map[string]string, filter string) bool { + fields := strings.SplitN(filter, "=", 2) + var key, value string + var opposite bool if len(fields) == 2 { - keyFilter = fields[0] - valueFilter = fields[1] + key = fields[0] + if strings.HasSuffix(key, "!") { + key = strings.TrimSuffix(key, "!") + opposite = true + } + value = fields[1] } else { - valueFilter = fields[0] + value = fields[0] } - for key, value := range keyValues { - if (key == keyFilter || keyFilter == "") && strings.Contains(value, valueFilter) { - return true + for k, v := range labels { + if opposite { + if (key == "" || k == key) && !strings.Contains(v, value) { + return true + } + } else { + if (key == "" || k == key) && strings.Contains(v, value) { + return true + } } } + if opposite && labels[key] == "" { + return true + } return false } diff --git a/pkg/models/resources/v1alpha3/namespace/namespaces.go b/pkg/models/resources/v1alpha3/namespace/namespaces.go index e70b11b10a135c3d760d008383a71604b437f48d..8febda6ff6a48d2ddfc3bb1019bbd8231a494841 100644 --- a/pkg/models/resources/v1alpha3/namespace/namespaces.go +++ b/pkg/models/resources/v1alpha3/namespace/namespaces.go @@ -2,7 +2,6 @@ package namespace import ( v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/informers" "kubesphere.io/kubesphere/pkg/api" @@ -24,7 +23,7 @@ func (n namespaceGetter) Get(_, name string) (runtime.Object, error) { } func (n namespaceGetter) List(_ string, query *query.Query) (*api.ListResult, error) { - ns, err := n.informers.Core().V1().Namespaces().Lister().List(labels.Everything()) + ns, err := n.informers.Core().V1().Namespaces().Lister().List(query.Selector()) if err != nil { return nil, err } diff --git a/pkg/models/resources/v1alpha3/pod/pods.go b/pkg/models/resources/v1alpha3/pod/pods.go new file mode 100644 index 0000000000000000000000000000000000000000..bdfa845a391ddb51ddfcb6a459235c34ece59fd8 --- /dev/null +++ b/pkg/models/resources/v1alpha3/pod/pods.go @@ -0,0 +1,115 @@ +/* + + Copyright 2019 The KubeSphere 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 pod + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type podsGetter struct { + informer informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &podsGetter{informer: sharedInformers} +} + +func (d *podsGetter) Get(namespace, name string) (runtime.Object, error) { + return d.informer.Core().V1().Pods().Lister().Pods(namespace).Get(name) +} + +func (d *podsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + + all, err := d.informer.Core().V1().Pods().Lister().Pods(namespace).List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, app := range all { + result = append(result, app) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *podsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftPod, ok := left.(*corev1.Pod) + if !ok { + return false + } + + rightPod, ok := right.(*corev1.Pod) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftPod.ObjectMeta, rightPod.ObjectMeta, field) +} + +func (d *podsGetter) filter(object runtime.Object, filter query.Filter) bool { + pod, ok := object.(*corev1.Pod) + + if !ok { + return false + } + switch filter.Field { + case "nodeName": + if pod.Spec.NodeName != string(filter.Value) { + return false + } + case "pvcName": + if !d.podBindPVC(pod, string(filter.Value)) { + return false + } + case "serviceName": + if !d.podBelongToService(pod, string(filter.Value)) { + return false + } + } + return v1alpha3.DefaultObjectMetaFilter(pod.ObjectMeta, filter) +} + +func (s *podsGetter) podBindPVC(item *corev1.Pod, pvcName string) bool { + for _, v := range item.Spec.Volumes { + if v.VolumeSource.PersistentVolumeClaim != nil && + v.VolumeSource.PersistentVolumeClaim.ClaimName == pvcName { + return true + } + } + return false +} + +func (s *podsGetter) podBelongToService(item *corev1.Pod, serviceName string) bool { + service, err := s.informer.Core().V1().Services().Lister().Services(item.Namespace).Get(serviceName) + if err != nil { + return false + } + selector := labels.Set(service.Spec.Selector).AsSelectorPreValidated() + if selector.Empty() || !selector.Matches(labels.Set(item.Labels)) { + return false + } + return true +} diff --git a/pkg/models/resources/v1alpha3/pod/pods_test.go b/pkg/models/resources/v1alpha3/pod/pods_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d5fb596520943d7aeb1da80c16dcb1d77433ab60 --- /dev/null +++ b/pkg/models/resources/v1alpha3/pod/pods_test.go @@ -0,0 +1,91 @@ +package pod + +import ( + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListPods(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "default", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldNamespace: query.Value("default")}, + }, + &api.ListResult{ + Items: []interface{}{foo3, foo2, foo1}, + TotalItems: len(pods), + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + got, err := getter.List(test.namespace, test.query) + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + } +} + +var ( + foo1 = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "default", + }, + } + foo2 = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "default", + }, + } + foo3 = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo3", + Namespace: "default", + }, + } + pods = []interface{}{foo1, foo2, foo3} +) + +func prepare() v1alpha3.Interface { + + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, pod := range pods { + informer.Core().V1().Pods().Informer().GetIndexer().Add(pod) + } + + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/resource/resource.go b/pkg/models/resources/v1alpha3/resource/resource.go index 057a1228109b42ecbec6a4d02acbcd490a0c6bcb..6a9b81af66895876a8ef378676a82688776a55ce 100644 --- a/pkg/models/resources/v1alpha3/resource/resource.go +++ b/pkg/models/resources/v1alpha3/resource/resource.go @@ -1,15 +1,45 @@ +/* + * + * Copyright 2020 The KubeSphere 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 resource import ( "errors" + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/application" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/clusterrole" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/configmap" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/deployment" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/globalrole" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/namespace" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/pod" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/role" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/user" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/workspace" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/workspacerole" ) var ErrResourceNotSupported = errors.New("resource is not supported") @@ -23,7 +53,15 @@ func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter { getters[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}] = deployment.New(factory.KubernetesSharedInformerFactory()) getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}] = namespace.New(factory.KubernetesSharedInformerFactory()) + getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}] = configmap.New(factory.KubernetesSharedInformerFactory()) + getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}] = pod.New(factory.KubernetesSharedInformerFactory()) getters[schema.GroupVersionResource{Group: "app.k8s.io", Version: "v1beta1", Resource: "applications"}] = application.New(factory.ApplicationSharedInformerFactory()) + getters[tenantv1alpha1.SchemeGroupVersion.WithResource(tenantv1alpha1.ResourcePluralWorkspace)] = workspace.New(factory.KubeSphereSharedInformerFactory()) + getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralGlobalRole)] = globalrole.New(factory.KubeSphereSharedInformerFactory()) + getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralWorkspaceRole)] = workspacerole.New(factory.KubeSphereSharedInformerFactory()) + getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralUser)] = user.New(factory.KubeSphereSharedInformerFactory()) + getters[rbacv1.SchemeGroupVersion.WithResource("roles")] = role.New(factory.KubernetesSharedInformerFactory()) + getters[rbacv1.SchemeGroupVersion.WithResource("clusterroles")] = clusterrole.New(factory.KubernetesSharedInformerFactory()) return &ResourceGetter{ getters: getters, @@ -38,11 +76,10 @@ func (r *ResourceGetter) tryResource(resource string) v1alpha3.Interface { return v } } - return nil } -func (r *ResourceGetter) Get(resource, namespace, name string) (interface{}, error) { +func (r *ResourceGetter) Get(resource, namespace, name string) (runtime.Object, error) { getter := r.tryResource(resource) if getter == nil { return nil, ErrResourceNotSupported diff --git a/pkg/models/resources/v1alpha3/resource/resource_test.go b/pkg/models/resources/v1alpha3/resource/resource_test.go index 4ea035c141200f2dbf422ac533c9e7a8e78c46e7..199690958de232bdb9cf5568aa9ff4ba01cecc67 100644 --- a/pkg/models/resources/v1alpha3/resource/resource_test.go +++ b/pkg/models/resources/v1alpha3/resource/resource_test.go @@ -55,7 +55,7 @@ func TestResourceGetter(t *testing.T) { }, SortBy: query.FieldName, Ascending: false, - Filters: []query.Filter{}, + Filters: map[query.Field]query.Value{}, }, ExpectError: nil, ExpectResponse: &api.ListResult{ diff --git a/pkg/models/resources/v1alpha3/role/roles.go b/pkg/models/resources/v1alpha3/role/roles.go new file mode 100644 index 0000000000000000000000000000000000000000..4ebba9faafcd20f2f360e5bd85189cbcc7d41d5f --- /dev/null +++ b/pkg/models/resources/v1alpha3/role/roles.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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 role + +import ( + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type rolesGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &rolesGetter{sharedInformers: sharedInformers} +} + +func (d *rolesGetter) Get(namespace, name string) (runtime.Object, error) { + return d.sharedInformers.Rbac().V1().Roles().Lister().Roles(namespace).Get(name) +} + +func (d *rolesGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + all, err := d.sharedInformers.Rbac().V1().Roles().Lister().Roles(namespace).List(query.Selector()) + + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *rolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*rbacv1.Role) + if !ok { + return false + } + + rightRole, ok := right.(*rbacv1.Role) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *rolesGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*rbacv1.Role) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/role/roles_test.go b/pkg/models/resources/v1alpha3/role/roles_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7097831f9034308f2ce097780c6a965ea9600cab --- /dev/null +++ b/pkg/models/resources/v1alpha3/role/roles_test.go @@ -0,0 +1,97 @@ +package role + +import ( + "github.com/google/go-cmp/cmp" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListRoles(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "bar", + }, + } + + foo2 = &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "bar", + }, + } + bar1 = &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + Namespace: "bar", + }, + } + + roles = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, role := range roles { + informer.Rbac().V1().Roles().Informer().GetIndexer().Add(role) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/user/users.go b/pkg/models/resources/v1alpha3/user/users.go new file mode 100644 index 0000000000000000000000000000000000000000..3c74e9d4596d40679ada8ebdfe1c70cfea01d353 --- /dev/null +++ b/pkg/models/resources/v1alpha3/user/users.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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 user + +import ( + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type usersGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &usersGetter{sharedInformers: sharedInformers} +} + +func (d *usersGetter) Get(_, name string) (runtime.Object, error) { + return d.sharedInformers.Iam().V1alpha2().Users().Lister().Get(name) +} + +func (d *usersGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Iam().V1alpha2().Users().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *usersGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*iamv1alpha2.User) + if !ok { + return false + } + + rightRole, ok := right.(*iamv1alpha2.User) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *usersGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*iamv1alpha2.User) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/user/users_test.go b/pkg/models/resources/v1alpha3/user/users_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6ce6e080576addec57571833204f7c217d5cf61a --- /dev/null +++ b/pkg/models/resources/v1alpha3/user/users_test.go @@ -0,0 +1,94 @@ +package user + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListUsers(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &iamv1alpha2.User{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &iamv1alpha2.User{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &iamv1alpha2.User{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + users = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, user := range users { + informer.Iam().V1alpha2().Users().Informer().GetIndexer().Add(user) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/workspace/workspaces.go b/pkg/models/resources/v1alpha3/workspace/workspaces.go new file mode 100644 index 0000000000000000000000000000000000000000..539fcd8174c8d47ced2f9296745087b53684c7a7 --- /dev/null +++ b/pkg/models/resources/v1alpha3/workspace/workspaces.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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 workspace + +import ( + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/query" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type workspaceGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &workspaceGetter{sharedInformers: sharedInformers} +} + +func (d *workspaceGetter) Get(_, name string) (runtime.Object, error) { + return d.sharedInformers.Tenant().V1alpha1().Workspaces().Lister().Get(name) +} + +func (d *workspaceGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Tenant().V1alpha1().Workspaces().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *workspaceGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*tenantv1alpha1.Workspace) + if !ok { + return false + } + + rightRole, ok := right.(*tenantv1alpha1.Workspace) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *workspaceGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*tenantv1alpha1.Workspace) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/workspace/workspaces_test.go b/pkg/models/resources/v1alpha3/workspace/workspaces_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3c748399dab4db90dd43386576d2ae8c229a966b --- /dev/null +++ b/pkg/models/resources/v1alpha3/workspace/workspaces_test.go @@ -0,0 +1,94 @@ +package workspace + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListWorkspaces(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &tenantv1alpha1.Workspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &tenantv1alpha1.Workspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &tenantv1alpha1.Workspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + workspaces = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, workspace := range workspaces { + informer.Tenant().V1alpha1().Workspaces().Informer().GetIndexer().Add(workspace) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/workspacerole/workspaceroles.go b/pkg/models/resources/v1alpha3/workspacerole/workspaceroles.go new file mode 100644 index 0000000000000000000000000000000000000000..71df9072169982e79e80baabab328fab86b44dc9 --- /dev/null +++ b/pkg/models/resources/v1alpha3/workspacerole/workspaceroles.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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 workspacerole + +import ( + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type workspacerolesGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &workspacerolesGetter{sharedInformers: sharedInformers} +} + +func (d *workspacerolesGetter) Get(_, name string) (runtime.Object, error) { + return d.sharedInformers.Iam().V1alpha2().WorkspaceRoles().Lister().Get(name) +} + +func (d *workspacerolesGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Iam().V1alpha2().WorkspaceRoles().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *workspacerolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*iamv1alpha2.WorkspaceRole) + if !ok { + return false + } + + rightRole, ok := right.(*iamv1alpha2.WorkspaceRole) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *workspacerolesGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*iamv1alpha2.WorkspaceRole) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/workspacerole/workspaceroles_test.go b/pkg/models/resources/v1alpha3/workspacerole/workspaceroles_test.go new file mode 100644 index 0000000000000000000000000000000000000000..56b7b8187f28d1f44d0ef377f0fcc3b34226bacc --- /dev/null +++ b/pkg/models/resources/v1alpha3/workspacerole/workspaceroles_test.go @@ -0,0 +1,94 @@ +package workspacerole + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListWorkspaceRoles(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &iamv1alpha2.WorkspaceRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &iamv1alpha2.WorkspaceRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &iamv1alpha2.WorkspaceRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + roles = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, role := range roles { + informer.Iam().V1alpha2().WorkspaceRoles().Informer().GetIndexer().Add(role) + } + return New(informer) +} diff --git a/pkg/models/tenant/tenant.go b/pkg/models/tenant/tenant.go index 5f94d3f08a323f687a048fc13ade1610623a0f74..3bd88d6f56e1cd7e1a6a65503d7aee092519263b 100644 --- a/pkg/models/tenant/tenant.go +++ b/pkg/models/tenant/tenant.go @@ -18,45 +18,45 @@ package tenant import ( + "fmt" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/authentication/user" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" - iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory" + "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models/iam/am" - "kubesphere.io/kubesphere/pkg/simple/client/k8s" + resources "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + resourcesv1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" ) type Interface interface { - ListWorkspaces(user user.Info) (*api.ListResult, error) - ListNamespaces(user user.Info, workspace string) (*api.ListResult, error) + ListWorkspaces(user user.Info, query *query.Query) (*api.ListResult, error) + ListNamespaces(user user.Info, workspace string, query *query.Query) (*api.ListResult, error) } type tenantOperator struct { - informers informers.InformerFactory - am am.AccessManagementInterface - authorizer authorizer.Authorizer + am am.AccessManagementInterface + authorizer authorizer.Authorizer + resourceGetter *resourcesv1alpha3.ResourceGetter } -func New(k8sClient k8s.Client, informers informers.InformerFactory) Interface { - amOperator := am.NewAMOperator(k8sClient.KubeSphere(), informers.KubeSphereSharedInformerFactory()) +func New(informers informers.InformerFactory) Interface { + amOperator := am.NewAMOperator(informers) opaAuthorizer := authorizerfactory.NewOPAAuthorizer(amOperator) return &tenantOperator{ - informers: informers, - am: amOperator, - authorizer: opaAuthorizer, + am: amOperator, + authorizer: opaAuthorizer, + resourceGetter: resourcesv1alpha3.NewResourceGetter(informers), } } -func (t *tenantOperator) ListWorkspaces(user user.Info) (*api.ListResult, error) { - - workspaces := make([]*tenantv1alpha1.Workspace, 0) +func (t *tenantOperator) ListWorkspaces(user user.Info, queryParam *query.Query) (*api.ListResult, error) { listWS := authorizer.AttributesRecord{ User: user, @@ -74,49 +74,56 @@ func (t *tenantOperator) ListWorkspaces(user user.Info) (*api.ListResult, error) } if decision == authorizer.DecisionAllow { - workspaces, err = t.informers.KubeSphereSharedInformerFactory(). - Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything()) - if err != nil { - klog.Error(err) - return nil, err - } - } else { - workspaceRoles, err := t.am.ListRolesOfUser(iamv1alpha2.WorkspaceScope, user.GetName()) + result, err := t.resourceGetter.List(tenantv1alpha1.ResourcePluralWorkspace, "", queryParam) + if err != nil { klog.Error(err) return nil, err } - for _, role := range workspaceRoles { + return result, nil + } + + workspaceRoleBindings, err := t.am.ListWorkspaceRoleBindings(user.GetName(), "") + + if err != nil { + klog.Error(err) + return nil, err + } - workspace, err := t.informers.KubeSphereSharedInformerFactory(). - Tenant().V1alpha1().Workspaces().Lister().Get(role.Target.Name) + workspaces := make([]runtime.Object, 0) - if errors.IsNotFound(err) { - klog.Warningf("workspace role: %s found but workspace not exist", role.Target) - continue - } + for _, roleBinding := range workspaceRoleBindings { - if err != nil { - klog.Error(err) - return nil, err - } + workspaceName := roleBinding.Labels[tenantv1alpha1.WorkspaceLabel] + workspace, err := t.resourceGetter.Get(tenantv1alpha1.ResourcePluralWorkspace, "", workspaceName) - if !containsWorkspace(workspaces, workspace) { - workspaces = append(workspaces, workspace) - } + if errors.IsNotFound(err) { + klog.Warningf("workspace role: %+v found but workspace not exist", roleBinding.ObjectMeta) + continue + } + + if err != nil { + klog.Error(err) + return nil, err + } + + if !contains(workspaces, workspace) { + workspaces = append(workspaces, workspace) } } - return &api.ListResult{ - TotalItems: len(workspaces), - Items: workspacesToInterfaces(workspaces), - }, nil + result := resources.DefaultList(workspaces, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool { + return resources.DefaultObjectMetaCompare(left.(*tenantv1alpha1.Workspace).ObjectMeta, right.(*tenantv1alpha1.Workspace).ObjectMeta, field) + }, func(workspace runtime.Object, filter query.Filter) bool { + return resources.DefaultObjectMetaFilter(workspace.(*tenantv1alpha1.Workspace).ObjectMeta, filter) + }) + + return result, nil } -func (t *tenantOperator) ListNamespaces(user user.Info, workspace string) (*api.ListResult, error) { - namespaces := make([]*corev1.Namespace, 0) +func (t *tenantOperator) ListNamespaces(user user.Info, workspace string, queryParam *query.Query) (*api.ListResult, error) { listNSInWS := authorizer.AttributesRecord{ User: user, @@ -135,78 +142,65 @@ func (t *tenantOperator) ListNamespaces(user user.Info, workspace string) (*api. } if decision == authorizer.DecisionAllow { - namespaces, err = t.informers.KubernetesSharedInformerFactory(). - Core().V1().Namespaces().Lister().List(labels.Everything()) - if err != nil { - klog.Error(err) - return nil, err - } - } else { - namespaceRoles, err := t.am.ListRolesOfUser(iamv1alpha2.NamespaceScope, workspace) + queryParam.Filters[query.FieldLabel] = query.Value(fmt.Sprintf("%s=%s", tenantv1alpha1.WorkspaceLabel, workspace)) + + result, err := t.resourceGetter.List("namespaces", "", queryParam) if err != nil { klog.Error(err) return nil, err } - for _, role := range namespaceRoles { + return result, nil + } + + roleBindings, err := t.am.ListRoleBindings(user.GetName(), "") - namespace, err := t.informers.KubernetesSharedInformerFactory(). - Core().V1().Namespaces().Lister().Get(role.Target.Name) + if err != nil { + klog.Error(err) + return nil, err + } - if errors.IsNotFound(err) { - klog.Warningf("workspace role: %s found but workspace not exist", role.Target) - continue - } + namespaces := make([]runtime.Object, 0) - if err != nil { - klog.Error(err) - return nil, err - } + for _, roleBinding := range roleBindings { + namespaceName := roleBinding.Namespace + namespace, err := t.resourceGetter.Get("namespaces", "", namespaceName) - if !containsNamespace(namespaces, namespace) { - namespaces = append(namespaces, namespace) - } + if errors.IsNotFound(err) { + klog.Warningf("workspace role: %+v found but workspace not exist", roleBinding.ObjectMeta) + continue } - } - return &api.ListResult{ - TotalItems: len(namespaces), - Items: namespacesToInterfaces(namespaces), - }, nil -} + if err != nil { + klog.Error(err) + return nil, err + } -func containsWorkspace(workspaces []*tenantv1alpha1.Workspace, workspace *tenantv1alpha1.Workspace) bool { - for _, item := range workspaces { - if item.Name == workspace.Name { - return true + if !contains(namespaces, namespace) { + namespaces = append(namespaces, namespace) } } - return false -} -func containsNamespace(namespaces []*corev1.Namespace, namespace *corev1.Namespace) bool { - for _, item := range namespaces { - if item.Name == namespace.Name { - return true + result := resources.DefaultList(namespaces, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool { + return resources.DefaultObjectMetaCompare(left.(*corev1.Namespace).ObjectMeta, right.(*corev1.Namespace).ObjectMeta, field) + }, func(object runtime.Object, filter query.Filter) bool { + namespace := object.(*corev1.Namespace).ObjectMeta + if workspaceLabel, ok := namespace.Labels[tenantv1alpha1.WorkspaceLabel]; !ok || workspaceLabel != workspace { + return false } - } - return false -} + return resources.DefaultObjectMetaFilter(namespace, filter) + }) -func workspacesToInterfaces(workspaces []*tenantv1alpha1.Workspace) []interface{} { - ret := make([]interface{}, len(workspaces)) - for index, v := range workspaces { - ret[index] = v - } - return ret + return result, nil } -func namespacesToInterfaces(namespaces []*corev1.Namespace) []interface{} { - ret := make([]interface{}, len(namespaces)) - for index, v := range namespaces { - ret[index] = v +func contains(objects []runtime.Object, object runtime.Object) bool { + for _, item := range objects { + if item == object { + return true + } } - return ret + return false } diff --git a/pkg/simple/client/ldap/interface.go b/pkg/simple/client/ldap/interface.go index 8406263d121bad716252077e9df8e4b1ba149212..ea83b769b37c9e8ab2e0949d5ac34a0acade1dfc 100644 --- a/pkg/simple/client/ldap/interface.go +++ b/pkg/simple/client/ldap/interface.go @@ -1,7 +1,9 @@ package ldap import ( + "kubesphere.io/kubesphere/pkg/api" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" ) // Interface defines CRUD behaviors of manipulating users @@ -20,4 +22,6 @@ type Interface interface { // Authenticate checks if (name, password) is valid, return ErrInvalidCredentials if not Authenticate(name string, password string) error + + List(query *query.Query) (*api.ListResult, error) } diff --git a/pkg/simple/client/ldap/ldap.go b/pkg/simple/client/ldap/ldap.go index 4611592efcbb19784ae3060ff24acb2abac8c471..1372551e209a8e08bf4d9690d0be5fa1d4e88c53 100644 --- a/pkg/simple/client/ldap/ldap.go +++ b/pkg/simple/client/ldap/ldap.go @@ -22,8 +22,13 @@ import ( "github.com/go-ldap/ldap" "github.com/google/uuid" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/server/errors" + "sort" + "strings" "sync" "time" ) @@ -109,6 +114,7 @@ func (l *ldapInterfaceImpl) createSearchBase() error { if err != nil { return err } + defer conn.Close() createIfNotExistsFunc := func(request *ldap.AddRequest) error { searchRequest := &ldap.SearchRequest{ @@ -165,10 +171,10 @@ func (l *ldapInterfaceImpl) newConn() (ldap.Client, error) { if err != nil { return nil, err } + defer conn.Close() err = conn.Bind(l.managerDN, l.managerPassword) if err != nil { - conn.Close() return nil, err } return conn, nil @@ -357,6 +363,7 @@ func (l *ldapInterfaceImpl) Authenticate(username, password string) error { if err != nil { return err } + defer conn.Close() dn := l.dnForUsername(username) err = conn.Bind(dn, password) @@ -365,3 +372,106 @@ func (l *ldapInterfaceImpl) Authenticate(username, password string) error { } return err } + +func (l *ldapInterfaceImpl) List(query *query.Query) (*api.ListResult, error) { + conn, err := l.newConn() + if err != nil { + return nil, err + } + defer conn.Close() + + pageControl := ldap.NewControlPaging(1000) + + users := make([]iamv1alpha2.User, 0) + + filter := "(&(objectClass=inetOrgPerson))" + + if keyword := query.Filters["keyword"]; keyword != "" { + filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword) + } + + if username := query.Filters["username"]; username != "" { + uidFilter := "" + for _, username := range strings.Split(string(username), "|") { + uidFilter += fmt.Sprintf("(uid=%s)", username) + } + filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", uidFilter) + } + + if email := query.Filters["email"]; email != "" { + emailFilter := "" + for _, username := range strings.Split(string(email), "|") { + emailFilter += fmt.Sprintf("(mail=%s)", username) + } + filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", emailFilter) + } + + for { + userSearchRequest := ldap.NewSearchRequest( + l.userSearchBase, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + filter, + []string{"uid", "mail", "description", "preferredLanguage", "createTimestamp"}, + []ldap.Control{pageControl}, + ) + + response, err := conn.Search(userSearchRequest) + + if err != nil { + klog.Errorln("search user", err) + return nil, err + } + + for _, entry := range response.Entries { + + uid := entry.GetAttributeValue("uid") + email := entry.GetAttributeValue("mail") + description := entry.GetAttributeValue("description") + lang := entry.GetAttributeValue("preferredLanguage") + createTimestamp, _ := time.Parse("20060102150405Z", entry.GetAttributeValue("createTimestamp")) + + user := iamv1alpha2.User{ObjectMeta: metav1.ObjectMeta{Name: uid, CreationTimestamp: metav1.Time{Time: createTimestamp}}, Spec: iamv1alpha2.UserSpec{ + Email: email, + Lang: lang, + Description: description, + }} + + users = append(users, user) + } + + updatedControl := ldap.FindControl(response.Controls, ldap.ControlTypePaging) + if ctrl, ok := updatedControl.(*ldap.ControlPaging); ctrl != nil && ok && len(ctrl.Cookie) != 0 { + pageControl.SetCookie(ctrl.Cookie) + continue + } + + break + } + + sort.Slice(users, func(i, j int) bool { + if !query.Ascending { + i, j = j, i + } + switch query.SortBy { + case "username": + return strings.Compare(users[i].Name, users[j].Name) <= 0 + case "createTime": + fallthrough + default: + return users[i].CreationTimestamp.Before(&users[j].CreationTimestamp) + } + }) + + items := make([]interface{}, 0) + + for i, user := range users { + if i >= query.Pagination.Offset && len(items) < query.Pagination.Limit { + items = append(items, user) + } + } + + return &api.ListResult{ + Items: items, + TotalItems: len(users), + }, nil +} diff --git a/pkg/simple/client/ldap/simple_ldap.go b/pkg/simple/client/ldap/simple_ldap.go index c2550fe218595fde44c591881d0c9ea8bd2b089b..38a4ab8557375716da199d29425c1891813f8939 100644 --- a/pkg/simple/client/ldap/simple_ldap.go +++ b/pkg/simple/client/ldap/simple_ldap.go @@ -2,7 +2,9 @@ package ldap import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" ) // simpleLdap is a implementation of ldap.Interface, you should never use this in production env! @@ -74,3 +76,16 @@ func (s simpleLdap) Authenticate(name string, password string) error { return nil } + +func (l *simpleLdap) List(query *query.Query) (*api.ListResult, error) { + items := make([]interface{}, 0) + + for _, user := range l.store { + items = append(items, user) + } + + return &api.ListResult{ + Items: items, + TotalItems: len(items), + }, nil +} diff --git a/pkg/simple/client/logging/elasticsearch/api_body_test.go b/pkg/simple/client/logging/elasticsearch/api_body_test.go index f5202550b8334a1683c91dd67cfa91b4693737b5..6e160f35881bd3c0eb9c8e1cffd13bc296b64120 100644 --- a/pkg/simple/client/logging/elasticsearch/api_body_test.go +++ b/pkg/simple/client/logging/elasticsearch/api_body_test.go @@ -4,80 +4,8 @@ import ( "github.com/google/go-cmp/cmp" "kubesphere.io/kubesphere/pkg/simple/client/logging" "testing" - "time" ) -func TestMainBool(t *testing.T) { - var tests = []struct { - description string - searchFilter logging.SearchFilter - expected *bodyBuilder - }{ - { - description: "filter 2 namespaces", - searchFilter: logging.SearchFilter{ - NamespaceFilter: map[string]time.Time{ - "kubesphere-system": time.Unix(1582000000, 0), - "kubesphere-logging-system": time.Unix(1582969999, 0), - }, - }, - expected: &bodyBuilder{Body{ - Query: &Query{ - Bool: Bool{ - Filter: []Match{ - { - Bool: &Bool{ - Should: []Match{ - { - Bool: &Bool{ - Filter: []Match{ - { - MatchPhrase: map[string]string{"kubernetes.namespace_name.keyword": "kubesphere-system"}, - }, - { - Range: &Range{&Time{Gte: func() *time.Time { t := time.Unix(1582000000, 0); return &t }()}}, - }, - }, - }, - }, - { - Bool: &Bool{ - Filter: []Match{ - { - MatchPhrase: map[string]string{"kubernetes.namespace_name.keyword": "kubesphere-logging-system"}, - }, - { - Range: &Range{&Time{Gte: func() *time.Time { t := time.Unix(1582969999, 0); return &t }()}}, - }, - }, - }, - }, - }, - MinimumShouldMatch: 1, - }, - }, - }, - }, - }, - }}, - }, - } - - for _, test := range tests { - t.Run(test.description, func(t *testing.T) { - body, err := newBodyBuilder().mainBool(test.searchFilter).bytes() - expected, _ := test.expected.bytes() - if err != nil { - t.Fatal(err) - } - - if diff := cmp.Diff(body, expected); diff != "" { - t.Fatalf("%T differ (-got, +want): %s", expected, diff) - } - }) - } -} - func TestCardinalityAggregation(t *testing.T) { var test = struct { description string diff --git a/vendor/k8s.io/kubectl/LICENSE b/vendor/k8s.io/kubectl/LICENSE deleted file mode 100644 index 8dada3edaf50dbc082c9a125058f25def75e625a..0000000000000000000000000000000000000000 --- a/vendor/k8s.io/kubectl/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/k8s.io/kubectl/pkg/util/openapi/OWNERS b/vendor/k8s.io/kubectl/pkg/util/openapi/OWNERS deleted file mode 100644 index 99dabed0834caf43e96fa32757bad608454671e5..0000000000000000000000000000000000000000 --- a/vendor/k8s.io/kubectl/pkg/util/openapi/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -# See the OWNERS docs at https://go.k8s.io/owners - -approvers: -- apelisse -reviewers: -- apelisse diff --git a/vendor/k8s.io/kubectl/pkg/util/openapi/doc.go b/vendor/k8s.io/kubectl/pkg/util/openapi/doc.go deleted file mode 100644 index 08194d58084cc36f42848bda234cfbd4c1d612ac..0000000000000000000000000000000000000000 --- a/vendor/k8s.io/kubectl/pkg/util/openapi/doc.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2017 The Kubernetes 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 openapi is a collection of libraries for fetching the openapi spec -// from a Kubernetes server and then indexing the type definitions. -// The openapi spec contains the object model definitions and extensions metadata -// such as the patchStrategy and patchMergeKey for creating patches. -package openapi // k8s.io/kubectl/pkg/util/openapi diff --git a/vendor/k8s.io/kubectl/pkg/util/openapi/dryrun.go b/vendor/k8s.io/kubectl/pkg/util/openapi/dryrun.go deleted file mode 100644 index 33cf9e9e5c89bcd2cbc43b2f003e57aa2b6432ef..0000000000000000000000000000000000000000 --- a/vendor/k8s.io/kubectl/pkg/util/openapi/dryrun.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2017 The Kubernetes 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 openapi - -import ( - "errors" - - openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2" - yaml "gopkg.in/yaml.v2" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -func hasGVKExtension(extensions []*openapi_v2.NamedAny, gvk schema.GroupVersionKind) bool { - for _, extension := range extensions { - if extension.GetValue().GetYaml() == "" || - extension.GetName() != "x-kubernetes-group-version-kind" { - continue - } - var value map[string]string - err := yaml.Unmarshal([]byte(extension.GetValue().GetYaml()), &value) - if err != nil { - continue - } - - if value["group"] == gvk.Group && value["kind"] == gvk.Kind && value["version"] == gvk.Version { - return true - } - return false - } - return false -} - -// SupportsDryRun is a method that let's us look in the OpenAPI if the -// specific group-version-kind supports the dryRun query parameter for -// the PATCH end-point. -func SupportsDryRun(doc *openapi_v2.Document, gvk schema.GroupVersionKind) (bool, error) { - for _, path := range doc.GetPaths().GetPath() { - // Is this describing the gvk we're looking for? - if !hasGVKExtension(path.GetValue().GetPatch().GetVendorExtension(), gvk) { - continue - } - for _, param := range path.GetValue().GetPatch().GetParameters() { - if param.GetParameter().GetNonBodyParameter().GetQueryParameterSubSchema().GetName() == "dryRun" { - return true, nil - } - } - return false, nil - } - - return false, errors.New("couldn't find GVK in openapi") -} diff --git a/vendor/k8s.io/kubectl/pkg/util/openapi/extensions.go b/vendor/k8s.io/kubectl/pkg/util/openapi/extensions.go deleted file mode 100644 index f1b5cdd4f2767d3cb485cfbca4027f3fcbda9838..0000000000000000000000000000000000000000 --- a/vendor/k8s.io/kubectl/pkg/util/openapi/extensions.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2017 The Kubernetes 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 openapi - -import "github.com/go-openapi/spec" - -// PrintColumnsKey is the key that defines which columns should be printed -const PrintColumnsKey = "x-kubernetes-print-columns" - -// GetPrintColumns looks for the open API extension for the display columns. -func GetPrintColumns(extensions spec.Extensions) (string, bool) { - return extensions.GetString(PrintColumnsKey) -} diff --git a/vendor/k8s.io/kubectl/pkg/util/openapi/openapi.go b/vendor/k8s.io/kubectl/pkg/util/openapi/openapi.go deleted file mode 100644 index c8f370b99bfe3612e33bd19e5d32d6698e31a398..0000000000000000000000000000000000000000 --- a/vendor/k8s.io/kubectl/pkg/util/openapi/openapi.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright 2017 The Kubernetes 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 openapi - -import ( - openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2" - - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/kube-openapi/pkg/util/proto" -) - -// Resources interface describe a resources provider, that can give you -// resource based on group-version-kind. -type Resources interface { - LookupResource(gvk schema.GroupVersionKind) proto.Schema -} - -// groupVersionKindExtensionKey is the key used to lookup the -// GroupVersionKind value for an object definition from the -// definition's "extensions" map. -const groupVersionKindExtensionKey = "x-kubernetes-group-version-kind" - -// document is an implementation of `Resources`. It looks for -// resources in an openapi Schema. -type document struct { - // Maps gvk to model name - resources map[schema.GroupVersionKind]string - models proto.Models -} - -var _ Resources = &document{} - -// NewOpenAPIData creates a new `Resources` out of the openapi document -func NewOpenAPIData(doc *openapi_v2.Document) (Resources, error) { - models, err := proto.NewOpenAPIData(doc) - if err != nil { - return nil, err - } - - resources := map[schema.GroupVersionKind]string{} - for _, modelName := range models.ListModels() { - model := models.LookupModel(modelName) - if model == nil { - panic("ListModels returns a model that can't be looked-up.") - } - gvkList := parseGroupVersionKind(model) - for _, gvk := range gvkList { - if len(gvk.Kind) > 0 { - resources[gvk] = modelName - } - } - } - - return &document{ - resources: resources, - models: models, - }, nil -} - -func (d *document) LookupResource(gvk schema.GroupVersionKind) proto.Schema { - modelName, found := d.resources[gvk] - if !found { - return nil - } - return d.models.LookupModel(modelName) -} - -// Get and parse GroupVersionKind from the extension. Returns empty if it doesn't have one. -func parseGroupVersionKind(s proto.Schema) []schema.GroupVersionKind { - extensions := s.GetExtensions() - - gvkListResult := []schema.GroupVersionKind{} - - // Get the extensions - gvkExtension, ok := extensions[groupVersionKindExtensionKey] - if !ok { - return []schema.GroupVersionKind{} - } - - // gvk extension must be a list of at least 1 element. - gvkList, ok := gvkExtension.([]interface{}) - if !ok { - return []schema.GroupVersionKind{} - } - - for _, gvk := range gvkList { - // gvk extension list must be a map with group, version, and - // kind fields - gvkMap, ok := gvk.(map[interface{}]interface{}) - if !ok { - continue - } - group, ok := gvkMap["group"].(string) - if !ok { - continue - } - version, ok := gvkMap["version"].(string) - if !ok { - continue - } - kind, ok := gvkMap["kind"].(string) - if !ok { - continue - } - - gvkListResult = append(gvkListResult, schema.GroupVersionKind{ - Group: group, - Version: version, - Kind: kind, - }) - } - - return gvkListResult -} diff --git a/vendor/k8s.io/kubectl/pkg/util/openapi/openapi_getter.go b/vendor/k8s.io/kubectl/pkg/util/openapi/openapi_getter.go deleted file mode 100644 index d5c9476a02b8a2f85237301d8138ea3c655b7691..0000000000000000000000000000000000000000 --- a/vendor/k8s.io/kubectl/pkg/util/openapi/openapi_getter.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2017 The Kubernetes 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 openapi - -import ( - "sync" - - "k8s.io/client-go/discovery" -) - -// synchronizedOpenAPIGetter fetches the openapi schema once and then caches it in memory -type synchronizedOpenAPIGetter struct { - // Cached results - sync.Once - openAPISchema Resources - err error - - openAPIClient discovery.OpenAPISchemaInterface -} - -var _ Getter = &synchronizedOpenAPIGetter{} - -// Getter is an interface for fetching openapi specs and parsing them into an Resources struct -type Getter interface { - // OpenAPIData returns the parsed OpenAPIData - Get() (Resources, error) -} - -// NewOpenAPIGetter returns an object to return OpenAPIDatas which reads -// from a server, and then stores in memory for subsequent invocations -func NewOpenAPIGetter(openAPIClient discovery.OpenAPISchemaInterface) Getter { - return &synchronizedOpenAPIGetter{ - openAPIClient: openAPIClient, - } -} - -// Resources implements Getter -func (g *synchronizedOpenAPIGetter) Get() (Resources, error) { - g.Do(func() { - s, err := g.openAPIClient.OpenAPISchema() - if err != nil { - g.err = err - return - } - - g.openAPISchema, g.err = NewOpenAPIData(s) - }) - - // Return the save result - return g.openAPISchema, g.err -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 376b710ac4ca9566412746668c5d52041eddd42b..b24f80e149098646f841f18dad2b0823e638a713 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -664,7 +664,7 @@ golang.org/x/crypto/ssh golang.org/x/crypto/ssh/agent golang.org/x/crypto/ssh/knownhosts golang.org/x/crypto/ssh/terminal -# golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 => golang.org/x/net v0.0.0-20190620200207-3b0461eec859 +# golang.org/x/net v0.0.0-20190923162816-aa69164e4478 => golang.org/x/net v0.0.0-20190620200207-3b0461eec859 golang.org/x/net/context golang.org/x/net/context/ctxhttp golang.org/x/net/html @@ -1368,8 +1368,6 @@ k8s.io/kube-openapi/pkg/schemaconv k8s.io/kube-openapi/pkg/util k8s.io/kube-openapi/pkg/util/proto k8s.io/kube-openapi/pkg/util/sets -# k8s.io/kubectl v0.17.3 => k8s.io/kubectl v0.17.3 -k8s.io/kubectl/pkg/util/openapi # k8s.io/utils v0.0.0-20191114184206-e782cd3c129f => k8s.io/utils v0.0.0-20191114184206-e782cd3c129f k8s.io/utils/buffer k8s.io/utils/integer @@ -1444,7 +1442,6 @@ sigs.k8s.io/controller-tools/pkg/webhook # sigs.k8s.io/kubefed v0.2.0-alpha.1 => sigs.k8s.io/kubefed v0.2.0-alpha.1 sigs.k8s.io/kubefed/pkg/apis sigs.k8s.io/kubefed/pkg/apis/core/common -sigs.k8s.io/kubefed/pkg/apis/core/typeconfig sigs.k8s.io/kubefed/pkg/apis/core/v1alpha1 sigs.k8s.io/kubefed/pkg/apis/core/v1beta1 sigs.k8s.io/kubefed/pkg/apis/multiclusterdns/v1alpha1 @@ -1452,13 +1449,8 @@ sigs.k8s.io/kubefed/pkg/apis/scheduling/v1alpha1 sigs.k8s.io/kubefed/pkg/client/generic sigs.k8s.io/kubefed/pkg/client/generic/scheme sigs.k8s.io/kubefed/pkg/controller/util -sigs.k8s.io/kubefed/pkg/kubefedctl -sigs.k8s.io/kubefed/pkg/kubefedctl/enable -sigs.k8s.io/kubefed/pkg/kubefedctl/federate sigs.k8s.io/kubefed/pkg/kubefedctl/options -sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning sigs.k8s.io/kubefed/pkg/kubefedctl/util -sigs.k8s.io/kubefed/pkg/version # sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca => sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca sigs.k8s.io/structured-merge-diff/fieldpath sigs.k8s.io/structured-merge-diff/merge diff --git a/vendor/sigs.k8s.io/kubefed/pkg/apis/core/typeconfig/interface.go b/vendor/sigs.k8s.io/kubefed/pkg/apis/core/typeconfig/interface.go deleted file mode 100644 index a5926272d46126ba42b4f0c7df1e7bf627aae225..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/apis/core/typeconfig/interface.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 typeconfig - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// Interface defines how to interact with a FederatedTypeConfig -type Interface interface { - GetObjectMeta() metav1.ObjectMeta - GetTargetType() metav1.APIResource - GetNamespaced() bool - GetPropagationEnabled() bool - GetFederatedType() metav1.APIResource - GetStatusType() *metav1.APIResource - GetStatusEnabled() bool - GetFederatedNamespaced() bool - IsNamespace() bool -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/apis/core/typeconfig/util.go b/vendor/sigs.k8s.io/kubefed/pkg/apis/core/typeconfig/util.go deleted file mode 100644 index 999d645409be220885d0e03459c706a5a50d7ba3..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/apis/core/typeconfig/util.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 typeconfig - -import ( - "fmt" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// GroupQualifiedName returns the plural name of the api resource -// optionally qualified by its group: -// -// '[.]' -// -// This is the naming scheme for FederatedTypeConfig resources. The -// scheme ensures that, for a given KubeFed control plane, -// federation of a target type will be configured by at most one -// FederatedTypeConfig. -func GroupQualifiedName(apiResource metav1.APIResource) string { - if len(apiResource.Group) == 0 { - return apiResource.Name - } - return fmt.Sprintf("%s.%s", apiResource.Name, apiResource.Group) -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/disable.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/disable.go deleted file mode 100644 index 1c23b187765e0c234d8006096818a321603f494d..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/disable.go +++ /dev/null @@ -1,378 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 kubefedctl - -import ( - "context" - "fmt" - "io" - "strings" - "time" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - apiextv1b1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/rest" - "k8s.io/klog" - - "sigs.k8s.io/kubefed/pkg/apis/core/typeconfig" - fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" - genericclient "sigs.k8s.io/kubefed/pkg/client/generic" - ctlutil "sigs.k8s.io/kubefed/pkg/controller/util" - "sigs.k8s.io/kubefed/pkg/kubefedctl/enable" - "sigs.k8s.io/kubefed/pkg/kubefedctl/options" - "sigs.k8s.io/kubefed/pkg/kubefedctl/util" -) - -const ( - federatedGroupUsage = "The name of the API group to use for deleting the federated CRD type when the federated type config does not exist. Only used with --delete-crd." - targetVersionUsage = "The API version of the target type to use for deletion of the federated CRD type when the federated type config does not exist. Only used with --delete-crd." -) - -var ( - disable_long = ` - Disables propagation of a Kubernetes API type. This command - can also optionally delete the API resources added by the enable - command. - - Current context is assumed to be a Kubernetes cluster hosting - the kubefed control plane. Please use the - --host-cluster-context flag otherwise.` - - disable_example = ` - # Disable propagation of the kubernetes API type 'Deployment', named - in FederatedTypeConfig as 'deployments.apps' - kubefedctl disable deployments.apps - - # Disable propagation of the kubernetes API type 'Deployment', named - in FederatedTypeConfig as 'deployments.apps', and delete the - corresponding Federated API resource - kubefedctl disable deployments.apps --delete-crd` -) - -type disableType struct { - options.GlobalSubcommandOptions - options.CommonEnableOptions - disableTypeOptions -} - -type disableTypeOptions struct { - deleteCRD bool - enableTypeDirective *enable.EnableTypeDirective -} - -// Bind adds the disable specific arguments to the flagset passed in as an -// argument. -func (o *disableTypeOptions) Bind(flags *pflag.FlagSet) { - flags.BoolVar(&o.deleteCRD, "delete-crd", false, "Whether to remove the API resource added by 'enable'.") -} - -// NewCmdTypeDisable defines the `disable` command that -// disables federation of a Kubernetes API type. -func NewCmdTypeDisable(cmdOut io.Writer, config util.FedConfig) *cobra.Command { - opts := &disableType{} - - cmd := &cobra.Command{ - Use: "disable NAME", - Short: "Disables propagation of a Kubernetes API type", - Long: disable_long, - Example: disable_example, - Run: func(cmd *cobra.Command, args []string) { - err := opts.Complete(args) - if err != nil { - klog.Fatalf("Error: %v", err) - } - - err = opts.Run(cmdOut, config) - if err != nil { - klog.Fatalf("Error: %v", err) - } - }, - } - - flags := cmd.Flags() - opts.GlobalSubcommandBind(flags) - opts.CommonSubcommandBind(flags, federatedGroupUsage, targetVersionUsage) - opts.Bind(flags) - - return cmd -} - -// Complete ensures that options are valid and marshals them if necessary. -func (j *disableType) Complete(args []string) error { - j.enableTypeDirective = enable.NewEnableTypeDirective() - directive := j.enableTypeDirective - - if err := j.SetName(args); err != nil { - return err - } - - if !j.deleteCRD { - if len(j.TargetVersion) > 0 { - return errors.New("--version flag valid only with --delete-crd") - } else if j.FederatedGroup != options.DefaultFederatedGroup { - return errors.New("--kubefed-group flag valid only with --delete-crd") - } - } - - if len(j.TargetVersion) > 0 { - directive.Spec.TargetVersion = j.TargetVersion - } - if len(j.FederatedGroup) > 0 { - directive.Spec.FederatedGroup = j.FederatedGroup - } - - return nil -} - -// Run is the implementation of the `disable` command. -func (j *disableType) Run(cmdOut io.Writer, config util.FedConfig) error { - hostConfig, err := config.HostConfig(j.HostClusterContext, j.Kubeconfig) - if err != nil { - return errors.Wrap(err, "Failed to get host cluster config") - } - - // If . is specified, the target name is assumed as a group qualified name. - // In such case, ignore the lookup to make sure deletion of a federatedtypeconfig - // for which the corresponding target has been removed. - name := j.TargetName - if !strings.Contains(j.TargetName, ".") { - apiResource, err := enable.LookupAPIResource(hostConfig, j.TargetName, "") - if err != nil { - return err - } - name = typeconfig.GroupQualifiedName(*apiResource) - } - - typeConfigName := ctlutil.QualifiedName{ - Namespace: j.KubeFedNamespace, - Name: name, - } - j.enableTypeDirective.Name = typeConfigName.Name - return DisableFederation(cmdOut, hostConfig, j.enableTypeDirective, typeConfigName, j.deleteCRD, j.DryRun, true) -} - -func DisableFederation(cmdOut io.Writer, config *rest.Config, enableTypeDirective *enable.EnableTypeDirective, - typeConfigName ctlutil.QualifiedName, deleteCRD, dryRun, verifyStopped bool) error { - client, err := genericclient.New(config) - if err != nil { - return errors.Wrap(err, "Failed to get kubefed clientset") - } - - write := func(data string) { - if cmdOut == nil { - return - } - - if _, err := cmdOut.Write([]byte(data)); err != nil { - klog.Fatalf("Unexpected err: %v\n", err) - } - } - - typeConfig := &fedv1b1.FederatedTypeConfig{} - ftcExists, err := checkFederatedTypeConfigExists(client, typeConfig, typeConfigName, write) - if err != nil { - return err - } - - if dryRun { - return nil - } - - // Disable propagation and verify it is stopped before deleting the CRD - // when no custom resources exist. This avoids spurious error messages in - // the controller manager log as watches are terminated and cannot be - // reestablished. - if ftcExists { - if deleteCRD { - err = checkFederatedTypeCustomResourcesExist(config, typeConfig, write) - if err != nil { - return err - } - } - if typeConfig.GetPropagationEnabled() { - err = disablePropagation(client, typeConfig, typeConfigName, write) - if err != nil { - return err - } - } - if verifyStopped { - err = verifyPropagationControllerStopped(client, typeConfigName, write) - if err != nil { - return err - } - } - } - - if deleteCRD { - if !ftcExists { - typeConfig, err = generatedFederatedTypeConfig(config, enableTypeDirective) - if err != nil { - return err - } - } - err = deleteFederatedType(config, typeConfig, write) - if err != nil { - return err - } - } - - if ftcExists { - err = deleteFederatedTypeConfig(client, typeConfig, typeConfigName, write) - if err != nil { - return err - } - } - - return nil -} - -func checkFederatedTypeConfigExists(client genericclient.Client, typeConfig *fedv1b1.FederatedTypeConfig, typeConfigName ctlutil.QualifiedName, write func(string)) (bool, error) { - err := client.Get(context.TODO(), typeConfig, typeConfigName.Namespace, typeConfigName.Name) - if err == nil { - return true, nil - } - - if apierrors.IsNotFound(err) { - write(fmt.Sprintf("FederatedTypeConfig %q does not exist\n", typeConfigName)) - return false, nil - } - - return false, errors.Wrapf(err, "Error retrieving FederatedTypeConfig %q", typeConfigName) -} - -func disablePropagation(client genericclient.Client, typeConfig *fedv1b1.FederatedTypeConfig, typeConfigName ctlutil.QualifiedName, write func(string)) error { - if typeConfig.GetPropagationEnabled() { - typeConfig.Spec.Propagation = fedv1b1.PropagationDisabled - err := client.Update(context.TODO(), typeConfig) - if err != nil { - return errors.Wrapf(err, "Error disabling propagation for FederatedTypeConfig %q", typeConfigName) - } - write(fmt.Sprintf("Disabled propagation for FederatedTypeConfig %q\n", typeConfigName)) - } else { - write(fmt.Sprintf("Propagation already disabled for FederatedTypeConfig %q\n", typeConfigName)) - } - return nil -} - -func verifyPropagationControllerStopped(client genericclient.Client, typeConfigName ctlutil.QualifiedName, write func(string)) error { - write(fmt.Sprintf("Verifying propagation controller is stopped for FederatedTypeConfig %q\n", typeConfigName)) - - var typeConfig *fedv1b1.FederatedTypeConfig - err := wait.PollImmediate(100*time.Millisecond, 10*time.Second, func() (bool, error) { - typeConfig = &fedv1b1.FederatedTypeConfig{} - err := client.Get(context.TODO(), typeConfig, typeConfigName.Namespace, typeConfigName.Name) - if err != nil { - klog.Errorf("Error retrieving FederatedTypeConfig %q: %v", typeConfigName, err) - return false, nil - } - if typeConfig.Status.PropagationController == fedv1b1.ControllerStatusNotRunning { - return true, nil - } - return false, nil - }) - - if err != nil { - return errors.Wrapf(err, "Unable to verify propagation controller for FederatedTypeConfig %q is stopped: %v", typeConfigName, err) - } - - write(fmt.Sprintf("Propagation controller for FederatedTypeConfig %q is stopped\n", typeConfigName)) - return nil -} - -func deleteFederatedTypeConfig(client genericclient.Client, typeConfig *fedv1b1.FederatedTypeConfig, typeConfigName ctlutil.QualifiedName, write func(string)) error { - err := client.Delete(context.TODO(), typeConfig, typeConfig.Namespace, typeConfig.Name) - if err != nil { - return errors.Wrapf(err, "Error deleting FederatedTypeConfig %q", typeConfigName) - } - write(fmt.Sprintf("federatedtypeconfig %q deleted\n", typeConfigName)) - return nil -} - -func generatedFederatedTypeConfig(config *rest.Config, enableTypeDirective *enable.EnableTypeDirective) (*fedv1b1.FederatedTypeConfig, error) { - apiResource, err := enable.LookupAPIResource(config, enableTypeDirective.Name, enableTypeDirective.Spec.TargetVersion) - if err != nil { - return nil, err - } - typeConfig := enable.GenerateTypeConfigForTarget(*apiResource, enableTypeDirective).(*fedv1b1.FederatedTypeConfig) - return typeConfig, nil -} - -func deleteFederatedType(config *rest.Config, typeConfig typeconfig.Interface, write func(string)) error { - err := checkFederatedTypeCustomResourcesExist(config, typeConfig, write) - if err != nil { - return err - } - - crdName := typeconfig.GroupQualifiedName(typeConfig.GetFederatedType()) - err = deleteFederatedCRD(config, crdName, write) - if err != nil { - return err - } - - return nil -} - -func checkFederatedTypeCustomResourcesExist(config *rest.Config, typeConfig typeconfig.Interface, write func(string)) error { - federatedTypeAPIResource := typeConfig.GetFederatedType() - crdName := typeconfig.GroupQualifiedName(federatedTypeAPIResource) - exists, err := customResourcesExist(config, &federatedTypeAPIResource) - if err != nil { - return err - } else if exists { - return errors.Errorf("Cannot delete CRD %q while resource instances exist. Please try kubefedctl disable again after removing the resource instances or without the '--delete-crd' option\n", crdName) - } - return nil -} - -func customResourcesExist(config *rest.Config, resource *metav1.APIResource) (bool, error) { - client, err := ctlutil.NewResourceClient(config, resource) - if err != nil { - return false, err - } - - options := metav1.ListOptions{} - objList, err := client.Resources("").List(options) - if apierrors.IsNotFound(err) { - return false, nil - } else if err != nil { - return false, err - } - return len(objList.Items) != 0, nil -} - -func deleteFederatedCRD(config *rest.Config, crdName string, write func(string)) error { - client, err := apiextv1b1client.NewForConfig(config) - if err != nil { - return errors.Wrap(err, "Error creating crd client") - } - - err = client.CustomResourceDefinitions().Delete(crdName, nil) - if apierrors.IsNotFound(err) { - write(fmt.Sprintf("customresourcedefinition %q does not exist\n", crdName)) - } else if err != nil { - return errors.Wrapf(err, "Error deleting crd %q", crdName) - } else { - write(fmt.Sprintf("customresourcedefinition %q deleted\n", crdName)) - } - return nil -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/deprecatedapis.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/deprecatedapis.go deleted file mode 100644 index 22d77ea1f48c40804caa35660d6d0250695c809d..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/deprecatedapis.go +++ /dev/null @@ -1,153 +0,0 @@ -/* -Copyright 2019 The Kubernetes 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 enable - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - - fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" -) - -// Deprecated APIs removed in 1.16 will be served by current equivalent APIs -// https://kubernetes.io/blog/2019/07/18/api-deprecations-in-1-16/ -// -// Only allow one of the equivalent APIs for federation to avoid the possibility -// of multiple sync controllers fighting to update the same resource -var equivalentAPIs = map[string][]schema.GroupVersion{ - "deployments": { - { - Group: "apps", - Version: "v1", - }, - { - Group: "apps", - Version: "v1beta1", - }, - { - Group: "apps", - Version: "v1beta2", - }, - { - Group: "extensions", - Version: "v1beta1", - }, - }, - "daemonsets": { - { - Group: "apps", - Version: "v1", - }, - { - Group: "apps", - Version: "v1beta1", - }, - { - Group: "apps", - Version: "v1beta2", - }, - { - Group: "extensions", - Version: "v1beta1", - }, - }, - "statefulsets": { - { - Group: "apps", - Version: "v1", - }, - { - Group: "apps", - Version: "v1beta1", - }, - { - Group: "apps", - Version: "v1beta2", - }, - }, - "replicasets": { - { - Group: "apps", - Version: "v1", - }, - { - Group: "apps", - Version: "v1beta1", - }, - { - Group: "apps", - Version: "v1beta2", - }, - { - Group: "extensions", - Version: "v1beta1", - }, - }, - "networkpolicies": { - { - Group: "networking.k8s.io", - Version: "v1", - }, - { - Group: "extensions", - Version: "v1beta1", - }, - }, - "podsecuritypolicies": { - { - Group: "policy", - Version: "v1beta1", - }, - { - Group: "extensions", - Version: "v1beta1", - }, - }, - "ingresses": { - { - Group: "networking.k8s.io", - Version: "v1beta1", - }, - { - Group: "extensions", - Version: "v1beta1", - }, - }, -} - -func IsEquivalentAPI(existingAPI, newAPI *fedv1b1.APIResource) bool { - if existingAPI.PluralName != newAPI.PluralName { - return false - } - - apis, ok := equivalentAPIs[existingAPI.PluralName] - if !ok { - return false - } - - for _, gv := range apis { - if gv.Group == existingAPI.Group && gv.Version == existingAPI.Version { - // skip exactly matched API from equivalent API list - continue - } - - if gv.Group == newAPI.Group && gv.Version == newAPI.Version { - return true - } - } - - return false -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/directive.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/directive.go deleted file mode 100644 index 48919900b9ed1e3f548ef779fce29132df5c7c5e..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/directive.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 enable - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "sigs.k8s.io/kubefed/pkg/kubefedctl/options" -) - -// EnableTypeDirectiveSpec defines the desired state of EnableTypeDirective. -type EnableTypeDirectiveSpec struct { - // The API version of the target type. - // +optional - TargetVersion string `json:"targetVersion,omitempty"` - - // The name of the API group to use for generated federated types. - // +optional - FederatedGroup string `json:"federatedGroup,omitempty"` - - // The API version to use for generated federated types. - // +optional - FederatedVersion string `json:"federatedVersion,omitempty"` -} - -// TODO(marun) This should become a proper API type and drive enabling -// type federation via a controller. For now its only purpose is to -// enable loading of configuration from disk. -type EnableTypeDirective struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec EnableTypeDirectiveSpec `json:"spec,omitempty"` -} - -func (ft *EnableTypeDirective) SetDefaults() { - ft.Spec.FederatedGroup = options.DefaultFederatedGroup - ft.Spec.FederatedVersion = options.DefaultFederatedVersion -} - -func NewEnableTypeDirective() *EnableTypeDirective { - ft := &EnableTypeDirective{} - ft.SetDefaults() - return ft -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/enable.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/enable.go deleted file mode 100644 index ea5c8b114b12ca1a725bca0714888c3a11956511..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/enable.go +++ /dev/null @@ -1,428 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 enable - -import ( - "context" - "fmt" - "io" - - jsoniter "github.com/json-iterator/go" - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - apiextv1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - apiextv1b1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - pkgruntime "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/rest" - "k8s.io/klog" - - "sigs.k8s.io/kubefed/pkg/apis/core/typeconfig" - fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" - genericclient "sigs.k8s.io/kubefed/pkg/client/generic" - ctlutil "sigs.k8s.io/kubefed/pkg/controller/util" - "sigs.k8s.io/kubefed/pkg/kubefedctl/options" - "sigs.k8s.io/kubefed/pkg/kubefedctl/util" -) - -const ( - federatedGroupUsage = "The name of the API group to use for the generated federated type." - targetVersionUsage = "Optional, the API version of the target type." -) - -var ( - enable_long = ` - Enables a Kubernetes API type (including a CRD) to be propagated - to clusters registered with a KubeFed control plane. A CRD for - the federated type will be generated and a FederatedTypeConfig will - be created to configure a sync controller. - - Current context is assumed to be a Kubernetes cluster hosting - the kubefed control plane. Please use the - --host-cluster-context flag otherwise.` - - enable_example = ` - # Enable federation of Deployments - kubefedctl enable deployments.apps --host-cluster-context=cluster1 - - # Enable federation of Deployments identified by name specified in - # deployment.yaml - kubefedctl enable -f deployment.yaml` -) - -type enableType struct { - options.GlobalSubcommandOptions - options.CommonEnableOptions - enableTypeOptions -} - -type enableTypeOptions struct { - federatedVersion string - output string - outputYAML bool - filename string - enableTypeDirective *EnableTypeDirective -} - -// Bind adds the join specific arguments to the flagset passed in as an -// argument. -func (o *enableTypeOptions) Bind(flags *pflag.FlagSet) { - flags.StringVar(&o.federatedVersion, "federated-version", options.DefaultFederatedVersion, "The API version to use for the generated federated type.") - flags.StringVarP(&o.output, "output", "o", "", "If provided, the resources that would be created in the API by the command are instead output to stdout in the provided format. Valid values are ['yaml'].") - flags.StringVarP(&o.filename, "filename", "f", "", "If provided, the command will be configured from the provided yaml file. Only --output will be accepted from the command line") -} - -// NewCmdTypeEnable defines the `enable` command that -// enables federation of a Kubernetes API type. -func NewCmdTypeEnable(cmdOut io.Writer, config util.FedConfig) *cobra.Command { - opts := &enableType{} - - cmd := &cobra.Command{ - Use: "enable (NAME | -f FILENAME)", - Short: "Enables propagation of a Kubernetes API type", - Long: enable_long, - Example: enable_example, - Run: func(cmd *cobra.Command, args []string) { - err := opts.Complete(args) - if err != nil { - klog.Fatalf("Error: %v", err) - } - - err = opts.Run(cmdOut, config) - if err != nil { - klog.Fatalf("Error: %v", err) - } - }, - } - - flags := cmd.Flags() - opts.GlobalSubcommandBind(flags) - opts.CommonSubcommandBind(flags, federatedGroupUsage, targetVersionUsage) - opts.Bind(flags) - - return cmd -} - -// Complete ensures that options are valid and marshals them if necessary. -func (j *enableType) Complete(args []string) error { - j.enableTypeDirective = NewEnableTypeDirective() - fd := j.enableTypeDirective - - if j.output == "yaml" { - j.outputYAML = true - } else if len(j.output) > 0 { - return errors.Errorf("Invalid value for --output: %s", j.output) - } - - if len(j.filename) > 0 { - err := DecodeYAMLFromFile(j.filename, fd) - if err != nil { - return errors.Wrapf(err, "Failed to load yaml from file %q", j.filename) - } - return nil - } - - if err := j.SetName(args); err != nil { - return err - } - - fd.Name = j.TargetName - - if len(j.TargetVersion) > 0 { - fd.Spec.TargetVersion = j.TargetVersion - } - if len(j.FederatedGroup) > 0 { - fd.Spec.FederatedGroup = j.FederatedGroup - } - if len(j.federatedVersion) > 0 { - fd.Spec.FederatedVersion = j.federatedVersion - } - - return nil -} - -// Run is the implementation of the `enable` command. -func (j *enableType) Run(cmdOut io.Writer, config util.FedConfig) error { - hostConfig, err := config.HostConfig(j.HostClusterContext, j.Kubeconfig) - if err != nil { - return errors.Wrap(err, "Failed to get host cluster config") - } - - resources, err := GetResources(hostConfig, j.enableTypeDirective) - if err != nil { - return err - } - - if j.outputYAML { - concreteTypeConfig := resources.TypeConfig.(*fedv1b1.FederatedTypeConfig) - objects := []pkgruntime.Object{concreteTypeConfig, resources.CRD} - err := writeObjectsToYAML(objects, cmdOut) - if err != nil { - return errors.Wrap(err, "Failed to write objects to YAML") - } - // -o yaml implies dry run - return nil - } - - return CreateResources(cmdOut, hostConfig, resources, j.KubeFedNamespace, j.DryRun) -} - -type typeResources struct { - TypeConfig typeconfig.Interface - CRD *apiextv1b1.CustomResourceDefinition -} - -func GetResources(config *rest.Config, enableTypeDirective *EnableTypeDirective) (*typeResources, error) { - apiResource, err := LookupAPIResource(config, enableTypeDirective.Name, enableTypeDirective.Spec.TargetVersion) - if err != nil { - return nil, err - } - klog.V(2).Infof("Found type %q", resourceKey(*apiResource)) - - typeConfig := GenerateTypeConfigForTarget(*apiResource, enableTypeDirective) - - accessor, err := newSchemaAccessor(config, *apiResource) - if err != nil { - return nil, errors.Wrap(err, "Error initializing validation schema accessor") - } - - shortNames := []string{} - for _, shortName := range apiResource.ShortNames { - shortNames = append(shortNames, fmt.Sprintf("f%s", shortName)) - } - - crd := federatedTypeCRD(typeConfig, accessor, shortNames) - - return &typeResources{ - TypeConfig: typeConfig, - CRD: crd, - }, nil -} - -// TODO(marun) Allow updates to the configuration for a type that has -// already been enabled for kubefed. This would likely involve -// updating the version of the target type and the validation of the schema. -func CreateResources(cmdOut io.Writer, config *rest.Config, resources *typeResources, namespace string, dryRun bool) error { - write := func(data string) { - if cmdOut != nil { - if _, err := cmdOut.Write([]byte(data)); err != nil { - klog.Fatalf("Unexpected err: %v\n", err) - } - } - } - - hostClientset, err := util.HostClientset(config) - if err != nil { - return errors.Wrap(err, "Failed to create host clientset") - } - _, err = hostClientset.CoreV1().Namespaces().Get(namespace, metav1.GetOptions{}) - if apierrors.IsNotFound(err) { - return errors.Wrapf(err, "KubeFed system namespace %q does not exist", namespace) - } else if err != nil { - return errors.Wrapf(err, "Error attempting to determine whether KubeFed system namespace %q exists", namespace) - } - - client, err := genericclient.New(config) - if err != nil { - return errors.Wrap(err, "Failed to get kubefed clientset") - } - - concreteTypeConfig := resources.TypeConfig.(*fedv1b1.FederatedTypeConfig) - existingTypeConfig := &fedv1b1.FederatedTypeConfig{} - err = client.Get(context.TODO(), existingTypeConfig, namespace, concreteTypeConfig.Name) - if err != nil && !apierrors.IsNotFound(err) { - return errors.Wrapf(err, "Error retrieving FederatedTypeConfig %q", concreteTypeConfig.Name) - } - if err == nil { - fedType := existingTypeConfig.GetFederatedType() - target := existingTypeConfig.GetTargetType() - concreteType := concreteTypeConfig.GetFederatedType() - if fedType.Name != concreteType.Name || fedType.Version != concreteType.Version || fedType.Group != concreteType.Group { - return errors.Errorf("Federation is already enabled for %q with federated type %q. Changing the federated type to %q is not supported.", - qualifiedAPIResourceName(target), - qualifiedAPIResourceName(fedType), - qualifiedAPIResourceName(concreteType)) - } - } - - crdClient, err := apiextv1b1client.NewForConfig(config) - if err != nil { - return errors.Wrap(err, "Failed to create crd clientset") - } - - existingCRD, err := crdClient.CustomResourceDefinitions().Get(resources.CRD.Name, metav1.GetOptions{}) - if apierrors.IsNotFound(err) { - if !dryRun { - _, err = crdClient.CustomResourceDefinitions().Create(resources.CRD) - if err != nil { - return errors.Wrapf(err, "Error creating CRD %q", resources.CRD.Name) - } - } - write(fmt.Sprintf("customresourcedefinition.apiextensions.k8s.io/%s created\n", resources.CRD.Name)) - } else if err != nil { - return errors.Wrapf(err, "Error getting CRD %q", resources.CRD.Name) - } else { - ftcs := &fedv1b1.FederatedTypeConfigList{} - err := client.List(context.TODO(), ftcs, namespace) - if err != nil { - return errors.Wrap(err, "Error getting FederatedTypeConfig list") - } - - for _, ftc := range ftcs.Items { - targetAPI := concreteTypeConfig.Spec.TargetType - existingAPI := ftc.Spec.TargetType - if IsEquivalentAPI(&existingAPI, &targetAPI) { - existingName := qualifiedAPIResourceName(ftc.GetTargetType()) - name := qualifiedAPIResourceName(concreteTypeConfig.GetTargetType()) - qualifiedFTCName := ctlutil.QualifiedName{ - Namespace: ftc.Namespace, - Name: ftc.Name, - } - - return errors.Errorf("Failed to enable %q. Federation of this type is already enabled for equivalent type %q by FederatedTypeConfig %q", - name, existingName, qualifiedFTCName) - } - - if concreteTypeConfig.Name == ftc.Name { - continue - } - - fedType := ftc.Spec.FederatedType - name := typeconfig.GroupQualifiedName(metav1.APIResource{Name: fedType.PluralName, Group: fedType.Group}) - if name == existingCRD.Name { - return errors.Errorf("Failed to enable federation of %q due to the FederatedTypeConfig for %q already referencing a federated type CRD named %q. If these target types are distinct despite sharing the same kind, specifying a non-default --federated-group should allow %q to be enabled.", - concreteTypeConfig.Name, ftc.Name, name, concreteTypeConfig.Name) - } - } - - existingCRD.Spec = resources.CRD.Spec - if !dryRun { - _, err = crdClient.CustomResourceDefinitions().Update(existingCRD) - if err != nil { - return errors.Wrapf(err, "Error updating CRD %q", resources.CRD.Name) - } - } - write(fmt.Sprintf("customresourcedefinition.apiextensions.k8s.io/%s updated\n", resources.CRD.Name)) - } - - concreteTypeConfig.Namespace = namespace - err = client.Get(context.TODO(), existingTypeConfig, namespace, concreteTypeConfig.Name) - createdOrUpdated := "created" - if err != nil { - if !apierrors.IsNotFound(err) { - return errors.Wrapf(err, "Error retrieving FederatedTypeConfig %q", concreteTypeConfig.Name) - } - if !dryRun { - err = client.Create(context.TODO(), concreteTypeConfig) - if err != nil { - return errors.Wrapf(err, "Error creating FederatedTypeConfig %q", concreteTypeConfig.Name) - } - } - } else { - existingTypeConfig.Spec = concreteTypeConfig.Spec - if !dryRun { - err = client.Update(context.TODO(), existingTypeConfig) - if err != nil { - return errors.Wrapf(err, "Error updating FederatedTypeConfig %q", concreteTypeConfig.Name) - } - } - createdOrUpdated = "updated" - } - write(fmt.Sprintf("federatedtypeconfig.core.kubefed.io/%s %s in namespace %s\n", - concreteTypeConfig.Name, createdOrUpdated, namespace)) - return nil -} - -func GenerateTypeConfigForTarget(apiResource metav1.APIResource, enableTypeDirective *EnableTypeDirective) typeconfig.Interface { - spec := enableTypeDirective.Spec - kind := apiResource.Kind - pluralName := apiResource.Name - typeConfig := &fedv1b1.FederatedTypeConfig{ - // Explicitly including TypeMeta will ensure it will be - // serialized properly to yaml. - TypeMeta: metav1.TypeMeta{ - Kind: "FederatedTypeConfig", - APIVersion: "core.kubefed.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: typeconfig.GroupQualifiedName(apiResource), - }, - Spec: fedv1b1.FederatedTypeConfigSpec{ - TargetType: fedv1b1.APIResource{ - Version: apiResource.Version, - Kind: kind, - Scope: NamespacedToScope(apiResource), - }, - Propagation: fedv1b1.PropagationEnabled, - FederatedType: fedv1b1.APIResource{ - Group: spec.FederatedGroup, - Version: spec.FederatedVersion, - Kind: fmt.Sprintf("Federated%s", kind), - PluralName: fmt.Sprintf("federated%s", pluralName), - Scope: FederatedNamespacedToScope(apiResource), - }, - }, - } - - // Set defaults that would normally be set by the api - fedv1b1.SetFederatedTypeConfigDefaults(typeConfig) - return typeConfig -} - -func qualifiedAPIResourceName(resource metav1.APIResource) string { - if resource.Group == "" { - return fmt.Sprintf("%s/%s", resource.Name, resource.Version) - } - return fmt.Sprintf("%s.%s/%s", resource.Name, resource.Group, resource.Version) -} - -func federatedTypeCRD(typeConfig typeconfig.Interface, accessor schemaAccessor, shortNames []string) *apiextv1b1.CustomResourceDefinition { - templateSchema := accessor.templateSchema() - schema := federatedTypeValidationSchema(templateSchema) - return CrdForAPIResource(typeConfig.GetFederatedType(), schema, shortNames) -} - -func writeObjectsToYAML(objects []pkgruntime.Object, w io.Writer) error { - for _, obj := range objects { - if _, err := w.Write([]byte("---\n")); err != nil { - return errors.Wrap(err, "Error encoding object to yaml") - } - - if err := writeObjectToYAML(obj, w); err != nil { - return errors.Wrap(err, "Error encoding object to yaml") - } - } - return nil -} - -func writeObjectToYAML(obj pkgruntime.Object, w io.Writer) error { - json, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(obj) - if err != nil { - return err - } - - unstructuredObj := &unstructured.Unstructured{} - if _, _, err := unstructured.UnstructuredJSONScheme.Decode(json, nil, unstructuredObj); err != nil { - return err - } - - return util.WriteUnstructuredToYaml(unstructuredObj, w) -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/schema.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/schema.go deleted file mode 100644 index ec16c208c54196d98f1d187678f127a528b3a580..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/schema.go +++ /dev/null @@ -1,221 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 enable - -import ( - "fmt" - - "github.com/pkg/errors" - - apiextv1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - apiextv1b1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/discovery" - "k8s.io/client-go/rest" - "k8s.io/kube-openapi/pkg/util/proto" - "k8s.io/kubectl/pkg/util/openapi" -) - -type schemaAccessor interface { - templateSchema() map[string]apiextv1b1.JSONSchemaProps -} - -func newSchemaAccessor(config *rest.Config, apiResource metav1.APIResource) (schemaAccessor, error) { - // Assume the resource may be a CRD, and fall back to OpenAPI if that is not the case. - crdAccessor, err := newCRDSchemaAccessor(config, apiResource) - if err != nil { - return nil, err - } - if crdAccessor != nil { - return crdAccessor, nil - } - return newOpenAPISchemaAccessor(config, apiResource) -} - -type crdSchemaAccessor struct { - validation *apiextv1b1.CustomResourceValidation -} - -func newCRDSchemaAccessor(config *rest.Config, apiResource metav1.APIResource) (schemaAccessor, error) { - // CRDs must have a group - if len(apiResource.Group) == 0 { - return nil, nil - } - // Check whether the target resource is a crd - crdClient, err := apiextv1b1client.NewForConfig(config) - if err != nil { - return nil, errors.Wrap(err, "Failed to create crd clientset") - } - crdName := fmt.Sprintf("%s.%s", apiResource.Name, apiResource.Group) - crd, err := crdClient.CustomResourceDefinitions().Get(crdName, metav1.GetOptions{}) - if apierrors.IsNotFound(err) { - return nil, nil - } - if err != nil { - return nil, errors.Wrapf(err, "Error attempting retrieval of crd %q", crdName) - } - return &crdSchemaAccessor{validation: crd.Spec.Validation}, nil -} - -func (a *crdSchemaAccessor) templateSchema() map[string]apiextv1b1.JSONSchemaProps { - if a.validation != nil && a.validation.OpenAPIV3Schema != nil { - return a.validation.OpenAPIV3Schema.Properties - } - return nil -} - -type openAPISchemaAccessor struct { - targetResource proto.Schema -} - -func newOpenAPISchemaAccessor(config *rest.Config, apiResource metav1.APIResource) (schemaAccessor, error) { - client, err := discovery.NewDiscoveryClientForConfig(config) - if err != nil { - return nil, errors.Wrap(err, "Error creating discovery client") - } - resources, err := openapi.NewOpenAPIGetter(client).Get() - if err != nil { - return nil, errors.Wrap(err, "Error loading openapi schema") - } - gvk := schema.GroupVersionKind{ - Group: apiResource.Group, - Version: apiResource.Version, - Kind: apiResource.Kind, - } - targetResource := resources.LookupResource(gvk) - if targetResource == nil { - return nil, errors.Errorf("Unable to find openapi schema for %q", gvk) - } - return &openAPISchemaAccessor{ - targetResource: targetResource, - }, nil -} - -func (a *openAPISchemaAccessor) templateSchema() map[string]apiextv1b1.JSONSchemaProps { - var templateSchema *apiextv1b1.JSONSchemaProps - visitor := &jsonSchemaVistor{ - collect: func(schema apiextv1b1.JSONSchemaProps) { - templateSchema = &schema - }, - } - a.targetResource.Accept(visitor) - - return templateSchema.Properties -} - -// jsonSchemaVistor converts proto.Schema resources into json schema. -// A local visitor (and associated callback) is intended to be created -// whenever a function needs to recurse. -// -// TODO(marun) Generate more extensive schema if/when openapi schema -// provides more detail as per https://github.com/ant31/crd-validation -type jsonSchemaVistor struct { - collect func(schema apiextv1b1.JSONSchemaProps) -} - -func (v *jsonSchemaVistor) VisitArray(a *proto.Array) { - arraySchema := apiextv1b1.JSONSchemaProps{ - Type: "array", - Items: &apiextv1b1.JSONSchemaPropsOrArray{}, - } - localVisitor := &jsonSchemaVistor{ - collect: func(schema apiextv1b1.JSONSchemaProps) { - arraySchema.Items.Schema = &schema - }, - } - a.SubType.Accept(localVisitor) - v.collect(arraySchema) -} - -func (v *jsonSchemaVistor) VisitMap(m *proto.Map) { - mapSchema := apiextv1b1.JSONSchemaProps{ - Type: "object", - AdditionalProperties: &apiextv1b1.JSONSchemaPropsOrBool{ - Allows: true, - }, - } - localVisitor := &jsonSchemaVistor{ - collect: func(schema apiextv1b1.JSONSchemaProps) { - mapSchema.AdditionalProperties.Schema = &schema - }, - } - m.SubType.Accept(localVisitor) - v.collect(mapSchema) -} - -func (v *jsonSchemaVistor) VisitPrimitive(p *proto.Primitive) { - schema := schemaForPrimitive(p) - v.collect(schema) -} - -func (v *jsonSchemaVistor) VisitKind(k *proto.Kind) { - kindSchema := apiextv1b1.JSONSchemaProps{ - Type: "object", - Properties: make(map[string]apiextv1b1.JSONSchemaProps), - Required: k.RequiredFields, - } - for key, fieldSchema := range k.Fields { - // Status cannot be defined for a template - if key == "status" { - continue - } - localVisitor := &jsonSchemaVistor{ - collect: func(schema apiextv1b1.JSONSchemaProps) { - kindSchema.Properties[key] = schema - }, - } - fieldSchema.Accept(localVisitor) - } - v.collect(kindSchema) -} - -func (v *jsonSchemaVistor) VisitReference(r proto.Reference) { - // Short-circuit the recursive definition of JSONSchemaProps (used for CRD validation) - // - // TODO(marun) Implement proper support for recursive schema - if r.Reference() == "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps" { - v.collect(apiextv1b1.JSONSchemaProps{Type: "object"}) - return - } - - r.SubSchema().Accept(v) -} - -func schemaForPrimitive(p *proto.Primitive) apiextv1b1.JSONSchemaProps { - schema := apiextv1b1.JSONSchemaProps{} - - if p.Format == "int-or-string" { - schema.AnyOf = []apiextv1b1.JSONSchemaProps{ - { - Type: "integer", - Format: "int32", - }, - { - Type: "string", - }, - } - return schema - } - - if len(p.Format) > 0 { - schema.Format = p.Format - } - schema.Type = p.Type - return schema -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/util.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/util.go deleted file mode 100644 index 44f596a443c0f4c6d95c9146cdf3df085d6deb09..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/util.go +++ /dev/null @@ -1,199 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 enable - -import ( - "fmt" - "io" - "os" - "strings" - - "github.com/pkg/errors" - - apiextv1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/yaml" - "k8s.io/client-go/discovery" - "k8s.io/client-go/rest" - - "sigs.k8s.io/kubefed/pkg/apis/core/common" - "sigs.k8s.io/kubefed/pkg/apis/core/typeconfig" -) - -func DecodeYAMLFromFile(filename string, obj interface{}) error { - f, err := os.Open(filename) - if err != nil { - return err - } - defer f.Close() - - return DecodeYAML(f, obj) -} - -func DecodeYAML(r io.Reader, obj interface{}) error { - decoder := yaml.NewYAMLToJSONDecoder(r) - return decoder.Decode(obj) -} - -func CrdForAPIResource(apiResource metav1.APIResource, validation *apiextv1b1.CustomResourceValidation, shortNames []string) *apiextv1b1.CustomResourceDefinition { - scope := apiextv1b1.ClusterScoped - if apiResource.Namespaced { - scope = apiextv1b1.NamespaceScoped - } - return &apiextv1b1.CustomResourceDefinition{ - // Explicitly including TypeMeta will ensure it will be - // serialized properly to yaml. - TypeMeta: metav1.TypeMeta{ - Kind: "CustomResourceDefinition", - APIVersion: "apiextensions.k8s.io/v1beta1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: typeconfig.GroupQualifiedName(apiResource), - }, - Spec: apiextv1b1.CustomResourceDefinitionSpec{ - Group: apiResource.Group, - Version: apiResource.Version, - Scope: scope, - Names: apiextv1b1.CustomResourceDefinitionNames{ - Plural: apiResource.Name, - Kind: apiResource.Kind, - ShortNames: shortNames, - }, - Validation: validation, - Subresources: &apiextv1b1.CustomResourceSubresources{ - Status: &apiextv1b1.CustomResourceSubresourceStatus{}, - }, - }, - } -} - -func LookupAPIResource(config *rest.Config, key, targetVersion string) (*metav1.APIResource, error) { - resourceLists, err := GetServerPreferredResources(config) - if err != nil { - return nil, err - } - - var targetResource *metav1.APIResource - var matchedResources []string - for _, resourceList := range resourceLists { - // The list holds the GroupVersion for its list of APIResources - gv, err := schema.ParseGroupVersion(resourceList.GroupVersion) - if err != nil { - return nil, errors.Wrap(err, "Error parsing GroupVersion") - } - if len(targetVersion) > 0 && gv.Version != targetVersion { - continue - } - for _, resource := range resourceList.APIResources { - group := gv.Group - if NameMatchesResource(key, resource, group) { - if targetResource == nil { - targetResource = resource.DeepCopy() - targetResource.Group = group - targetResource.Version = gv.Version - } - matchedResources = append(matchedResources, groupQualifiedName(resource.Name, gv.Group)) - } - } - - } - if len(matchedResources) > 1 { - return nil, errors.Errorf("Multiple resources are matched by %q: %s. A group-qualified plural name must be provided.", key, strings.Join(matchedResources, ", ")) - } - - if targetResource != nil { - return targetResource, nil - } - - return nil, errors.Errorf("Unable to find api resource named %q.", key) -} - -func NameMatchesResource(name string, apiResource metav1.APIResource, group string) bool { - lowerCaseName := strings.ToLower(name) - if lowerCaseName == apiResource.Name || - lowerCaseName == apiResource.SingularName || - lowerCaseName == strings.ToLower(apiResource.Kind) || - lowerCaseName == fmt.Sprintf("%s.%s", apiResource.Name, group) { - return true - } - for _, shortName := range apiResource.ShortNames { - if lowerCaseName == strings.ToLower(shortName) { - return true - } - } - - return false -} - -func GetServerPreferredResources(config *rest.Config) ([]*metav1.APIResourceList, error) { - // TODO(marun) Consider using a caching scheme ala kubectl - client, err := discovery.NewDiscoveryClientForConfig(config) - if err != nil { - return nil, errors.Wrap(err, "Error creating discovery client") - } - - resourceLists, err := client.ServerPreferredResources() - if err != nil { - return nil, errors.Wrap(err, "Error listing api resources") - } - return resourceLists, nil -} - -func NamespacedToScope(apiResource metav1.APIResource) apiextv1b1.ResourceScope { - if apiResource.Namespaced { - return apiextv1b1.NamespaceScoped - } - return apiextv1b1.ClusterScoped -} - -func FederatedNamespacedToScope(apiResource metav1.APIResource) apiextv1b1.ResourceScope { - // Special-case the scope of federated namespace since it will - // hopefully be the only instance of the scope of a federated - // type differing from the scope of its target. - if typeconfig.GroupQualifiedName(apiResource) == common.NamespaceName { - // FederatedNamespace is namespaced to allow the control plane to run - // with only namespace-scoped permissions e.g. to determine placement. - return apiextv1b1.NamespaceScoped - } - return NamespacedToScope(apiResource) -} - -func resourceKey(apiResource metav1.APIResource) string { - var group string - if len(apiResource.Group) == 0 { - group = "core" - } else { - group = apiResource.Group - } - var version string - if len(apiResource.Version) == 0 { - version = "v1" - } else { - version = apiResource.Version - } - return fmt.Sprintf("%s.%s/%s", apiResource.Name, group, version) -} - -func groupQualifiedName(name, group string) string { - apiResource := metav1.APIResource{ - Name: name, - Group: group, - } - - return typeconfig.GroupQualifiedName(apiResource) -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/validation.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/validation.go deleted file mode 100644 index 7314904cba4a4ec470ecf66ff8b327259afc2a4e..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/enable/validation.go +++ /dev/null @@ -1,260 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 enable - -import ( - v1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - - "sigs.k8s.io/kubefed/pkg/controller/util" -) - -func federatedTypeValidationSchema(templateSchema map[string]v1beta1.JSONSchemaProps) *v1beta1.CustomResourceValidation { - schema := ValidationSchema(v1beta1.JSONSchemaProps{ - Type: "object", - Properties: map[string]v1beta1.JSONSchemaProps{ - "placement": { - Type: "object", - Properties: map[string]v1beta1.JSONSchemaProps{ - // References to one or more clusters allow a - // scheduling mechanism to explicitly indicate - // placement. If one or more clusters is provided, - // the clusterSelector field will be ignored. - "clusters": { - Type: "array", - Items: &v1beta1.JSONSchemaPropsOrArray{ - Schema: &v1beta1.JSONSchemaProps{ - Type: "object", - Properties: map[string]v1beta1.JSONSchemaProps{ - "name": { - Type: "string", - }, - }, - Required: []string{ - "name", - }, - }, - }, - }, - "clusterSelector": { - Type: "object", - Properties: map[string]v1beta1.JSONSchemaProps{ - "matchExpressions": { - Type: "array", - Items: &v1beta1.JSONSchemaPropsOrArray{ - Schema: &v1beta1.JSONSchemaProps{ - Type: "object", - Properties: map[string]v1beta1.JSONSchemaProps{ - "key": { - Type: "string", - }, - "operator": { - Type: "string", - }, - "values": { - Type: "array", - Items: &v1beta1.JSONSchemaPropsOrArray{ - Schema: &v1beta1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - }, - Required: []string{ - "key", - "operator", - }, - }, - }, - }, - "matchLabels": { - Type: "object", - AdditionalProperties: &v1beta1.JSONSchemaPropsOrBool{ - Schema: &v1beta1.JSONSchemaProps{ - Type: "string", - }, - }, - }, - }, - }, - }, - }, - "overrides": { - Type: "array", - Items: &v1beta1.JSONSchemaPropsOrArray{ - Schema: &v1beta1.JSONSchemaProps{ - Type: "object", - Properties: map[string]v1beta1.JSONSchemaProps{ - "clusterName": { - Type: "string", - }, - "clusterOverrides": { - Type: "array", - Items: &v1beta1.JSONSchemaPropsOrArray{ - Schema: &v1beta1.JSONSchemaProps{ - Type: "object", - Properties: map[string]v1beta1.JSONSchemaProps{ - "op": { - Type: "string", - Pattern: "^(add|remove|replace)?$", - }, - "path": { - Type: "string", - }, - "value": { - // Supporting the override of an arbitrary field - // precludes up-front validation. Errors in - // the definition of override values will need to - // be caught during propagation. - AnyOf: []v1beta1.JSONSchemaProps{ - { - Type: "string", - }, - { - Type: "integer", - }, - { - Type: "boolean", - }, - { - Type: "object", - }, - { - Type: "array", - }, - }, - }, - }, - Required: []string{ - "path", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }) - if templateSchema != nil { - specProperties := schema.OpenAPIV3Schema.Properties["spec"].Properties - specProperties["template"] = v1beta1.JSONSchemaProps{ - Type: "object", - } - // Add retainReplicas field to types that exposes a replicas - // field that could be targeted by HPA. - if templateSpec, ok := templateSchema["spec"]; ok { - // TODO: find a simpler way to detect that a resource is scalable than having to compute the entire schema. - if replicasField, ok := templateSpec.Properties["replicas"]; ok { - if replicasField.Type == "integer" && replicasField.Format == "int32" { - specProperties[util.RetainReplicasField] = v1beta1.JSONSchemaProps{ - Type: "boolean", - } - } - } - } - - } - return schema -} - -func ValidationSchema(specProps v1beta1.JSONSchemaProps) *v1beta1.CustomResourceValidation { - return &v1beta1.CustomResourceValidation{ - OpenAPIV3Schema: &v1beta1.JSONSchemaProps{ - Properties: map[string]v1beta1.JSONSchemaProps{ - "apiVersion": { - Type: "string", - }, - "kind": { - Type: "string", - }, - // TODO(marun) Add a comprehensive schema for metadata - "metadata": { - Type: "object", - }, - "spec": specProps, - "status": { - Type: "object", - Properties: map[string]v1beta1.JSONSchemaProps{ - "conditions": { - Type: "array", - Items: &v1beta1.JSONSchemaPropsOrArray{ - Schema: &v1beta1.JSONSchemaProps{ - Type: "object", - Properties: map[string]v1beta1.JSONSchemaProps{ - "type": { - Type: "string", - }, - "status": { - Type: "string", - }, - "reason": { - Type: "string", - }, - "lastUpdateTime": { - Format: "date-time", - Type: "string", - }, - "lastTransitionTime": { - Format: "date-time", - Type: "string", - }, - }, - Required: []string{ - "type", - "status", - }, - }, - }, - }, - "clusters": { - Type: "array", - Items: &v1beta1.JSONSchemaPropsOrArray{ - Schema: &v1beta1.JSONSchemaProps{ - Type: "object", - Properties: map[string]v1beta1.JSONSchemaProps{ - "name": { - Type: "string", - }, - "status": { - Type: "string", - }, - }, - Required: []string{ - "name", - }, - }, - }, - }, - "observedGeneration": { - Format: "int64", - Type: "integer", - }, - }, - }, - }, - // Require a spec (even if empty) as an aid to users - // manually creating federated configmaps or - // secrets. These target types do not include a spec, - // and the absence of the spec in a federated - // equivalent could indicate a malformed resource. - Required: []string{ - "spec", - }, - }, - } -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/federate/federate.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/federate/federate.go deleted file mode 100644 index df90977140f9a0e17c492c70a4417cfb4941644b..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/federate/federate.go +++ /dev/null @@ -1,552 +0,0 @@ -/* -Copyright 2019 The Kubernetes 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 federate - -import ( - "context" - "io" - "time" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - apimeta "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/rest" - "k8s.io/klog" - - "sigs.k8s.io/kubefed/pkg/apis/core/typeconfig" - fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" - genericclient "sigs.k8s.io/kubefed/pkg/client/generic" - ctlutil "sigs.k8s.io/kubefed/pkg/controller/util" - "sigs.k8s.io/kubefed/pkg/kubefedctl/enable" - "sigs.k8s.io/kubefed/pkg/kubefedctl/options" - "sigs.k8s.io/kubefed/pkg/kubefedctl/util" -) - -const ( - createResourceRetryTimeout = 10 * time.Second - createResourceRetryInterval = 1 * time.Second -) - -var ( - // Controller created resources should always be skipped while federating content - controllerCreatedAPIResourceNames = []string{ - "endpoints", - "events", - "events.events.k8s.io", - "propagatedversions.core.kubefed.io", - } - - federate_long = ` - Federate creates a federated resource from a kubernetes resource. - The target resource must exist in the cluster hosting the kubefed - control plane. If the federated resource needs to be created in the - API, the control plane must have a FederatedTypeConfig for the type - of the kubernetes resource. If using with flag '-o yaml', it is not - necessary for the FederatedTypeConfig to exist (or even for the - kubefed API to be installed in the cluster). - - Current context is assumed to be a Kubernetes cluster hosting - the kubefed control plane. Please use the --host-cluster-context - flag otherwise.` - - federate_example = ` - # Federate resource named "my-cm" in namespace "my-ns" of kubernetes type "configmaps" (identified by short name "cm") - kubefedctl federate cm "my-cm" -n "my-ns" --host-cluster-context=cluster1` -) - -type federateResource struct { - options.GlobalSubcommandOptions - typeName string - resourceName string - resourceNamespace string - output string - outputYAML bool - enableType bool - federateContents bool - filename string - skipAPIResourceNames []string -} - -func (j *federateResource) Bind(flags *pflag.FlagSet) { - flags.StringVarP(&j.resourceNamespace, "namespace", "n", "", "The namespace of the resource to federate.") - flags.StringVarP(&j.output, "output", "o", "", "If provided, the resource that would be created in the API by the command is instead output to stdout in the provided format. Valid format is ['yaml'].") - flags.BoolVarP(&j.enableType, "enable-type", "t", false, "If true, attempt to enable federation of the API type of the resource before creating the federated resource.") - flags.BoolVarP(&j.federateContents, "contents", "c", false, "Applicable only to namespaces. If provided, the command will federate all resources within the namespace after federating the namespace.") - flags.StringVarP(&j.filename, "filename", "f", "", "If specified, the provided yaml file will be used as the input for target resources to federate. This mode will only emit federated resource yaml to standard output. Other flag options if provided will be ignored.") - flags.StringSliceVarP(&j.skipAPIResourceNames, "skip-api-resources", "s", []string{}, "Comma separated names of the api resources to skip when federating contents in a namespace. Name could be short name "+ - "(e.g. 'deploy), kind (e.g. 'deployment'), plural name (e.g. 'deployments'), group qualified plural name (e.g. 'deployments.apps') or group name itself (e.g. 'apps') to skip the whole group.") -} - -// Complete ensures that options are valid. -func (j *federateResource) Complete(args []string) error { - if j.output == "yaml" { - j.outputYAML = true - } else if len(j.output) > 0 { - return errors.Errorf("Invalid value for --output: %s", j.output) - } - - if len(j.filename) > 0 { - if len(args) > 0 { - return errors.Errorf("Flag '--filename' does not take any args. Got args: %v", args) - } - return nil - } - - if len(args) == 0 { - return errors.New("TYPE-NAME is required") - } - j.typeName = args[0] - - if len(args) == 1 { - return errors.New("RESOURCE-NAME is required") - } - j.resourceName = args[1] - - if j.enableType && j.outputYAML { - return errors.New("Flag '--enable-type' cannot be used with '--output [yaml]'") - } - - return nil -} - -// NewCmdFederateResource defines the `federate` command that federates a -// Kubernetes resource of the given kubernetes type. -func NewCmdFederateResource(cmdOut io.Writer, config util.FedConfig) *cobra.Command { - opts := &federateResource{} - - cmd := &cobra.Command{ - Use: "federate TYPE-NAME RESOURCE-NAME", - Short: "Federate creates a federated resource from a kubernetes resource", - Long: federate_long, - Example: federate_example, - Run: func(cmd *cobra.Command, args []string) { - err := opts.Complete(args) - if err != nil { - klog.Fatalf("Error: %v", err) - } - - err = opts.Run(cmdOut, config) - if err != nil { - klog.Fatalf("Error: %v", err) - } - }, - } - - flags := cmd.Flags() - opts.GlobalSubcommandBind(flags) - opts.Bind(flags) - - return cmd -} - -// Run is the implementation of the `federate resource` command. -func (j *federateResource) Run(cmdOut io.Writer, config util.FedConfig) error { - - if len(j.resourceNamespace) == 0 { - var err error - j.resourceNamespace, err = util.GetNamespace(j.HostClusterContext, j.Kubeconfig, config) - if err != nil { - return err - } - } - - hostConfig, err := config.HostConfig(j.HostClusterContext, j.Kubeconfig) - if err != nil { - return errors.Wrap(err, "Failed to get host cluster config") - } - - if len(j.filename) > 0 { - resources, err := DecodeUnstructuredFromFile(j.filename) - if err != nil { - return errors.Wrapf(err, "Failed to load yaml from file %q", j.filename) - } - federatedResources, err := FederateResources(resources) - if err != nil { - return err - } - - err = WriteUnstructuredObjsToYaml(federatedResources, cmdOut) - if err != nil { - return errors.Wrap(err, "Failed to write federated resources to YAML") - } - return nil - - } - - qualifiedResourceName := ctlutil.QualifiedName{ - Namespace: j.resourceNamespace, - Name: j.resourceName, - } - artifacts, err := GetFederateArtifacts(hostConfig, j.typeName, j.KubeFedNamespace, qualifiedResourceName, j.enableType, j.outputYAML) - if err != nil { - return err - } - artifactsList := []*FederateArtifacts{} - artifactsList = append(artifactsList, artifacts) - - kind := artifacts.typeConfig.GetTargetType().Kind - if kind != ctlutil.NamespaceKind && j.federateContents { - return errors.New("Flag '--contents' can only be used with type 'namespaces'.") - } - - if kind == ctlutil.NamespaceKind && j.federateContents { - containedArtifactsList, err := GetContainedArtifactsList(hostConfig, j.resourceName, j.KubeFedNamespace, j.skipAPIResourceNames, j.enableType, j.outputYAML) - if err != nil { - return err - } - artifactsList = append(artifactsList, containedArtifactsList...) - } - - if j.outputYAML { - for _, artifacts := range artifactsList { - err := WriteUnstructuredObjsToYaml(artifacts.federatedResources, cmdOut) - if err != nil { - return errors.Wrap(err, "Failed to write federated resource to YAML") - } - } - return nil - } - - return CreateResources(cmdOut, hostConfig, artifactsList, j.KubeFedNamespace, j.enableType, j.DryRun) -} - -func FederateResources(resources []*unstructured.Unstructured) ([]*unstructured.Unstructured, error) { - var federatedResources []*unstructured.Unstructured - for _, targetResource := range resources { - // A Group, a Version and a Kind is sufficient for API Resource definition. - gvk := targetResource.GroupVersionKind() - - // Construct an API Resource from above info. - // TODO(irfanurrehman) Should we depend on the lookup from the - // API Server instead, for some specific scenario? - plural, singular := apimeta.UnsafeGuessKindToResource(gvk) - apiResource := metav1.APIResource{ - Name: plural.Resource, - SingularName: singular.Resource, - Group: gvk.Group, - Version: gvk.Version, - Kind: gvk.Kind, - } - apiResource.Namespaced = targetResource.GetNamespace() == "" - - qualifiedName := ctlutil.NewQualifiedName(targetResource) - typeConfig := enable.GenerateTypeConfigForTarget(apiResource, enable.NewEnableTypeDirective()) - federatedResource, err := FederatedResourceFromTargetResource(typeConfig, targetResource) - if err != nil { - return nil, errors.Wrapf(err, "Error getting %s from %s %q", typeConfig.GetFederatedType().Kind, typeConfig.GetTargetType().Kind, qualifiedName) - } - - federatedResources = append(federatedResources, federatedResource) - } - - return federatedResources, nil -} - -type FederateArtifacts struct { - // Identifies if typeConfig for this type is installed - typeConfigInstalled bool - - // Identifies the type - typeConfig typeconfig.Interface - // List of federated resources of this type - federatedResources []*unstructured.Unstructured -} - -func GetFederateArtifacts(hostConfig *rest.Config, typeName, kubefedNamespace string, qualifiedName ctlutil.QualifiedName, enableType, outputYAML bool) (*FederateArtifacts, error) { - // Lookup kubernetes API availability - apiResource, err := enable.LookupAPIResource(hostConfig, typeName, "") - if err != nil { - return nil, errors.Wrapf(err, "Failed to find target API resource %s", typeName) - } - klog.V(2).Infof("API Resource for %s found", typeName) - - typeConfigInstalled, typeConfig, err := getTypeConfig(hostConfig, *apiResource, kubefedNamespace, enableType, outputYAML) - if err != nil { - return nil, err - } - - targetResource, err := getTargetResource(hostConfig, typeConfig, qualifiedName) - if err != nil { - return nil, err - } - - federatedResource, err := FederatedResourceFromTargetResource(typeConfig, targetResource) - if err != nil { - return nil, errors.Wrapf(err, "Error getting %s from %s %q", typeConfig.GetFederatedType().Kind, typeConfig.GetTargetType().Kind, qualifiedName) - } - - var federatedResources []*unstructured.Unstructured - federatedResources = append(federatedResources, federatedResource) - return &FederateArtifacts{ - typeConfigInstalled: typeConfigInstalled, - typeConfig: typeConfig, - federatedResources: federatedResources, - }, nil -} - -func getTypeConfig(hostConfig *rest.Config, apiResource metav1.APIResource, kubefedNamespace string, enableType, outputYAML bool) (bool, typeconfig.Interface, error) { - resolvedTypeName := typeconfig.GroupQualifiedName(apiResource) - installedTypeConfig, err := getInstalledTypeConfig(hostConfig, resolvedTypeName, kubefedNamespace) - if err == nil { - return true, installedTypeConfig, nil - } - notFound := apierrors.IsNotFound(err) - if notFound && !outputYAML && !enableType { - return false, nil, errors.Errorf("%v. Consider using '--enable-type' to optionally enable type while federating the resource", err) - } - - generatedTypeConfig := enable.GenerateTypeConfigForTarget(apiResource, enable.NewEnableTypeDirective()) - if notFound && enableType { // We have already generated typeConfig to additionally enable type - return false, generatedTypeConfig, nil - } - if outputYAML { // Output as yaml does not bother what error happened while accessing typeConfig - klog.V(1).Infof("Falling back to a generated type config due to lookup failure: %v", err) - return false, generatedTypeConfig, nil - } - return false, nil, err -} - -func getInstalledTypeConfig(hostConfig *rest.Config, typeName, kubefedNamespace string) (typeconfig.Interface, error) { - client, err := genericclient.New(hostConfig) - if err != nil { - return nil, errors.Wrap(err, "Failed to get generic client") - } - - concreteTypeConfig := &fedv1b1.FederatedTypeConfig{} - err = client.Get(context.TODO(), concreteTypeConfig, kubefedNamespace, typeName) - if err != nil { - return nil, err - } - return concreteTypeConfig, nil -} - -func getTargetResource(hostConfig *rest.Config, typeConfig typeconfig.Interface, qualifiedName ctlutil.QualifiedName) (*unstructured.Unstructured, error) { - targetAPIResource := typeConfig.GetTargetType() - targetClient, err := ctlutil.NewResourceClient(hostConfig, &targetAPIResource) - if err != nil { - return nil, errors.Wrapf(err, "Error creating client for %s", targetAPIResource.Kind) - } - - kind := targetAPIResource.Kind - resource, err := targetClient.Resources(qualifiedName.Namespace).Get(qualifiedName.Name, metav1.GetOptions{}) - if err != nil { - return nil, errors.Wrapf(err, "Error retrieving target %s %q", kind, qualifiedName) - } - - klog.V(2).Infof("Target %s %q found", kind, qualifiedName) - return resource, nil -} - -func FederatedResourceFromTargetResource(typeConfig typeconfig.Interface, resource *unstructured.Unstructured) (*unstructured.Unstructured, error) { - fedAPIResource := typeConfig.GetFederatedType() - targetResource := resource.DeepCopy() - - targetKind := typeConfig.GetTargetType().Kind - - // Special handling is needed for some controller set fields. - switch targetKind { - case ctlutil.NamespaceKind: - { - unstructured.RemoveNestedField(targetResource.Object, "spec", "finalizers") - } - case ctlutil.ServiceAccountKind: - { - unstructured.RemoveNestedField(targetResource.Object, ctlutil.SecretsField) - } - case ctlutil.ServiceKind: - { - var targetPorts []interface{} - targetPorts, ok, err := unstructured.NestedSlice(targetResource.Object, "spec", "ports") - if err != nil { - return nil, err - } - if ok { - for index := range targetPorts { - port := targetPorts[index].(map[string]interface{}) - delete(port, "nodePort") - targetPorts[index] = port - } - err := unstructured.SetNestedSlice(targetResource.Object, targetPorts, "spec", "ports") - if err != nil { - return nil, err - } - } - unstructured.RemoveNestedField(targetResource.Object, "spec", "clusterIP") - } - } - - qualifiedName := ctlutil.NewQualifiedName(targetResource) - resourceNamespace := getNamespace(typeConfig, qualifiedName) - fedResource := &unstructured.Unstructured{} - SetBasicMetaFields(fedResource, fedAPIResource, qualifiedName.Name, resourceNamespace, "") - - // Warn if annotations are present in case the intention is to - // define annotations in the template of the federated resource. - annotations, _, err := unstructured.NestedMap(targetResource.Object, "metadata", "annotations") - if err != nil { - return nil, errors.Wrap(err, "Failed to retrieve metadata.annotations") - } - if len(annotations) > 0 { - klog.Warningf("Annotations defined for %s %q will not appear in the template of the federated resource: %v", targetKind, qualifiedName, annotations) - } - - if err := RemoveUnwantedFields(targetResource); err != nil { - return nil, err - } - - err = unstructured.SetNestedField(fedResource.Object, targetResource.Object, ctlutil.SpecField, ctlutil.TemplateField) - if err != nil { - return nil, err - } - err = unstructured.SetNestedStringMap(fedResource.Object, map[string]string{}, ctlutil.SpecField, ctlutil.PlacementField, ctlutil.ClusterSelectorField, ctlutil.MatchLabelsField) - if err != nil { - return nil, err - } - - return fedResource, err -} - -func getNamespace(typeConfig typeconfig.Interface, qualifiedName ctlutil.QualifiedName) string { - if typeConfig.GetTargetType().Kind == ctlutil.NamespaceKind { - return qualifiedName.Name - } - return qualifiedName.Namespace -} - -func CreateResources(cmdOut io.Writer, hostConfig *rest.Config, artifactsList []*FederateArtifacts, namespace string, enableType, dryRun bool) error { - for _, artifacts := range artifactsList { - if enableType && !artifacts.typeConfigInstalled { - enableTypeDirective := enable.NewEnableTypeDirective() - enableTypeDirective.Name = artifacts.typeConfig.GetObjectMeta().Name - typeResources, err := enable.GetResources(hostConfig, enableTypeDirective) - if err != nil { - return err - } - err = enable.CreateResources(cmdOut, hostConfig, typeResources, namespace, dryRun) - if err != nil { - return err - } - } - - err := CreateFederatedResources(hostConfig, artifacts.typeConfig, artifacts.federatedResources, dryRun) - if err != nil { - return err - } - } - - return nil -} - -func CreateFederatedResources(hostConfig *rest.Config, typeConfig typeconfig.Interface, federatedResources []*unstructured.Unstructured, dryRun bool) error { - for _, federatedResource := range federatedResources { - err := CreateFederatedResource(hostConfig, typeConfig, federatedResource, dryRun) - if err != nil { - return err - } - } - - return nil -} - -func CreateFederatedResource(hostConfig *rest.Config, typeConfig typeconfig.Interface, federatedResource *unstructured.Unstructured, dryRun bool) error { - if typeConfig.GetTargetType().Kind == ctlutil.NamespaceKind { - // TODO: irfanurrehman: Can a target namespace be federated into another namespace? - klog.Infof("Resource to federate is a namespace. Given namespace will itself be the container for the federated namespace") - } - - fedAPIResource := typeConfig.GetFederatedType() - fedKind := fedAPIResource.Kind - fedClient, err := ctlutil.NewResourceClient(hostConfig, &fedAPIResource) - if err != nil { - return errors.Wrapf(err, "Error creating client for %s", fedKind) - } - - qualifiedFedName := ctlutil.NewQualifiedName(federatedResource) - if !dryRun { - // It might take a little while for the federated type to appear if the - // same is being enabled while or immediately before federating the resource. - err = wait.PollImmediate(createResourceRetryInterval, createResourceRetryTimeout, func() (bool, error) { - _, err := fedClient.Resources(federatedResource.GetNamespace()).Create(federatedResource, metav1.CreateOptions{}) - if apierrors.IsNotFound(err) { - return false, nil - } - if err != nil { - return false, err - } - return true, nil - }) - if err != nil { - return errors.Wrapf(err, "Error creating federated resource %q", qualifiedFedName) - } - } - - klog.Infof("Successfully created %s %q from %s", fedKind, qualifiedFedName, typeConfig.GetTargetType().Kind) - return nil -} - -func GetContainedArtifactsList(hostConfig *rest.Config, containerNamespace, kubefedNamespace string, skipAPIResourceNames []string, enableType, outputYAML bool) ([]*FederateArtifacts, error) { - targetResourcesList, err := getResourcesInNamespace(hostConfig, containerNamespace, skipAPIResourceNames) - if err != nil { - return nil, err - } - - artifactsList := []*FederateArtifacts{} - for _, targetResources := range targetResourcesList { - apiResource := targetResources.apiResource - typeConfigInstalled, typeConfig, err := getTypeConfig(hostConfig, apiResource, kubefedNamespace, enableType, outputYAML) - if err != nil { - return nil, err - } - var federatedResources []*unstructured.Unstructured - for _, targetResource := range targetResources.resources { - federatedResource, err := FederatedResourceFromTargetResource(typeConfig, targetResource) - if err != nil { - return nil, err - } - - federatedResources = append(federatedResources, federatedResource) - } - federateArtifacts := FederateArtifacts{ - typeConfigInstalled: typeConfigInstalled, - typeConfig: typeConfig, - federatedResources: federatedResources, - } - artifactsList = append(artifactsList, &federateArtifacts) - } - - return artifactsList, nil -} - -func WriteUnstructuredObjsToYaml(unstructuredObjs []*unstructured.Unstructured, w io.Writer) error { - for _, unstructuredObj := range unstructuredObjs { - if _, err := w.Write([]byte("---\n")); err != nil { - return errors.Wrap(err, "Error encoding object to yaml") - } - err := util.WriteUnstructuredToYaml(unstructuredObj, w) - if err != nil { - return err - } - } - - return nil -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/federate/util.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/federate/util.go deleted file mode 100644 index d1518552e7f19e0d99e37f5c0f816fa92e83b8be..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/federate/util.go +++ /dev/null @@ -1,264 +0,0 @@ -/* -Copyright 2019 The Kubernetes 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 federate - -import ( - "bufio" - "io" - "os" - - "github.com/pkg/errors" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/validation" - utilyaml "k8s.io/apimachinery/pkg/util/yaml" - versionhelper "k8s.io/apimachinery/pkg/version" - "k8s.io/client-go/rest" - "k8s.io/klog" - "sigs.k8s.io/yaml" - - "sigs.k8s.io/kubefed/pkg/apis/core/typeconfig" - ctlutil "sigs.k8s.io/kubefed/pkg/controller/util" - "sigs.k8s.io/kubefed/pkg/kubefedctl/enable" - "sigs.k8s.io/kubefed/pkg/kubefedctl/util" -) - -func RemoveUnwantedFields(resource *unstructured.Unstructured) error { - unstructured.RemoveNestedField(resource.Object, "apiVersion") - unstructured.RemoveNestedField(resource.Object, "kind") - unstructured.RemoveNestedField(resource.Object, "status") - - // All metadata fields save labels should be cleared. Other - // metadata fields will be set by the system on creation or - // subsequently by controllers. - labels, _, err := unstructured.NestedMap(resource.Object, "metadata", "labels") - if err != nil { - return errors.Wrap(err, "Failed to retrieve metadata.labels") - } - unstructured.RemoveNestedField(resource.Object, "metadata") - if len(labels) > 0 { - err := unstructured.SetNestedMap(resource.Object, labels, "metadata", "labels") - if err != nil { - return errors.Wrap(err, "Failed to set metadata.labels") - } - } - - return nil -} - -func SetBasicMetaFields(resource *unstructured.Unstructured, apiResource metav1.APIResource, name, namespace, generateName string) { - resource.SetKind(apiResource.Kind) - gv := schema.GroupVersion{Group: apiResource.Group, Version: apiResource.Version} - resource.SetAPIVersion(gv.String()) - resource.SetName(name) - if generateName != "" { - resource.SetGenerateName(generateName) - } - if apiResource.Namespaced { - resource.SetNamespace(namespace) - } -} - -func namespacedAPIResourceMap(config *rest.Config, skipAPIResourceNames []string) (map[string]metav1.APIResource, error) { - apiResourceLists, err := enable.GetServerPreferredResources(config) - if err != nil { - return nil, err - } - - apiResources := make(map[string]metav1.APIResource) - for _, apiResourceList := range apiResourceLists { - if len(apiResourceList.APIResources) == 0 { - continue - } - - gv, err := schema.ParseGroupVersion(apiResourceList.GroupVersion) - if err != nil { - return nil, errors.Wrap(err, "Error parsing GroupVersion") - } - - group := gv.Group - if apiResourceGroupMatchesSkipName(skipAPIResourceNames, group) { - // A whole group is skipped by the user - continue - } - - if group == "extensions" { - // The strategy involved to choose a Group higher in order for k8s core - // APIs is to consider "extensions" as the outdated group [This seems to - // be true for all k8s APIResources, so far]. For example if "deployments" - // exists in "extensions" and "apps"; "deployments.apps" will be chosen. - // This doesn't apply to events but events are listed in - // controllerCreatedAPIResourceNames and so are skipped always. - - // Skipping this also assumes that "extensions" is not the only - // group exposed for this resource on the API Server, which probably - // is safe as "extensions" is deprecated. - // TODO(irfanurrehman): Document this. - continue - } - - for _, apiResource := range apiResourceList.APIResources { - if !apiResource.Namespaced || util.IsFederatedAPIResource(apiResource.Kind, group) || - apiResourceMatchesSkipName(apiResource, skipAPIResourceNames, group) { - continue - } - - // For all other resources (say CRDs) same kinds in different groups - // are treated as individual types. If there happens to be an API Resource - // which enables conversion and allows query of the same resource across - // different groups, a specific group resource will have to be chosen by - // the user using --skip-names to skip the not chosen one(s). - // TODO(irfanurrehman): Document this. - - // The individual apiResources do not have the group and version set - apiResource.Group = group - apiResource.Version = gv.Version - groupQualifiedName := typeconfig.GroupQualifiedName(apiResource) - if previousAPIResource, ok := apiResources[groupQualifiedName]; ok { - if versionhelper.CompareKubeAwareVersionStrings(gv.Version, previousAPIResource.Version) <= 0 { - // The newer version is not latest keep the previous. - continue - } - } - - apiResources[groupQualifiedName] = apiResource - } - } - - return apiResources, nil -} - -func apiResourceGroupMatchesSkipName(skipAPIResourceNames []string, group string) bool { - for _, name := range skipAPIResourceNames { - if name == "" { - continue - } - if name == group { - return true - } - } - return false -} - -func apiResourceMatchesSkipName(apiResource metav1.APIResource, skipAPIResourceNames []string, group string) bool { - names := append(controllerCreatedAPIResourceNames, skipAPIResourceNames...) - for _, name := range names { - if name == "" { - continue - } - if enable.NameMatchesResource(name, apiResource, group) { - return true - } - } - return false -} - -// resources stores a list of resources for an api type -type resources struct { - // resource type information - apiResource metav1.APIResource - // resource list - resources []*unstructured.Unstructured -} - -func getResourcesInNamespace(config *rest.Config, namespace string, skipAPIResourceNames []string) ([]resources, error) { - apiResources, err := namespacedAPIResourceMap(config, skipAPIResourceNames) - if err != nil { - return nil, err - } - - resourcesInNamespace := []resources{} - for _, apiResource := range apiResources { - client, err := ctlutil.NewResourceClient(config, &apiResource) - if err != nil { - return nil, errors.Wrapf(err, "Error creating client for %s", apiResource.Kind) - } - - resourceList, err := client.Resources(namespace).List(metav1.ListOptions{}) - if apierrors.IsNotFound(err) || resourceList == nil { - continue - } - if err != nil { - return nil, errors.Wrapf(err, "Error listing resources for %s", apiResource.Kind) - } - - // It would be a waste of cycles to iterate through empty slices while federating resource - if len(resourceList.Items) == 0 { - continue - } - - targetResources := resources{apiResource: apiResource} - for _, item := range resourceList.Items { - resource := item - errors := validation.IsDNS1123Subdomain(resource.GetName()) - if len(errors) == 0 { - targetResources.resources = append(targetResources.resources, &resource) - } else { - klog.Warningf("Skipping resource %s of type %s because it does not conform to the DNS-1123 subdomain spec.", resource.GetName(), apiResource.Name) - klog.Warningf("The following error(s) were reported during DNS-1123 validation: ") - for _, err := range errors { - klog.Warningf(err) - } - } - } - resourcesInNamespace = append(resourcesInNamespace, targetResources) - } - - return resourcesInNamespace, nil -} - -// decodeUnstructuredFromFile reads a list of yamls into a slice of unstructured objects -func DecodeUnstructuredFromFile(filename string) ([]*unstructured.Unstructured, error) { - var f *os.File - if filename == "-" { - f = os.Stdin - } else { - var err error - f, err = os.Open(filename) - - if err != nil { - return nil, err - } - } - defer f.Close() - - var unstructuredList []*unstructured.Unstructured - reader := utilyaml.NewYAMLReader(bufio.NewReader(f)) - for { - unstructuedObj := &unstructured.Unstructured{} - // Read one YAML document at a time, until io.EOF is returned - buf, err := reader.Read() - if err == io.EOF { - break - } else if err != nil { - return nil, err - } - if len(buf) == 0 { - break - } - if err := yaml.Unmarshal(buf, unstructuedObj); err != nil { - return nil, err - } - - unstructuredList = append(unstructuredList, unstructuedObj) - } - - return unstructuredList, nil -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/join.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/join.go deleted file mode 100644 index 27d0e60e1f70bedeea079dddd2ed7d52d33770ad..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/join.go +++ /dev/null @@ -1,880 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 kubefedctl - -import ( - "context" - goerrors "errors" - "io" - "reflect" - "strings" - "time" - - "github.com/pkg/errors" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" - apiextv1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" - kubeclient "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/klog" - - fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" - genericclient "sigs.k8s.io/kubefed/pkg/client/generic" - ctlutil "sigs.k8s.io/kubefed/pkg/controller/util" - "sigs.k8s.io/kubefed/pkg/kubefedctl/options" - "sigs.k8s.io/kubefed/pkg/kubefedctl/util" -) - -const ( - serviceAccountSecretTimeout = 30 * time.Second -) - -var ( - join_long = ` - Join registers a Kubernetes cluster with a KubeFed control - plane. - - Current context is assumed to be a Kubernetes cluster - hosting a KubeFed control plane. Please use the - --host-cluster-context flag otherwise.` - join_example = ` - # Register a cluster with a KubeFed control plane by - # specifying the cluster name and the context name of - # the control plane's host cluster. Cluster name must - # be a valid RFC 1123 subdomain name. Cluster context - # must be specified if the cluster name is different - # than the cluster's context in the local kubeconfig. - kubefedctl join foo --host-cluster-context=bar` - - // Policy rules allowing full access to resources in the cluster - // or namespace. - namespacedPolicyRules = []rbacv1.PolicyRule{ - { - Verbs: []string{rbacv1.VerbAll}, - APIGroups: []string{rbacv1.APIGroupAll}, - Resources: []string{rbacv1.ResourceAll}, - }, - } - clusterPolicyRules = []rbacv1.PolicyRule{ - namespacedPolicyRules[0], - { - NonResourceURLs: []string{rbacv1.NonResourceAll}, - Verbs: []string{"get"}, - }, - } -) - -type joinFederation struct { - options.GlobalSubcommandOptions - options.CommonJoinOptions - joinFederationOptions -} - -type joinFederationOptions struct { - secretName string - scope apiextv1b1.ResourceScope - errorOnExisting bool -} - -// Bind adds the join specific arguments to the flagset passed in as an -// argument. -func (o *joinFederationOptions) Bind(flags *pflag.FlagSet) { - flags.StringVar(&o.secretName, "secret-name", "", - "Name of the secret where the cluster's credentials will be stored in the host cluster. This name should be a valid RFC 1035 label. If unspecified, defaults to a generated name containing the cluster name.") - flags.BoolVar(&o.errorOnExisting, "error-on-existing", true, - "Whether the join operation will throw an error if it encounters existing artifacts with the same name as those it's trying to create. If false, the join operation will update existing artifacts to match its own specification.") -} - -// NewCmdJoin defines the `join` command that registers a cluster with -// a KubeFed control plane. -func NewCmdJoin(cmdOut io.Writer, config util.FedConfig) *cobra.Command { - opts := &joinFederation{} - - cmd := &cobra.Command{ - Use: "join CLUSTER_NAME --host-cluster-context=HOST_CONTEXT", - Short: "Register a cluster with a KubeFed control plane", - Long: join_long, - Example: join_example, - Run: func(cmd *cobra.Command, args []string) { - err := opts.Complete(args) - if err != nil { - klog.Fatalf("Error: %v", err) - } - - err = opts.Run(cmdOut, config) - if err != nil { - klog.Fatalf("Error: %v", err) - } - }, - } - - flags := cmd.Flags() - opts.GlobalSubcommandBind(flags) - opts.CommonSubcommandBind(flags) - opts.Bind(flags) - - return cmd -} - -// Complete ensures that options are valid and marshals them if necessary. -func (j *joinFederation) Complete(args []string) error { - err := j.SetName(args) - if err != nil { - return err - } - - if j.ClusterContext == "" { - klog.V(2).Infof("Defaulting cluster context to joining cluster name %s", j.ClusterName) - j.ClusterContext = j.ClusterName - } - - if j.HostClusterName != "" && strings.ContainsAny(j.HostClusterName, ":/") { - return goerrors.New("host-cluster-name may not contain \"/\" or \":\"") - } - - if j.HostClusterName == "" && strings.ContainsAny(j.HostClusterContext, ":/") { - klog.Fatal("host-cluster-name must be set if the name of the host cluster context contains one of \":\" or \"/\"") - } - - klog.V(2).Infof("Args and flags: name %s, host: %s, host-system-namespace: %s, kubeconfig: %s, cluster-context: %s, secret-name: %s, dry-run: %v", - j.ClusterName, j.HostClusterContext, j.KubeFedNamespace, j.Kubeconfig, j.ClusterContext, - j.secretName, j.DryRun) - - return nil -} - -// Run is the implementation of the `join` command. -func (j *joinFederation) Run(cmdOut io.Writer, config util.FedConfig) error { - hostClientConfig := config.GetClientConfig(j.HostClusterContext, j.Kubeconfig) - if err := j.SetHostClusterContextFromConfig(hostClientConfig); err != nil { - return err - } - - hostConfig, err := hostClientConfig.ClientConfig() - if err != nil { - // TODO(font): Return new error with this same text so it can be output - // by caller. - klog.V(2).Infof("Failed to get host cluster config: %v", err) - return err - } - - j.scope, err = options.GetScopeFromKubeFedConfig(hostConfig, j.KubeFedNamespace) - if err != nil { - return err - } - - clusterConfig, err := config.ClusterConfig(j.ClusterContext, j.Kubeconfig) - if err != nil { - klog.V(2).Infof("Failed to get joining cluster config: %v", err) - return err - } - - hostClusterName := j.HostClusterContext - if j.HostClusterName != "" { - hostClusterName = j.HostClusterName - } - - _, err = JoinCluster(hostConfig, clusterConfig, j.KubeFedNamespace, - hostClusterName, j.ClusterName, j.secretName, j.scope, j.DryRun, j.errorOnExisting) - - return err -} - -// JoinCluster registers a cluster with a KubeFed control plane. The -// KubeFed namespace in the joining cluster will be the same as in the -// host cluster. -func JoinCluster(hostConfig, clusterConfig *rest.Config, kubefedNamespace, - hostClusterName, joiningClusterName, secretName string, - scope apiextv1b1.ResourceScope, dryRun, errorOnExisting bool) (*fedv1b1.KubeFedCluster, error) { - - return joinClusterForNamespace(hostConfig, clusterConfig, kubefedNamespace, - kubefedNamespace, hostClusterName, joiningClusterName, secretName, - scope, dryRun, errorOnExisting) -} - -// joinClusterForNamespace registers a cluster with a KubeFed control -// plane. The KubeFed namespace in the joining cluster is provided by -// the joiningNamespace parameter. -func joinClusterForNamespace(hostConfig, clusterConfig *rest.Config, kubefedNamespace, - joiningNamespace, hostClusterName, joiningClusterName, secretName string, - scope apiextv1b1.ResourceScope, dryRun, errorOnExisting bool) (*fedv1b1.KubeFedCluster, error) { - - hostClientset, err := util.HostClientset(hostConfig) - if err != nil { - klog.V(2).Infof("Failed to get host cluster clientset: %v", err) - return nil, err - } - - clusterClientset, err := util.ClusterClientset(clusterConfig) - if err != nil { - klog.V(2).Infof("Failed to get joining cluster clientset: %v", err) - return nil, err - } - - client, err := genericclient.New(hostConfig) - if err != nil { - klog.V(2).Infof("Failed to get kubefed clientset: %v", err) - return nil, err - } - - klog.V(2).Infof("Performing preflight checks.") - err = performPreflightChecks(clusterClientset, joiningClusterName, hostClusterName, joiningNamespace, errorOnExisting) - if err != nil { - return nil, err - } - - klog.V(2).Infof("Creating %s namespace in joining cluster", joiningNamespace) - _, err = createKubeFedNamespace(clusterClientset, joiningNamespace, - joiningClusterName, dryRun) - if err != nil { - klog.V(2).Infof("Error creating %s namespace in joining cluster: %v", - joiningNamespace, err) - return nil, err - } - klog.V(2).Infof("Created %s namespace in joining cluster", joiningNamespace) - - saName, err := createAuthorizedServiceAccount(clusterClientset, - joiningNamespace, joiningClusterName, hostClusterName, - scope, dryRun, errorOnExisting) - if err != nil { - return nil, err - } - - secret, caBundle, err := populateSecretInHostCluster(clusterClientset, hostClientset, - saName, kubefedNamespace, joiningNamespace, joiningClusterName, secretName, dryRun) - if err != nil { - klog.V(2).Infof("Error creating secret in host cluster: %s due to: %v", hostClusterName, err) - return nil, err - } - - var disabledTLSValidations []fedv1b1.TLSValidation - if clusterConfig.TLSClientConfig.Insecure { - disabledTLSValidations = append(disabledTLSValidations, fedv1b1.TLSAll) - } - - kubefedCluster, err := createKubeFedCluster(client, joiningClusterName, clusterConfig.Host, - secret.Name, kubefedNamespace, caBundle, disabledTLSValidations, dryRun, errorOnExisting) - if err != nil { - klog.V(2).Infof("Failed to create federated cluster resource: %v", err) - return nil, err - } - - klog.V(2).Info("Created federated cluster resource") - return kubefedCluster, nil -} - -// This function is exported for testing purposes only. -var TestOnly_JoinClusterForNamespace = joinClusterForNamespace - -// performPreflightChecks checks that the host and joining clusters are in -// a consistent state. -func performPreflightChecks(clusterClientset kubeclient.Interface, name, hostClusterName, - kubefedNamespace string, errorOnExisting bool) error { - // Make sure there is no existing service account in the joining cluster. - saName := util.ClusterServiceAccountName(name, hostClusterName) - _, err := clusterClientset.CoreV1().ServiceAccounts(kubefedNamespace).Get(saName, - metav1.GetOptions{}) - - switch { - case apierrors.IsNotFound(err): - return nil - case err != nil: - return err - case errorOnExisting: - return errors.Errorf("service account: %s already exists in joining cluster: %s", saName, name) - default: - klog.V(2).Infof("Service account %s already exists in joining cluster %s", saName, name) - return nil - } -} - -// createKubeFedCluster creates a federated cluster resource that associates -// the cluster and secret. -func createKubeFedCluster(client genericclient.Client, joiningClusterName, apiEndpoint, - secretName, kubefedNamespace string, caBundle []byte, disabledTLSValidations []fedv1b1.TLSValidation, - dryRun, errorOnExisting bool) (*fedv1b1.KubeFedCluster, error) { - fedCluster := &fedv1b1.KubeFedCluster{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: kubefedNamespace, - Name: joiningClusterName, - }, - Spec: fedv1b1.KubeFedClusterSpec{ - APIEndpoint: apiEndpoint, - CABundle: caBundle, - SecretRef: fedv1b1.LocalSecretReference{ - Name: secretName, - }, - DisabledTLSValidations: disabledTLSValidations, - }, - } - - if dryRun { - return fedCluster, nil - } - - existingFedCluster := &fedv1b1.KubeFedCluster{} - err := client.Get(context.TODO(), existingFedCluster, kubefedNamespace, joiningClusterName) - switch { - case err != nil && !apierrors.IsNotFound(err): - klog.V(2).Infof("Could not retrieve federated cluster %s due to %v", joiningClusterName, err) - return nil, err - case err == nil && errorOnExisting: - return nil, errors.Errorf("federated cluster %s already exists in host cluster", joiningClusterName) - case err == nil: - existingFedCluster.Spec = fedCluster.Spec - err = client.Update(context.TODO(), existingFedCluster) - if err != nil { - klog.V(2).Infof("Could not update federated cluster %s due to %v", fedCluster.Name, err) - return nil, err - } - return existingFedCluster, nil - default: - err = client.Create(context.TODO(), fedCluster) - if err != nil { - klog.V(2).Infof("Could not create federated cluster %s due to %v", fedCluster.Name, err) - return nil, err - } - return fedCluster, nil - } -} - -// createKubeFedNamespace creates the kubefed namespace in the cluster -// associated with clusterClientset, if it doesn't already exist. -func createKubeFedNamespace(clusterClientset kubeclient.Interface, kubefedNamespace, - joiningClusterName string, dryRun bool) (*corev1.Namespace, error) { - fedNamespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: kubefedNamespace, - }, - } - - if dryRun { - return fedNamespace, nil - } - - _, err := clusterClientset.CoreV1().Namespaces().Get(kubefedNamespace, metav1.GetOptions{}) - if err != nil && !apierrors.IsNotFound(err) { - klog.V(2).Infof("Could not get %s namespace: %v", kubefedNamespace, err) - return nil, err - } - - if err == nil { - klog.V(2).Infof("Already existing %s namespace", kubefedNamespace) - return fedNamespace, nil - } - - // Not found, so create. - _, err = clusterClientset.CoreV1().Namespaces().Create(fedNamespace) - if err != nil && !apierrors.IsAlreadyExists(err) { - klog.V(2).Infof("Could not create %s namespace: %v", kubefedNamespace, err) - return nil, err - } - return fedNamespace, nil -} - -// createAuthorizedServiceAccount creates a service account and grants -// the privileges required by the KubeFed control plane to manage -// resources in the joining cluster. The name of the created service -// account is returned on success. -func createAuthorizedServiceAccount(joiningClusterClientset kubeclient.Interface, - namespace, joiningClusterName, hostClusterName string, - scope apiextv1b1.ResourceScope, dryRun, errorOnExisting bool) (string, error) { - - klog.V(2).Infof("Creating service account in joining cluster: %s", joiningClusterName) - - saName, err := createServiceAccount(joiningClusterClientset, namespace, - joiningClusterName, hostClusterName, dryRun, errorOnExisting) - if err != nil { - klog.V(2).Infof("Error creating service account: %s in joining cluster: %s due to: %v", - saName, joiningClusterName, err) - return "", err - } - - klog.V(2).Infof("Created service account: %s in joining cluster: %s", saName, joiningClusterName) - - if scope == apiextv1b1.NamespaceScoped { - klog.V(2).Infof("Creating role and binding for service account: %s in joining cluster: %s", saName, joiningClusterName) - - err = createRoleAndBinding(joiningClusterClientset, saName, namespace, joiningClusterName, dryRun, errorOnExisting) - if err != nil { - klog.V(2).Infof("Error creating role and binding for service account: %s in joining cluster: %s due to: %v", saName, joiningClusterName, err) - return "", err - } - - klog.V(2).Infof("Created role and binding for service account: %s in joining cluster: %s", - saName, joiningClusterName) - - klog.V(2).Infof("Creating health check cluster role and binding for service account: %s in joining cluster: %s", saName, joiningClusterName) - - err = createHealthCheckClusterRoleAndBinding(joiningClusterClientset, saName, namespace, joiningClusterName, - dryRun, errorOnExisting) - if err != nil { - klog.V(2).Infof("Error creating health check cluster role and binding for service account: %s in joining cluster: %s due to: %v", - saName, joiningClusterName, err) - return "", err - } - - klog.V(2).Infof("Created health check cluster role and binding for service account: %s in joining cluster: %s", - saName, joiningClusterName) - - } else { - klog.V(2).Infof("Creating cluster role and binding for service account: %s in joining cluster: %s", saName, joiningClusterName) - - err = createClusterRoleAndBinding(joiningClusterClientset, saName, namespace, joiningClusterName, dryRun, errorOnExisting) - if err != nil { - klog.V(2).Infof("Error creating cluster role and binding for service account: %s in joining cluster: %s due to: %v", - saName, joiningClusterName, err) - return "", err - } - - klog.V(2).Infof("Created cluster role and binding for service account: %s in joining cluster: %s", - saName, joiningClusterName) - } - - return saName, nil -} - -// createServiceAccount creates a service account in the cluster associated -// with clusterClientset with credentials that will be used by the host cluster -// to access its API server. -func createServiceAccount(clusterClientset kubeclient.Interface, namespace, - joiningClusterName, hostClusterName string, dryRun, errorOnExisting bool) (string, error) { - saName := util.ClusterServiceAccountName(joiningClusterName, hostClusterName) - sa := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: saName, - Namespace: namespace, - }, - } - - if dryRun { - return saName, nil - } - - // Create a new service account. - _, err := clusterClientset.CoreV1().ServiceAccounts(namespace).Create(sa) - switch { - case apierrors.IsAlreadyExists(err) && errorOnExisting: - klog.V(2).Infof("Service account %s/%s already exists in target cluster %s", namespace, saName, joiningClusterName) - return "", err - case err != nil && !apierrors.IsAlreadyExists(err): - klog.V(2).Infof("Could not create service account %s/%s in target cluster %s due to: %v", namespace, saName, joiningClusterName, err) - return "", err - default: - return saName, nil - } -} - -func bindingSubjects(saName, namespace string) []rbacv1.Subject { - return []rbacv1.Subject{ - { - Kind: rbacv1.ServiceAccountKind, - Name: saName, - Namespace: namespace, - }, - } -} - -// createClusterRoleAndBinding creates an RBAC cluster role and -// binding that allows the service account identified by saName to -// access all resources in all namespaces in the cluster associated -// with clientset. -func createClusterRoleAndBinding(clientset kubeclient.Interface, saName, namespace, clusterName string, dryRun, errorOnExisting bool) error { - if dryRun { - return nil - } - - roleName := util.RoleName(saName) - - role := &rbacv1.ClusterRole{ - ObjectMeta: metav1.ObjectMeta{ - Name: roleName, - }, - Rules: clusterPolicyRules, - } - existingRole, err := clientset.RbacV1().ClusterRoles().Get(roleName, metav1.GetOptions{}) - switch { - case err != nil && !apierrors.IsNotFound(err): - klog.V(2).Infof("Could not get cluster role for service account %s in joining cluster %s due to %v", - saName, clusterName, err) - return err - case err == nil && errorOnExisting: - return errors.Errorf("cluster role for service account %s in joining cluster %s already exists", saName, clusterName) - case err == nil: - existingRole.Rules = role.Rules - _, err := clientset.RbacV1().ClusterRoles().Update(existingRole) - if err != nil { - klog.V(2).Infof("Could not update cluster role for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - default: // role was not found - _, err := clientset.RbacV1().ClusterRoles().Create(role) - if err != nil { - klog.V(2).Infof("Could not create cluster role for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - } - - // TODO: This should limit its access to only necessary resources. - binding := &rbacv1.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: roleName, - }, - Subjects: bindingSubjects(saName, namespace), - RoleRef: rbacv1.RoleRef{ - APIGroup: rbacv1.GroupName, - Kind: "ClusterRole", - Name: roleName, - }, - } - existingBinding, err := clientset.RbacV1().ClusterRoleBindings().Get(binding.Name, metav1.GetOptions{}) - switch { - case err != nil && !apierrors.IsNotFound(err): - klog.V(2).Infof("Could not get cluster role binding for service account %s in joining cluster %s due to %v", - saName, clusterName, err) - return err - case err == nil && errorOnExisting: - return errors.Errorf("cluster role binding for service account %s in joining cluster %s already exists", saName, clusterName) - case err == nil: - // The roleRef cannot be updated, therefore if the existing roleRef is different, the existing rolebinding - // must be deleted and recreated with the correct roleRef - if !reflect.DeepEqual(existingBinding.RoleRef, binding.RoleRef) { - err = clientset.RbacV1().ClusterRoleBindings().Delete(existingBinding.Name, &metav1.DeleteOptions{}) - if err != nil { - klog.V(2).Infof("Could not delete existing cluster role binding for service account %s in joining cluster %s due to: %v", - saName, clusterName, err) - return err - } - _, err = clientset.RbacV1().ClusterRoleBindings().Create(binding) - if err != nil { - klog.V(2).Infof("Could not create cluster role binding for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - } else { - existingBinding.Subjects = binding.Subjects - _, err := clientset.RbacV1().ClusterRoleBindings().Update(existingBinding) - if err != nil { - klog.V(2).Infof("Could not update cluster role binding for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - } - default: - _, err = clientset.RbacV1().ClusterRoleBindings().Create(binding) - if err != nil { - klog.V(2).Infof("Could not create cluster role binding for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - } - return nil -} - -// createRoleAndBinding creates an RBAC role and binding -// that allows the service account identified by saName to access all -// resources in the specified namespace. -func createRoleAndBinding(clientset kubeclient.Interface, saName, namespace, clusterName string, dryRun, errorOnExisting bool) error { - if dryRun { - return nil - } - - roleName := util.RoleName(saName) - - role := &rbacv1.Role{ - ObjectMeta: metav1.ObjectMeta{ - Name: roleName, - }, - Rules: namespacedPolicyRules, - } - existingRole, err := clientset.RbacV1().Roles(namespace).Get(roleName, metav1.GetOptions{}) - switch { - case err != nil && !apierrors.IsNotFound(err): - klog.V(2).Infof("Could not retrieve role for service account %s in joining cluster %s due to %v", saName, clusterName, err) - return err - case errorOnExisting && err == nil: - return errors.Errorf("role for service account %s in joining cluster %s already exists", saName, clusterName) - case err == nil: - existingRole.Rules = role.Rules - _, err = clientset.RbacV1().Roles(namespace).Update(existingRole) - if err != nil { - klog.V(2).Infof("Could not update role for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - default: - _, err := clientset.RbacV1().Roles(namespace).Create(role) - if err != nil { - klog.V(2).Infof("Could not create role for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - } - - binding := &rbacv1.RoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: roleName, - }, - Subjects: bindingSubjects(saName, namespace), - RoleRef: rbacv1.RoleRef{ - APIGroup: rbacv1.GroupName, - Kind: "Role", - Name: roleName, - }, - } - - existingBinding, err := clientset.RbacV1().RoleBindings(namespace).Get(binding.Name, metav1.GetOptions{}) - switch { - case err != nil && !apierrors.IsNotFound(err): - klog.V(2).Infof("Could not retrieve role binding for service account %s in joining cluster %s due to: %v", - saName, clusterName, err) - return err - case err == nil && errorOnExisting: - return errors.Errorf("role binding for service account %s in joining cluster %s already exists", saName, clusterName) - case err == nil: - // The roleRef cannot be updated, therefore if the existing roleRef is different, the existing rolebinding - // must be deleted and recreated with the correct roleRef - if !reflect.DeepEqual(existingBinding.RoleRef, binding.RoleRef) { - err = clientset.RbacV1().RoleBindings(namespace).Delete(existingBinding.Name, &metav1.DeleteOptions{}) - if err != nil { - klog.V(2).Infof("Could not delete existing role binding for service account %s in joining cluster %s due to: %v", - saName, clusterName, err) - return err - } - _, err = clientset.RbacV1().RoleBindings(namespace).Create(binding) - if err != nil { - klog.V(2).Infof("Could not create role binding for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - } else { - existingBinding.Subjects = binding.Subjects - _, err = clientset.RbacV1().RoleBindings(namespace).Update(existingBinding) - if err != nil { - klog.V(2).Infof("Could not update role binding for service account %s in joining cluster %s due to: %v", - saName, clusterName, err) - return err - } - } - default: - _, err = clientset.RbacV1().RoleBindings(namespace).Create(binding) - if err != nil { - klog.V(2).Infof("Could not create role binding for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - } - - return nil -} - -// createHealthCheckClusterRoleAndBinding creates an RBAC cluster role and -// binding that allows the service account identified by saName to -// access the health check path of the cluster. -func createHealthCheckClusterRoleAndBinding(clientset kubeclient.Interface, saName, namespace, clusterName string, dryRun, errorOnExisting bool) error { - if dryRun { - return nil - } - - roleName := util.HealthCheckRoleName(saName, namespace) - - role := &rbacv1.ClusterRole{ - ObjectMeta: metav1.ObjectMeta{ - Name: roleName, - }, - Rules: []rbacv1.PolicyRule{ - { - Verbs: []string{"Get"}, - NonResourceURLs: []string{"/healthz"}, - }, - // The cluster client expects to be able to list nodes to retrieve zone and region details. - // TODO(marun) Consider making zone/region retrieval optional - { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"nodes"}, - }, - }, - } - existingRole, err := clientset.RbacV1().ClusterRoles().Get(role.Name, metav1.GetOptions{}) - switch { - case err != nil && !apierrors.IsNotFound(err): - klog.V(2).Infof("Could not get health check cluster role for service account %s in joining cluster %s due to %v", - saName, clusterName, err) - return err - case err == nil && errorOnExisting: - return errors.Errorf("health check cluster role for service account %s in joining cluster %s already exists", saName, clusterName) - case err == nil: - existingRole.Rules = role.Rules - _, err := clientset.RbacV1().ClusterRoles().Update(existingRole) - if err != nil { - klog.V(2).Infof("Could not update health check cluster role for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - default: // role was not found - _, err := clientset.RbacV1().ClusterRoles().Create(role) - if err != nil { - klog.V(2).Infof("Could not create health check cluster role for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - } - - binding := &rbacv1.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: roleName, - }, - Subjects: bindingSubjects(saName, namespace), - RoleRef: rbacv1.RoleRef{ - APIGroup: rbacv1.GroupName, - Kind: "ClusterRole", - Name: roleName, - }, - } - existingBinding, err := clientset.RbacV1().ClusterRoleBindings().Get(binding.Name, metav1.GetOptions{}) - switch { - case err != nil && !apierrors.IsNotFound(err): - klog.V(2).Infof("Could not get health check cluster role binding for service account %s in joining cluster %s due to %v", - saName, clusterName, err) - return err - case err == nil && errorOnExisting: - return errors.Errorf("health check cluster role binding for service account %s in joining cluster %s already exists", saName, clusterName) - case err == nil: - // The roleRef cannot be updated, therefore if the existing roleRef is different, the existing rolebinding - // must be deleted and recreated with the correct roleRef - if !reflect.DeepEqual(existingBinding.RoleRef, binding.RoleRef) { - err = clientset.RbacV1().ClusterRoleBindings().Delete(existingBinding.Name, &metav1.DeleteOptions{}) - if err != nil { - klog.V(2).Infof("Could not delete existing health check cluster role binding for service account %s in joining cluster %s due to: %v", - saName, clusterName, err) - return err - } - _, err = clientset.RbacV1().ClusterRoleBindings().Create(binding) - if err != nil { - klog.V(2).Infof("Could not create health check cluster role binding for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - } else { - existingBinding.Subjects = binding.Subjects - _, err := clientset.RbacV1().ClusterRoleBindings().Update(existingBinding) - if err != nil { - klog.V(2).Infof("Could not update health check cluster role binding for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - } - default: - _, err = clientset.RbacV1().ClusterRoleBindings().Create(binding) - if err != nil { - klog.V(2).Infof("Could not create health check cluster role binding for service account: %s in joining cluster: %s due to: %v", - saName, clusterName, err) - return err - } - } - return nil -} - -// populateSecretInHostCluster copies the service account secret for saName -// from the cluster referenced by clusterClientset to the client referenced by -// hostClientset, putting it in a secret named secretName in the provided -// namespace. -func populateSecretInHostCluster(clusterClientset, hostClientset kubeclient.Interface, - saName, hostNamespace, joiningNamespace, joiningClusterName, secretName string, - dryRun bool) (*corev1.Secret, []byte, error) { - - klog.V(2).Infof("Creating cluster credentials secret in host cluster") - - if dryRun { - dryRunSecret := &corev1.Secret{} - dryRunSecret.Name = secretName - return dryRunSecret, nil, nil - } - - // Get the secret from the joining cluster. - var secret *corev1.Secret - err := wait.PollImmediate(1*time.Second, serviceAccountSecretTimeout, func() (bool, error) { - sa, err := clusterClientset.CoreV1().ServiceAccounts(joiningNamespace).Get(saName, - metav1.GetOptions{}) - if err != nil { - return false, nil - } - - for _, objReference := range sa.Secrets { - saSecretName := objReference.Name - var err error - secret, err = clusterClientset.CoreV1().Secrets(joiningNamespace).Get(saSecretName, - metav1.GetOptions{}) - if err != nil { - return false, nil - } - if secret.Type == corev1.SecretTypeServiceAccountToken { - klog.V(2).Infof("Using secret named: %s", secret.Name) - return true, nil - } - } - return false, nil - }) - - if err != nil { - klog.V(2).Infof("Could not get service account secret from joining cluster: %v", err) - return nil, nil, err - } - - token, ok := secret.Data[ctlutil.TokenKey] - if !ok { - return nil, nil, errors.Errorf("Key %q not found in service account secret", ctlutil.TokenKey) - } - - // Create a secret in the host cluster containing the token. - v1Secret := corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: hostNamespace, - }, - Data: map[string][]byte{ - ctlutil.TokenKey: token, - }, - } - - if secretName == "" { - v1Secret.GenerateName = joiningClusterName + "-" - } else { - v1Secret.Name = secretName - } - - v1SecretResult, err := hostClientset.CoreV1().Secrets(hostNamespace).Create(&v1Secret) - if err != nil { - klog.V(2).Infof("Could not create secret in host cluster: %v", err) - return nil, nil, err - } - - // caBundle is optional so no error is suggested if it is not - // found in the secret. - caBundle := secret.Data["ca.crt"] - - klog.V(2).Infof("Created secret in host cluster named: %s", v1SecretResult.Name) - return v1SecretResult, caBundle, nil -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/kubefedctl.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/kubefedctl.go deleted file mode 100644 index 9f71cc125d28050c431ab7a3057e882418fa4a9a..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/kubefedctl.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 kubefedctl - -import ( - "flag" - "io" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - "k8s.io/client-go/tools/clientcmd" - apiserverflag "k8s.io/component-base/cli/flag" - - "sigs.k8s.io/kubefed/pkg/kubefedctl/enable" - "sigs.k8s.io/kubefed/pkg/kubefedctl/federate" - "sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning" - "sigs.k8s.io/kubefed/pkg/kubefedctl/util" -) - -// NewKubeFedCtlCommand creates the `kubefedctl` command and its nested children. -func NewKubeFedCtlCommand(out io.Writer) *cobra.Command { - // Parent command to which all subcommands are added. - rootCmd := &cobra.Command{ - Use: "kubefedctl", - Short: "kubefedctl controls a Kubernetes Cluster Federation", - Long: "kubefedctl controls a Kubernetes Cluster Federation. Find more information at https://sigs.k8s.io/kubefed.", - - RunE: runHelp, - } - - // Add the command line flags from other dependencies (e.g., klog), but do not - // warn if they contain underscores. - pflag.CommandLine.SetNormalizeFunc(apiserverflag.WordSepNormalizeFunc) - pflag.CommandLine.AddGoFlagSet(flag.CommandLine) - rootCmd.PersistentFlags().AddFlagSet(pflag.CommandLine) - - // From this point and forward we get warnings on flags that contain "_" separators - rootCmd.SetGlobalNormalizationFunc(apiserverflag.WarnWordSepNormalizeFunc) - - // Prevent klog errors about logging before parsing. - _ = flag.CommandLine.Parse(nil) - - fedConfig := util.NewFedConfig(clientcmd.NewDefaultPathOptions()) - rootCmd.AddCommand(enable.NewCmdTypeEnable(out, fedConfig)) - rootCmd.AddCommand(NewCmdTypeDisable(out, fedConfig)) - rootCmd.AddCommand(federate.NewCmdFederateResource(out, fedConfig)) - rootCmd.AddCommand(NewCmdJoin(out, fedConfig)) - rootCmd.AddCommand(NewCmdUnjoin(out, fedConfig)) - rootCmd.AddCommand(orphaning.NewCmdOrphaning(out, fedConfig)) - rootCmd.AddCommand(NewCmdVersion(out)) - - return rootCmd -} - -func runHelp(cmd *cobra.Command, args []string) error { - return cmd.Help() -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning/disable.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning/disable.go deleted file mode 100644 index c108d9c665dfc2a22c582aef13f11efd5dd21912..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning/disable.go +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright 2019 The Kubernetes 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 orphaning - -import ( - "io" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/klog" - - ctlutil "sigs.k8s.io/kubefed/pkg/controller/util" - "sigs.k8s.io/kubefed/pkg/kubefedctl/util" -) - -var ( - orphaning_disable_long = ` - Removes previously added "orphaning enable" ('kubefed.io/orphan: true') - annotation from a federated resource. When the federated resource is subsequently marked for deletion, - the resources it manages in member clusters will be removed before the federated resource is removed. - - Current context is assumed to be a Kubernetes cluster hosting - the kubefed control plane. Please use the - --host-cluster-context flag otherwise.` - - orphaning_disable_example = ` - # Disable the orphaning mode for a federated resource of type FederatedDeployment and named foo - kubefedctl orphaning disable FederatedDeployment foo --host-cluster-context=cluster1` -) - -// newCmdDisableOrphaning removes the 'kubefed.io/orphan: true' annotation from the federated resource -func newCmdDisableOrphaning(cmdOut io.Writer, config util.FedConfig) *cobra.Command { - opts := &orphanResource{} - cmd := &cobra.Command{ - Use: "disable ", - Short: "Disable orphaning deletion to ensure the removal of managed resources before removing the managing federated resource", - Long: orphaning_disable_long, - Example: orphaning_disable_example, - Run: func(cmd *cobra.Command, args []string) { - err := opts.Complete(args, config) - if err != nil { - klog.Fatalf("Error: %v", err) - } - - err = opts.RunDisable(cmdOut, config) - if err != nil { - klog.Fatalf("Error: %v", err) - } - }, - } - - flags := cmd.Flags() - opts.GlobalSubcommandBind(flags) - err := opts.Bind(flags) - if err != nil { - klog.Fatalf("Error: %v", err) - } - - return cmd -} - -// RunDisable implements the `disable` command. -func (o *orphanResource) RunDisable(cmdOut io.Writer, config util.FedConfig) error { - resourceClient, err := o.GetResourceClient(config, cmdOut) - if err != nil { - return err - } - fedResource, err := o.GetFederatedResource(resourceClient) - if err != nil { - return err - } - if !ctlutil.IsOrphaningEnabled(fedResource) { - return nil - } - ctlutil.DisableOrphaning(fedResource) - _, err = resourceClient.Update(fedResource, metav1.UpdateOptions{}) - if err != nil { - return errors.Wrapf(err, "Failed to update resource %s %q", fedResource.GetKind(), - ctlutil.QualifiedName{Name: fedResource.GetName(), Namespace: fedResource.GetNamespace()}) - } - return nil -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning/enable.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning/enable.go deleted file mode 100644 index 41d2c09a38226632da788b7b2945e3fdf16e0629..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning/enable.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright 2019 The Kubernetes 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 orphaning - -import ( - "io" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/klog" - - ctlutil "sigs.k8s.io/kubefed/pkg/controller/util" - "sigs.k8s.io/kubefed/pkg/kubefedctl/util" -) - -var ( - orphaning_enable_long = ` - Prevents the removal of managed resources from member clusters when their managing federated - resource is removed. This is accomplished by adding 'kubefed.io/orphan: true' as an annotation to the - federated resource. - - Current context is assumed to be a Kubernetes cluster hosting - the kubefed control plane. Please use the - --host-cluster-context flag otherwise.` - - orphan_enable_example = ` - # Enable the orphaning mode for a federated resource of type FederatedDeployment and named foo - kubefedctl orphaning enable FederatedDeployment foo --host-cluster-context=cluster1` -) - -// newCmdEnableOrphaning adds 'kubefed.io/orphan: true' as an annotation to the federated resource -func newCmdEnableOrphaning(cmdOut io.Writer, config util.FedConfig) *cobra.Command { - opts := &orphanResource{} - cmd := &cobra.Command{ - Use: "enable ", - Short: "Enable the orphaning (i.e. retention) of resources managed by a federated resource upon its removal.", - Long: orphaning_enable_long, - Example: orphan_enable_example, - Run: func(cmd *cobra.Command, args []string) { - err := opts.Complete(args, config) - if err != nil { - klog.Fatalf("Error: %v", err) - } - - err = opts.RunEnable(cmdOut, config) - if err != nil { - klog.Fatalf("Error: %v", err) - } - }, - } - - flags := cmd.Flags() - opts.GlobalSubcommandBind(flags) - err := opts.Bind(flags) - if err != nil { - klog.Fatalf("Error: %v", err) - } - - return cmd -} - -// RunEnable implements the `enable` command. -func (o *orphanResource) RunEnable(cmdOut io.Writer, config util.FedConfig) error { - resourceClient, err := o.GetResourceClient(config, cmdOut) - if err != nil { - return err - } - fedResource, err := o.GetFederatedResource(resourceClient) - if err != nil { - return err - } - if ctlutil.IsOrphaningEnabled(fedResource) { - return nil - } - ctlutil.EnableOrphaning(fedResource) - _, err = resourceClient.Update(fedResource, metav1.UpdateOptions{}) - if err != nil { - return errors.Wrapf(err, "Failed to update resource %s %q", fedResource.GetKind(), - ctlutil.QualifiedName{Name: fedResource.GetName(), Namespace: fedResource.GetNamespace()}) - } - - return nil -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning/orphaning.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning/orphaning.go deleted file mode 100644 index d625ed43d6a839bc0f431e8158fe32d46892f9b1..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning/orphaning.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright 2019 The Kubernetes 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 orphaning - -import ( - "fmt" - "io" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/client-go/dynamic" - "k8s.io/klog" - - "sigs.k8s.io/kubefed/pkg/apis/core/typeconfig" - ctlutil "sigs.k8s.io/kubefed/pkg/controller/util" - "sigs.k8s.io/kubefed/pkg/kubefedctl/enable" - "sigs.k8s.io/kubefed/pkg/kubefedctl/options" - "sigs.k8s.io/kubefed/pkg/kubefedctl/util" -) - -type orphanResource struct { - options.GlobalSubcommandOptions - typeName string - resourceName string - resourceNamespace string -} - -// Bind adds the join specific arguments to the flagset passed in as an argument. -func (o *orphanResource) Bind(flags *pflag.FlagSet) error { - flags.StringVarP(&o.resourceNamespace, "namespace", "n", "", "If present, the namespace scope for this CLI request") - err := flags.MarkHidden("kubefed-namespace") - if err != nil { - return err - } - err = flags.MarkHidden("dry-run") - if err != nil { - return err - } - return nil -} - -// NewCmdOrphaning the head of orphaning-deletion sub commands -func NewCmdOrphaning(cmdOut io.Writer, config util.FedConfig) *cobra.Command { - cmd := &cobra.Command{ - Use: "orphaning-deletion", - Short: "Manage orphaning delete policy", - Long: "Manage orphaning delete policy", - Run: func(cmd *cobra.Command, args []string) { - err := cmd.Help() - if err != nil { - klog.Fatalf("Error: %v", err) - } - }, - } - cmd.AddCommand(newCmdEnableOrphaning(cmdOut, config)) - cmd.AddCommand(newCmdDisableOrphaning(cmdOut, config)) - cmd.AddCommand(newCmdStatusOrphaning(cmdOut, config)) - - return cmd -} - -// Complete ensures that options are valid and marshals them if necessary. -func (o *orphanResource) Complete(args []string, config util.FedConfig) error { - if len(args) == 0 { - return errors.New("resource type is required") - } - - o.typeName = args[0] - - if len(args) == 1 { - return errors.New("resource name is required") - } - o.resourceName = args[1] - - if len(o.resourceNamespace) == 0 { - var err error - o.resourceNamespace, err = util.GetNamespace(o.HostClusterContext, o.Kubeconfig, config) - return err - } - return nil -} - -// Returns a Federated Resources Interface -func (o *orphanResource) GetResourceClient(config util.FedConfig, cmdOut io.Writer) (dynamic.ResourceInterface, error) { - hostClientConfig := config.GetClientConfig(o.HostClusterContext, o.Kubeconfig) - if err := o.SetHostClusterContextFromConfig(hostClientConfig); err != nil { - return nil, err - } - hostConfig, err := hostClientConfig.ClientConfig() - if err != nil { - return nil, errors.Wrapf(err, "Unable to load configuration for cluster context %q in kubeconfig %q.`", - o.HostClusterContext, o.Kubeconfig) - } - // Lookup kubernetes API availability - apiResource, err := enable.LookupAPIResource(hostConfig, o.typeName, "") - if err != nil { - return nil, errors.Wrapf(err, "Failed to find targeted %s type", o.typeName) - } - klog.V(2).Infof("API Resource for %s/%s found", typeconfig.GroupQualifiedName(*apiResource), apiResource.Version) - if !util.IsFederatedAPIResource(apiResource.Kind, apiResource.Group) { - fmt.Fprintf(cmdOut, "Warning: %s/%s might not be a federated resource\n", - typeconfig.GroupQualifiedName(*apiResource), apiResource.Version) - } - targetClient, err := ctlutil.NewResourceClient(hostConfig, apiResource) - - if err != nil { - return nil, errors.Wrapf(err, "Error creating client for %s", apiResource.Kind) - } - - resourceClient := targetClient.Resources(o.resourceNamespace) - return resourceClient, nil -} - -// Returns the Federated resource where the orphaning-deletion will be managed -func (o *orphanResource) GetFederatedResource(resourceClient dynamic.ResourceInterface) (*unstructured.Unstructured, error) { - resource, err := resourceClient.Get(o.resourceName, metav1.GetOptions{}) - if err != nil { - return nil, errors.Wrapf(err, "Failed to retrieve resource: %q", - ctlutil.QualifiedName{Name: o.resourceName, Namespace: o.resourceNamespace}) - } - return resource, nil -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning/status.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning/status.go deleted file mode 100644 index 15ebfd5d5cdf24fbbbe2c82959f4e1acc87478a8..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning/status.go +++ /dev/null @@ -1,95 +0,0 @@ -/* -Copyright 2019 The Kubernetes 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 orphaning - -import ( - "io" - - "github.com/spf13/cobra" - - "k8s.io/klog" - - ctlutil "sigs.k8s.io/kubefed/pkg/controller/util" - "sigs.k8s.io/kubefed/pkg/kubefedctl/util" -) - -const ( - Enabled = "Enabled" - Disabled = "Disabled" -) - -var ( - orphaning_status_long = ` - Checks the status of "orphaning enable" ('kubefed.io/orphan: true') annotation on a federated resource. - Returns "Enabled" or "Disabled" - - Current context is assumed to be a Kubernetes cluster hosting the kubefed control plane. - Please use the --host-cluster-context flag otherwise.` - - orphaning_status_example = ` - # Checks the status of the orphaning mode of a federated resource of type FederatedDeployment and named foo - kubefedctl orphaning status FederatedDeployment foo --host-cluster-context=cluster1` -) - -// newCmdStatusOrphaning checks status of orphaning deletion of the federated resource -func newCmdStatusOrphaning(cmdOut io.Writer, config util.FedConfig) *cobra.Command { - opts := &orphanResource{} - cmd := &cobra.Command{ - Use: "status ", - Short: "Get the orphaning deletion status of the federated resource", - Long: orphaning_status_long, - Example: orphaning_status_example, - Run: func(cmd *cobra.Command, args []string) { - err := opts.Complete(args, config) - if err != nil { - klog.Fatalf("Error: %v", err) - } - - err = opts.RunStatus(cmdOut, config) - if err != nil { - klog.Fatalf("Error: %v", err) - } - }, - } - - flags := cmd.Flags() - opts.GlobalSubcommandBind(flags) - err := opts.Bind(flags) - if err != nil { - klog.Fatalf("Error: %v", err) - } - - return cmd -} - -// RunStatus implements the `status` command. -func (o *orphanResource) RunStatus(cmdOut io.Writer, config util.FedConfig) error { - resourceClient, err := o.GetResourceClient(config, cmdOut) - if err != nil { - return err - } - fedResource, err := o.GetFederatedResource(resourceClient) - if err != nil { - return err - } - if ctlutil.IsOrphaningEnabled(fedResource) { - _, err = cmdOut.Write([]byte(Enabled + "\n")) - return err - } - _, err = cmdOut.Write([]byte(Disabled + "\n")) - return err -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/unjoin.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/unjoin.go deleted file mode 100644 index 7aa7a253539e248b4386de2758a301d1f3435aa4..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/unjoin.go +++ /dev/null @@ -1,442 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 kubefedctl - -import ( - "context" - goerrors "errors" - "io" - "strings" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kubeclient "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/klog" - - fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" - genericclient "sigs.k8s.io/kubefed/pkg/client/generic" - controllerutil "sigs.k8s.io/kubefed/pkg/controller/util" - "sigs.k8s.io/kubefed/pkg/kubefedctl/options" - "sigs.k8s.io/kubefed/pkg/kubefedctl/util" -) - -var ( - unjoin_long = ` - Unjoin removes the registration of a Kubernetes cluster - from a KubeFed control plane. Current context is assumed - to be a Kubernetes cluster hosting a KubeFed control - plane. Please use the --host-cluster-context flag - otherwise.` - unjoin_example = ` - # Remove the registration of a Kubernetes cluster - # from a KubeFed control plane by specifying the - # cluster name and the context name of the control - # plane's host cluster. Cluster name must be a - # valid RFC 1123 subdomain name. Cluster context - # must be specified if the cluster name is different - # than the cluster's context in the local kubeconfig. - kubefedctl unjoin foo --host-cluster-context=bar` -) - -type unjoinFederation struct { - options.GlobalSubcommandOptions - options.CommonJoinOptions - unjoinFederationOptions -} - -type unjoinFederationOptions struct { - forceDeletion bool -} - -// Bind adds the unjoin specific arguments to the flagset passed in as an -// argument. -func (o *unjoinFederationOptions) Bind(flags *pflag.FlagSet) { - flags.BoolVar(&o.forceDeletion, "force", false, - "Delete federated cluster and secret resources even if resources in the cluster targeted for unjoin are not removed successfully.") -} - -// NewCmdUnjoin defines the `unjoin` command that removes the -// registration of a cluster from a KubeFed control plane. -func NewCmdUnjoin(cmdOut io.Writer, config util.FedConfig) *cobra.Command { - opts := &unjoinFederation{} - - cmd := &cobra.Command{ - Use: "unjoin CLUSTER_NAME --host-cluster-context=HOST_CONTEXT", - Short: "Remove the registration of a cluster from a KubeFed control plane", - Long: unjoin_long, - Example: unjoin_example, - Run: func(cmd *cobra.Command, args []string) { - err := opts.Complete(args) - if err != nil { - klog.Fatalf("Error: %v", err) - } - - err = opts.Run(cmdOut, config) - if err != nil { - klog.Fatalf("Error: %v", err) - } - }, - } - - flags := cmd.Flags() - opts.GlobalSubcommandBind(flags) - opts.CommonSubcommandBind(flags) - opts.Bind(flags) - - return cmd -} - -// Complete ensures that options are valid and marshals them if necessary. -func (j *unjoinFederation) Complete(args []string) error { - err := j.SetName(args) - if err != nil { - return err - } - - if j.ClusterContext == "" { - klog.V(2).Infof("Defaulting cluster context to unjoining cluster name %s", j.ClusterName) - j.ClusterContext = j.ClusterName - } - - if j.HostClusterName != "" && strings.ContainsAny(j.HostClusterName, ":/") { - return goerrors.New("host-cluster-name may not contain \"/\" or \":\"") - } - - if j.HostClusterName == "" && strings.ContainsAny(j.HostClusterContext, ":/") { - return goerrors.New("host-cluster-name must be set if the name of the host cluster context contains one of \":\" or \"/\"") - } - - klog.V(2).Infof("Args and flags: name %s, host-cluster-context: %s, host-system-namespace: %s, kubeconfig: %s, cluster-context: %s, dry-run: %v", - j.ClusterName, j.HostClusterContext, j.KubeFedNamespace, j.Kubeconfig, j.ClusterContext, j.DryRun) - - return nil -} - -// Run is the implementation of the `unjoin` command. -func (j *unjoinFederation) Run(cmdOut io.Writer, config util.FedConfig) error { - hostClientConfig := config.GetClientConfig(j.HostClusterContext, j.Kubeconfig) - if err := j.SetHostClusterContextFromConfig(hostClientConfig); err != nil { - return err - } - - hostConfig, err := hostClientConfig.ClientConfig() - if err != nil { - // TODO(font): Return new error with this same text so it can be output - // by caller. - klog.V(2).Infof("Failed to get host cluster config: %v", err) - return err - } - - clusterConfig, err := config.ClusterConfig(j.ClusterContext, j.Kubeconfig) - if err != nil { - klog.V(2).Infof("Failed to get unjoining cluster config: %v", err) - - if !j.forceDeletion { - return err - } - // If configuration for the member cluster cannot be successfully loaded, - // forceDeletion indicates that resources associated with the member cluster - // should still be removed from the host cluster. - } - - hostClusterName := j.HostClusterContext - if j.HostClusterName != "" { - hostClusterName = j.HostClusterName - } - - return UnjoinCluster(hostConfig, clusterConfig, j.KubeFedNamespace, - hostClusterName, j.ClusterContext, j.ClusterName, j.forceDeletion, j.DryRun) -} - -// UnjoinCluster performs all the necessary steps to remove the -// registration of a cluster from a KubeFed control plane provided the -// required set of parameters are passed in. -func UnjoinCluster(hostConfig, clusterConfig *rest.Config, kubefedNamespace, hostClusterName, - unjoiningClusterContext, unjoiningClusterName string, forceDeletion, dryRun bool) error { - - hostClientset, err := util.HostClientset(hostConfig) - if err != nil { - klog.V(2).Infof("Failed to get host cluster clientset: %v", err) - return err - } - - var clusterClientset *kubeclient.Clientset - if clusterConfig != nil { - clusterClientset, err = util.ClusterClientset(clusterConfig) - if err != nil { - klog.V(2).Infof("Failed to get unjoining cluster clientset: %v", err) - if !forceDeletion { - return err - } - } - } - - client, err := genericclient.New(hostConfig) - if err != nil { - klog.V(2).Infof("Failed to get kubefed clientset: %v", err) - return err - } - - if clusterClientset != nil { - err := deleteRBACResources(clusterClientset, kubefedNamespace, unjoiningClusterName, hostClusterName, forceDeletion, dryRun) - if err != nil { - if !forceDeletion { - return err - } - klog.V(2).Infof("Failed to delete RBAC resources: %v", err) - } - - err = deleteFedNSFromUnjoinCluster(hostClientset, clusterClientset, kubefedNamespace, unjoiningClusterName, dryRun) - if err != nil { - if !forceDeletion { - return err - } - klog.V(2).Infof("Failed to delete kubefed namespace: %v", err) - } - } - - // deletionSucceeded when all operations in deleteRBACResources and deleteFedNSFromUnjoinCluster succeed. - return deleteFederatedClusterAndSecret(hostClientset, client, kubefedNamespace, unjoiningClusterName, forceDeletion, dryRun) -} - -// deleteKubeFedClusterAndSecret deletes a federated cluster resource that associates -// the cluster and secret. -func deleteFederatedClusterAndSecret(hostClientset kubeclient.Interface, client genericclient.Client, - kubefedNamespace, unjoiningClusterName string, forceDeletion, dryRun bool) error { - if dryRun { - return nil - } - - klog.V(2).Infof("Deleting kubefed cluster resource from namespace %q for unjoin cluster %q", - kubefedNamespace, unjoiningClusterName) - - fedCluster := &fedv1b1.KubeFedCluster{} - err := client.Get(context.TODO(), fedCluster, kubefedNamespace, unjoiningClusterName) - if err != nil { - return errors.Wrapf(err, "Failed to get kubefed cluster \"%s/%s\"", kubefedNamespace, unjoiningClusterName) - } - - err = hostClientset.CoreV1().Secrets(kubefedNamespace).Delete(fedCluster.Spec.SecretRef.Name, - &metav1.DeleteOptions{}) - if apierrors.IsNotFound(err) { - klog.V(2).Infof("Secret \"%s/%s\" does not exist in the host cluster.", kubefedNamespace, fedCluster.Spec.SecretRef.Name) - } else if err != nil { - wrappedErr := errors.Wrapf(err, "Failed to delete secret \"%s/%s\" for unjoin cluster %q", - kubefedNamespace, fedCluster.Spec.SecretRef.Name, unjoiningClusterName) - if !forceDeletion { - return wrappedErr - } - klog.V(2).Infof("%v", wrappedErr) - } else { - klog.V(2).Infof("Deleted secret \"%s/%s\" for unjoin cluster %q", kubefedNamespace, fedCluster.Spec.SecretRef.Name, unjoiningClusterName) - } - - err = client.Delete(context.TODO(), fedCluster, fedCluster.Namespace, fedCluster.Name) - if apierrors.IsNotFound(err) { - klog.V(2).Infof("KubeFed cluster \"%s/%s\" does not exist in the host cluster.", fedCluster.Namespace, fedCluster.Name) - } else if err != nil { - wrappedErr := errors.Wrapf(err, "Failed to delete kubefed cluster \"%s/%s\" for unjoin cluster %q", fedCluster.Namespace, fedCluster.Name, unjoiningClusterName) - if !forceDeletion { - return wrappedErr - } - klog.V(2).Infof("%v", wrappedErr) - } else { - klog.V(2).Infof("Deleted kubefed cluster \"%s/%s\" for unjoin cluster %q.", fedCluster.Namespace, fedCluster.Name, unjoiningClusterName) - } - - return nil -} - -// deleteRBACResources deletes the cluster role, cluster rolebindings and service account -// from the unjoining cluster. -func deleteRBACResources(unjoiningClusterClientset kubeclient.Interface, - namespace, unjoiningClusterName, hostClusterName string, forceDeletion, dryRun bool) error { - - saName := util.ClusterServiceAccountName(unjoiningClusterName, hostClusterName) - - err := deleteClusterRoleAndBinding(unjoiningClusterClientset, saName, namespace, unjoiningClusterName, forceDeletion, dryRun) - if err != nil { - return err - } - - err = deleteServiceAccount(unjoiningClusterClientset, saName, namespace, unjoiningClusterName, dryRun) - if err != nil { - return err - } - - return nil -} - -// deleteFedNSFromUnjoinCluster deletes the kubefed namespace from -// the unjoining cluster so long as the unjoining cluster is not the -// host cluster. -func deleteFedNSFromUnjoinCluster(hostClientset, unjoiningClusterClientset kubeclient.Interface, - kubefedNamespace, unjoiningClusterName string, dryRun bool) error { - - if dryRun { - return nil - } - - hostClusterNamespace, err := hostClientset.CoreV1().Namespaces().Get(kubefedNamespace, metav1.GetOptions{}) - if err != nil { - return errors.Wrapf(err, "Error retrieving namespace %q from host cluster", kubefedNamespace) - } - - unjoiningClusterNamespace, err := unjoiningClusterClientset.CoreV1().Namespaces().Get(kubefedNamespace, metav1.GetOptions{}) - if err != nil { - return errors.Wrapf(err, "Error retrieving namespace %q from unjoining cluster %q", kubefedNamespace, unjoiningClusterName) - } - - if controllerutil.IsPrimaryCluster(hostClusterNamespace, unjoiningClusterNamespace) { - klog.V(2).Infof("The kubefed namespace %q does not need to be deleted from the host cluster by unjoin.", kubefedNamespace) - return nil - } - - klog.V(2).Infof("Deleting kubefed namespace %q from unjoining cluster %q.", kubefedNamespace, unjoiningClusterName) - err = unjoiningClusterClientset.CoreV1().Namespaces().Delete(kubefedNamespace, &metav1.DeleteOptions{}) - if apierrors.IsNotFound(err) { - klog.V(2).Infof("The kubefed namespace %q no longer exists in unjoining cluster %q.", kubefedNamespace, unjoiningClusterName) - return nil - } else if err != nil { - return errors.Wrapf(err, "Could not delete kubefed namespace %q from unjoining cluster %q", kubefedNamespace, unjoiningClusterName) - } else { - klog.V(2).Infof("Deleted kubefed namespace %q from unjoining cluster %q.", kubefedNamespace, unjoiningClusterName) - } - - return nil -} - -// deleteServiceAccount deletes a service account in the cluster associated -// with clusterClientset with credentials that are used by the host cluster -// to access its API server. -func deleteServiceAccount(clusterClientset kubeclient.Interface, saName, - namespace, unjoiningClusterName string, dryRun bool) error { - if dryRun { - return nil - } - - klog.V(2).Infof("Deleting service account \"%s/%s\" in unjoining cluster %q.", namespace, saName, unjoiningClusterName) - - // Delete a service account. - err := clusterClientset.CoreV1().ServiceAccounts(namespace).Delete(saName, - &metav1.DeleteOptions{}) - if apierrors.IsNotFound(err) { - klog.V(2).Infof("Service account \"%s/%s\" does not exist.", namespace, saName) - } else if err != nil { - return errors.Wrapf(err, "Could not delete service account \"%s/%s\"", namespace, saName) - } else { - klog.V(2).Infof("Deleted service account \"%s/%s\" in unjoining cluster %q.", namespace, saName, unjoiningClusterName) - } - - return nil -} - -// deleteClusterRoleAndBinding deletes an RBAC cluster role and binding that -// allows the service account identified by saName to access all resources in -// all namespaces in the cluster associated with clusterClientset. -func deleteClusterRoleAndBinding(clusterClientset kubeclient.Interface, - saName, namespace, unjoiningClusterName string, forceDeletion, dryRun bool) error { - if dryRun { - return nil - } - - roleName := util.RoleName(saName) - healthCheckRoleName := util.HealthCheckRoleName(saName, namespace) - - // Attempt to delete all role and role bindings created by join - for _, name := range []string{roleName, healthCheckRoleName} { - klog.V(2).Infof("Deleting cluster role binding %q for service account %q in unjoining cluster %q.", - name, saName, unjoiningClusterName) - - err := clusterClientset.RbacV1().ClusterRoleBindings().Delete(name, &metav1.DeleteOptions{}) - if apierrors.IsNotFound(err) { - klog.V(2).Infof("Cluster role binding %q for service account %q does not exist in unjoining cluster %q.", - name, saName, unjoiningClusterName) - } else if err != nil { - wrappedErr := errors.Wrapf(err, "Could not delete cluster role binding %q for service account %q in unjoining cluster %q", - name, saName, unjoiningClusterName) - if !forceDeletion { - return wrappedErr - } - klog.V(2).Infof("%v", wrappedErr) - } else { - klog.V(2).Infof("Deleted cluster role binding %q for service account %q in unjoining cluster %q.", - name, saName, unjoiningClusterName) - } - - klog.V(2).Infof("Deleting cluster role %q for service account %q in unjoining cluster %q.", - name, saName, unjoiningClusterName) - err = clusterClientset.RbacV1().ClusterRoles().Delete(name, &metav1.DeleteOptions{}) - if apierrors.IsNotFound(err) { - klog.V(2).Infof("Cluster role %q for service account %q does not exist in unjoining cluster %q.", - name, saName, unjoiningClusterName) - } else if err != nil { - wrappedErr := errors.Wrapf(err, "Could not delete cluster role %q for service account %q in unjoining cluster %q", - name, saName, unjoiningClusterName) - if !forceDeletion { - return wrappedErr - } - klog.V(2).Infof("%v", wrappedErr) - } else { - klog.V(2).Infof("Deleted cluster role %q for service account %q in unjoining cluster %q.", - name, saName, unjoiningClusterName) - } - } - - klog.V(2).Infof("Deleting role binding \"%s/%s\" for service account %q in unjoining cluster %q.", - namespace, roleName, saName, unjoiningClusterName) - err := clusterClientset.RbacV1().RoleBindings(namespace).Delete(roleName, &metav1.DeleteOptions{}) - if apierrors.IsNotFound(err) { - klog.V(2).Infof("Role binding \"%s/%s\" for service account %q does not exist in unjoining cluster %q.", - namespace, roleName, saName, unjoiningClusterName) - } else if err != nil { - wrappedErr := errors.Wrapf(err, "Could not delete role binding \"%s/%s\" for service account %q in unjoining cluster %q", - namespace, roleName, saName, unjoiningClusterName) - if !forceDeletion { - return wrappedErr - } - klog.V(2).Infof("%v", wrappedErr) - } else { - klog.V(2).Infof("Deleted role binding \"%s/%s\" for service account %q in unjoining cluster %q.", - namespace, roleName, saName, unjoiningClusterName) - } - - klog.V(2).Infof("Deleting role \"%s/%s\" for service account %q in unjoining cluster %q.", - namespace, roleName, saName, unjoiningClusterName) - err = clusterClientset.RbacV1().Roles(namespace).Delete(roleName, &metav1.DeleteOptions{}) - if apierrors.IsNotFound(err) { - klog.V(2).Infof("Role \"%s/%s\" for service account %q does not exist in unjoining cluster %q.", - namespace, roleName, saName, unjoiningClusterName) - } else if err != nil { - wrappedErr := errors.Wrapf(err, "Could not delete role \"%s/%s\" for service account %q in unjoining cluster %q", - namespace, roleName, saName, unjoiningClusterName) - if !forceDeletion { - return wrappedErr - } - klog.V(2).Infof("%v", wrappedErr) - } else { - klog.V(2).Infof("Deleting Role \"%s/%s\" for service account %q in unjoining cluster %q.", - namespace, roleName, saName, unjoiningClusterName) - } - - return nil -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/version.go b/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/version.go deleted file mode 100644 index 56b414478eca704556540d6b3456f67b28e71c1c..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/kubefedctl/version.go +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 kubefedctl - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "sigs.k8s.io/kubefed/pkg/version" -) - -var ( - version_long = ` - Version prints the version info of this command.` - version_example = ` - # Print kubefed command version - kubefed version` -) - -// NewCmdVersion prints out the release version info for this command binary. -func NewCmdVersion(out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "version", - Short: "Print the version info", - Long: version_long, - Example: version_example, - Run: func(cmd *cobra.Command, args []string) { - fmt.Fprintf(out, "kubefedctl version: %s\n", fmt.Sprintf("%#v", version.Get())) - }, - } - - return cmd -} diff --git a/vendor/sigs.k8s.io/kubefed/pkg/version/base.go b/vendor/sigs.k8s.io/kubefed/pkg/version/base.go deleted file mode 100644 index c7a18cb23426342500b973a14419d90b34066d1e..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/version/base.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 version - -// Base version information. -// -// This is the fallback data used when version information from git is not -// provided via go ldflags (via Makefile). -var ( - version = "v0.0.1-alpha.0" // output of "git describe" - // the prerequisite is that the branch should be - // tagged using the correct versioning strategy. - - gitCommit = "unknown" // sha1 from git, output of $(git rev-parse HEAD) - gitTreeState = "unknown" // state of git tree, either "clean" or "dirty" - - buildDate = "unknown" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ') -) diff --git a/vendor/sigs.k8s.io/kubefed/pkg/version/version.go b/vendor/sigs.k8s.io/kubefed/pkg/version/version.go deleted file mode 100644 index 24a9a2ac87d77503eb2edd48aa2adee4e3d0a621..0000000000000000000000000000000000000000 --- a/vendor/sigs.k8s.io/kubefed/pkg/version/version.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -Copyright 2018 The Kubernetes 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 version - -import ( - "fmt" - "runtime" -) - -type Info struct { - Version string `json:"gitVersion"` - GitCommit string `json:"gitCommit"` - GitTreeState string `json:"gitTreeState"` - BuildDate string `json:"buildDate"` - GoVersion string `json:"goVersion"` - Compiler string `json:"compiler"` - Platform string `json:"platform"` -} - -// Get returns the overall codebase version. It's for detecting -// what code a binary was built from. -func Get() Info { - // These variables typically come from -ldflags settings and in - // their absence fallback to the settings in pkg/version/base.go - return Info{ - Version: version, - GitCommit: gitCommit, - GitTreeState: gitTreeState, - BuildDate: buildDate, - GoVersion: runtime.Version(), - Compiler: runtime.Compiler, - Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), - } -}