Support manual triggering of a repository update. (#6280)

* Support manual triggering of a repository update.

* cherry pick add api for workload template (#1982)

* cherry pick (add operator application (#1970))

* Modify routing implementation to improve readability

* cherry pick from kse dfc40e5adf5aa2e67d1

* Filter by Routing Parameter Namespace (#1990)

* add doc for workloadtemplates
This commit is contained in:
inksnw
2024-11-25 10:56:35 +08:00
committed by GitHub
parent 096e0ca868
commit bac3bc62f9
37 changed files with 679 additions and 318 deletions

View File

@@ -96,7 +96,7 @@ spec:
items:
description: |-
RawMessage is a raw encoded JSON value.
It implements Marshaler and Unmarshaler and can
It implements [Marshaler] and [Unmarshaler] and can
be used to delay JSON decoding or precompute a JSON encoding.
format: byte
type: string

View File

@@ -85,6 +85,7 @@ spec:
url:
type: string
required:
- syncPeriod
- url
type: object
status:

View File

@@ -92,16 +92,8 @@ spec:
properties:
conditions:
items:
description: "Condition contains details for one aspect of the current
state of this API Resource.\n---\nThis struct is intended for
direct use as an array at the field path .status.conditions. For
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
observations of a foo's current state.\n\t // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
\ // other fields\n\t}"
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
@@ -142,12 +134,7 @@ spec:
- Unknown
type: string
type:
description: |-
type of condition in CamelCase or in foo.example.com/CamelCase.
---
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
useful (see .node.status.conditions), the ability to deconflict is important.
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string

View File

@@ -49,16 +49,8 @@ spec:
properties:
conditions:
items:
description: "Condition contains details for one aspect of the current
state of this API Resource.\n---\nThis struct is intended for
direct use as an array at the field path .status.conditions. For
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
observations of a foo's current state.\n\t // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
\ // other fields\n\t}"
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
@@ -99,12 +91,7 @@ spec:
- Unknown
type: string
type:
description: |-
type of condition in CamelCase or in foo.example.com/CamelCase.
---
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
useful (see .node.status.conditions), the ability to deconflict is important.
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string

View File

@@ -55,10 +55,13 @@ spec:
description: The key to select.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
namespace:
type: string
@@ -81,10 +84,13 @@ spec:
a valid secret key.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
namespace:
type: string
@@ -140,16 +146,8 @@ spec:
properties:
conditions:
items:
description: "Condition contains details for one aspect of the current
state of this API Resource.\n---\nThis struct is intended for
direct use as an array at the field path .status.conditions. For
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
observations of a foo's current state.\n\t // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
\ // other fields\n\t}"
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
@@ -190,12 +188,7 @@ spec:
- Unknown
type: string
type:
description: |-
type of condition in CamelCase or in foo.example.com/CamelCase.
---
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
useful (see .node.status.conditions), the ability to deconflict is important.
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string

View File

@@ -149,16 +149,8 @@ spec:
properties:
conditions:
items:
description: "Condition contains details for one aspect of the current
state of this API Resource.\n---\nThis struct is intended for
direct use as an array at the field path .status.conditions. For
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
observations of a foo's current state.\n\t // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
\ // other fields\n\t}"
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
@@ -199,12 +191,7 @@ spec:
- Unknown
type: string
type:
description: |-
type of condition in CamelCase or in foo.example.com/CamelCase.
---
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
useful (see .node.status.conditions), the ability to deconflict is important.
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string

View File

@@ -73,11 +73,13 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string

View File

@@ -55,11 +55,13 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -109,6 +111,7 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
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
@@ -117,24 +120,28 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
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
x-kubernetes-list-type: atomic
resources:
description: Resources is a list of resources this rule applies
to. '*' represents all resources.
items:
type: string
type: array
x-kubernetes-list-type: atomic
verbs:
description: Verbs is a list of Verbs that apply to ALL the ResourceKinds
contained in this rule. '*' represents all verbs.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- verbs
type: object

View File

@@ -52,6 +52,7 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
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
@@ -60,24 +61,28 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
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
x-kubernetes-list-type: atomic
resources:
description: Resources is a list of resources this rule applies
to. '*' represents all resources.
items:
type: string
type: array
x-kubernetes-list-type: atomic
verbs:
description: Verbs is a list of Verbs that apply to ALL the ResourceKinds
contained in this rule. '*' represents all verbs.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- verbs
type: object
@@ -124,11 +129,13 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -178,6 +185,7 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
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
@@ -186,24 +194,28 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
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
x-kubernetes-list-type: atomic
resources:
description: Resources is a list of resources this rule applies
to. '*' represents all resources.
items:
type: string
type: array
x-kubernetes-list-type: atomic
verbs:
description: Verbs is a list of Verbs that apply to ALL the ResourceKinds
contained in this rule. '*' represents all verbs.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- verbs
type: object

View File

@@ -55,11 +55,13 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -109,6 +111,7 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
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
@@ -117,24 +120,28 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
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
x-kubernetes-list-type: atomic
resources:
description: Resources is a list of resources this rule applies
to. '*' represents all resources.
items:
type: string
type: array
x-kubernetes-list-type: atomic
verbs:
description: Verbs is a list of Verbs that apply to ALL the ResourceKinds
contained in this rule. '*' represents all verbs.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- verbs
type: object

View File

@@ -64,6 +64,7 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
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
@@ -72,6 +73,7 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
resourceNames:
description: ResourceNames is an optional white list of names
that the rule applies to. An empty set means that everything
@@ -79,18 +81,21 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
resources:
description: Resources is a list of resources this rule applies
to. '*' represents all resources.
items:
type: string
type: array
x-kubernetes-list-type: atomic
verbs:
description: Verbs is a list of Verbs that apply to ALL the
ResourceKinds contained in this rule. '*' represents all verbs.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- verbs
type: object

View File

@@ -59,6 +59,7 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
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
@@ -67,24 +68,28 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
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
x-kubernetes-list-type: atomic
resources:
description: Resources is a list of resources this rule applies
to. '*' represents all resources.
items:
type: string
type: array
x-kubernetes-list-type: atomic
verbs:
description: Verbs is a list of Verbs that apply to ALL the ResourceKinds
contained in this rule. '*' represents all verbs.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- verbs
type: object
@@ -139,11 +144,13 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -193,6 +200,7 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
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
@@ -201,24 +209,28 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
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
x-kubernetes-list-type: atomic
resources:
description: Resources is a list of resources this rule applies
to. '*' represents all resources.
items:
type: string
type: array
x-kubernetes-list-type: atomic
verbs:
description: Verbs is a list of Verbs that apply to ALL the ResourceKinds
contained in this rule. '*' represents all verbs.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- verbs
type: object

View File

@@ -86,18 +86,8 @@ spec:
properties:
conditions:
items:
description: "Condition contains details for one aspect of
the current state of this API Resource.\n---\nThis struct
is intended for direct use as an array at the field path
.status.conditions. For example,\n\n\n\ttype FooStatus
struct{\n\t // Represents the observations of a foo's
current state.\n\t // Known .status.conditions.type are:
\"Available\", \"Progressing\", and \"Degraded\"\n\t //
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t //
+listType=map\n\t // +listMapKey=type\n\t Conditions
[]metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\"
patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
\ // other fields\n\t}"
description: Condition contains details for one aspect of
the current state of this API Resource.
properties:
lastTransitionTime:
description: |-
@@ -139,12 +129,7 @@ spec:
- Unknown
type: string
type:
description: |-
type of condition in CamelCase or in foo.example.com/CamelCase.
---
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
useful (see .node.status.conditions), the ability to deconflict is important.
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
@@ -187,16 +172,8 @@ spec:
type: object
conditions:
items:
description: "Condition contains details for one aspect of the current
state of this API Resource.\n---\nThis struct is intended for
direct use as an array at the field path .status.conditions. For
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
observations of a foo's current state.\n\t // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
\ // other fields\n\t}"
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
@@ -237,12 +214,7 @@ spec:
- Unknown
type: string
type:
description: |-
type of condition in CamelCase or in foo.example.com/CamelCase.
---
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
useful (see .node.status.conditions), the ability to deconflict is important.
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string

View File

@@ -51,10 +51,13 @@ spec:
description: The key to select.
type: string
name:
default: ""
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?
type: string
namespace:
type: string

View File

@@ -84,11 +84,13 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -134,18 +136,8 @@ spec:
properties:
conditions:
items:
description: "Condition contains details for one aspect of
the current state of this API Resource.\n---\nThis struct
is intended for direct use as an array at the field path
.status.conditions. For example,\n\n\n\ttype FooStatus
struct{\n\t // Represents the observations of a foo's
current state.\n\t // Known .status.conditions.type are:
\"Available\", \"Progressing\", and \"Degraded\"\n\t //
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t //
+listType=map\n\t // +listMapKey=type\n\t Conditions
[]metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\"
patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
\ // other fields\n\t}"
description: Condition contains details for one aspect of
the current state of this API Resource.
properties:
lastTransitionTime:
description: |-
@@ -187,12 +179,7 @@ spec:
- Unknown
type: string
type:
description: |-
type of condition in CamelCase or in foo.example.com/CamelCase.
---
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
useful (see .node.status.conditions), the ability to deconflict is important.
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
@@ -235,16 +222,8 @@ spec:
type: object
conditions:
items:
description: "Condition contains details for one aspect of the current
state of this API Resource.\n---\nThis struct is intended for
direct use as an array at the field path .status.conditions. For
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
observations of a foo's current state.\n\t // Known .status.conditions.type
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
\ // other fields\n\t}"
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
@@ -285,12 +264,7 @@ spec:
- Unknown
type: string
type:
description: |-
type of condition in CamelCase or in foo.example.com/CamelCase.
---
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
useful (see .node.status.conditions), the ability to deconflict is important.
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string

View File

@@ -37,24 +37,8 @@ spec:
type: object
secrets:
items:
description: |-
ObjectReference contains enough information to let you inspect or modify the referred object.
---
New uses of this type are discouraged because of difficulty describing its usage when embedded in APIs.
1. Ignored fields. It includes many fields which are not generally honored. For instance, ResourceVersion and FieldPath are both very rarely valid in actual usage.
2. Invalid usage help. It is impossible to add specific help for individual usage. In most embedded usages, there are particular
restrictions like, "must refer only to types A and B" or "UID not honored" or "name must be restricted".
Those cannot be well described when embedded.
3. Inconsistent validation. Because the usages are different, the validation rules are different by usage, which makes it hard for users to predict what will happen.
4. The fields are both imprecise and overly precise. Kind is not a precise mapping to a URL. This can produce ambiguity
during interpretation and require a REST mapping. In most cases, the dependency is on the group,resource tuple
and the version of the actual struct is irrelevant.
5. We cannot easily change it. Because this type is embedded in many locations, updates to this type
will affect numerous schemas. Don't make new APIs embed an underspecified API type they do not control.
Instead of using this type, create a locally provided and used type that is well-focused on your reference.
For example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 .
description: ObjectReference contains enough information to let you
inspect or modify the referred object.
properties:
apiVersion:
description: API version of the referent.
@@ -68,7 +52,6 @@ spec:
the event) or if no container name is specified "spec.containers[2]" (container with
index 2 in this pod). This syntax is chosen only to have some well-defined way of
referencing a part of an object.
TODO: this design is not final and this field is subject to change in the future.
type: string
kind:
description: |-

View File

@@ -88,11 +88,13 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- operator
- scopeName
type: object
type: array
x-kubernetes-list-type: atomic
type: object
x-kubernetes-map-type: atomic
scopes:
@@ -104,6 +106,7 @@ spec:
match each object tracked by a quota
type: string
type: array
x-kubernetes-list-type: atomic
type: object
selector:
additionalProperties:

View File

@@ -97,11 +97,13 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -201,11 +203,13 @@ spec:
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string

View File

@@ -1565,6 +1565,63 @@ spec:
---
apiVersion: iam.kubesphere.io/v1beta1
kind: RoleTemplate
metadata:
annotations:
iam.kubesphere.io/role-template-rules: '{"workloadtemplates": "view"}'
labels:
iam.kubesphere.io/aggregate-to-operator: ""
iam.kubesphere.io/aggregate-to-viewer: ""
iam.kubesphere.io/category: namespace-configuration-management
iam.kubesphere.io/scope: "namespace"
kubesphere.io/managed: "true"
name: namespace-view-workloadtemplates
spec:
description:
en: 'View workloadtemplates in the project.'
zh: '查看项目中的工作负载模板。'
displayName:
en: workloadtemplate Viewing
zh: '工作负载模板查看'
rules:
- apiGroups:
- 'workloadtemplate.kubesphere.io'
resources:
- "*"
verbs:
- get
- list
- watch
---
apiVersion: iam.kubesphere.io/v1beta1
kind: RoleTemplate
metadata:
annotations:
iam.kubesphere.io/dependencies: '["namespace-view-workloadtemplates"]'
iam.kubesphere.io/role-template-rules: '{"workloadtemplates": "manage"}'
labels:
iam.kubesphere.io/aggregate-to-operator: ""
iam.kubesphere.io/category: namespace-configuration-management
iam.kubesphere.io/scope: "namespace"
kubesphere.io/managed: "true"
name: namespace-manage-workloadtemplates
spec:
description:
en: 'Create, edit, and delete workloadtemplates in the project.'
zh: '创建、编辑和删除项目中的工作负载模板。'
displayName:
en: workloadtemplate Management
zh: '工作负载模板管理'
rules:
- apiGroups:
- 'workloadtemplate.kubesphere.io'
resources:
- "*"
verbs:
- '*'
---
apiVersion: iam.kubesphere.io/v1beta1
kind: RoleTemplate
metadata:
annotations:
iam.kubesphere.io/role-template-rules: '{"secrets": "view"}'
@@ -1789,6 +1846,7 @@ metadata:
iam.kubesphere.io/scope: workspace
kubesphere.io/managed: "true"
iam.kubesphere.io/aggregate-to-viewer: ""
iam.kubesphere.io/aggregate-to-regular: ""
name: workspace-view-app-repos
spec:
description:

View File

@@ -64,6 +64,7 @@ import (
tenantapiv1beta1 "kubesphere.io/kubesphere/pkg/kapis/tenant/v1beta1"
terminalv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/terminal/v1alpha2"
"kubesphere.io/kubesphere/pkg/kapis/version"
workloadtemplatev1alpha1 "kubesphere.io/kubesphere/pkg/kapis/workloadtemplate/v1alpha1"
"kubesphere.io/kubesphere/pkg/models/auth"
"kubesphere.io/kubesphere/pkg/models/iam/am"
"kubesphere.io/kubesphere/pkg/models/iam/im"
@@ -186,6 +187,7 @@ func (s *APIServer) installKubeSphereAPIs() {
packagev1alpha1.NewHandler(s.RuntimeCache),
gatewayv1alpha2.NewHandler(s.RuntimeCache),
appv2.NewHandler(s.RuntimeClient, s.ClusterClient, s.S3Options),
workloadtemplatev1alpha1.NewHandler(s.RuntimeClient, s.K8sVersion, rbacAuthorizer),
static.NewHandler(s.CacheClient),
}

View File

@@ -24,6 +24,8 @@ import (
"strings"
"time"
"k8s.io/utils/ptr"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"kubesphere.io/api/constants"
@@ -120,25 +122,22 @@ func (r *RepoReconciler) UpdateStatus(ctx context.Context, helmRepo *appv2.Repo)
klog.Errorf("update status failed, error: %s", err)
return err
}
klog.Infof("update status successfully, repo: %s", helmRepo.GetName())
klog.Infof("update repo %s status: %s", helmRepo.GetName(), helmRepo.Status.State)
return nil
}
func (r *RepoReconciler) noNeedSync(ctx context.Context, helmRepo *appv2.Repo) (bool, error) {
if helmRepo.Spec.SyncPeriod == 0 {
if helmRepo.Status.State != appv2.StatusNosync {
helmRepo.Status.State = appv2.StatusNosync
klog.Infof("no sync when SyncPeriod=0, repo: %s", helmRepo.GetName())
if err := r.UpdateStatus(ctx, helmRepo); err != nil {
klog.Errorf("update status failed, error: %s", err)
return false, err
}
}
klog.Infof("no sync when SyncPeriod=0, repo: %s", helmRepo.GetName())
func (r *RepoReconciler) skipSync(helmRepo *appv2.Repo) (bool, error) {
if helmRepo.Status.State == appv2.StatusManualTrigger || helmRepo.Status.State == appv2.StatusSyncing {
klog.Infof("repo: %s state: %s", helmRepo.GetName(), helmRepo.Status.State)
return false, nil
}
if helmRepo.Spec.SyncPeriod == nil || *helmRepo.Spec.SyncPeriod == 0 {
klog.Infof("repo: %s no sync SyncPeriod=0", helmRepo.GetName())
return true, nil
}
passed := time.Since(helmRepo.Status.LastUpdateTime.Time).Seconds()
if helmRepo.Status.State == appv2.StatusSuccessful && passed < float64(helmRepo.Spec.SyncPeriod) {
if helmRepo.Status.State == appv2.StatusSuccessful && passed < float64(*helmRepo.Spec.SyncPeriod) {
klog.Infof("last sync time is %s, passed %f, no need to sync, repo: %s", helmRepo.Status.LastUpdateTime, passed, helmRepo.GetName())
return true, nil
}
@@ -173,6 +172,17 @@ func (r *RepoReconciler) Reconcile(ctx context.Context, request reconcile.Reques
klog.Errorf("get helm repo failed, error: %s", err)
return reconcile.Result{}, client.IgnoreNotFound(err)
}
if helmRepo.Status.State == "" {
helmRepo.Status.State = appv2.StatusCreated
err := r.UpdateStatus(ctx, helmRepo)
if err != nil {
return reconcile.Result{}, err
}
}
if helmRepo.Spec.SyncPeriod == nil {
helmRepo.Spec.SyncPeriod = ptr.To(0)
}
workspaceTemplate := &tenantv1beta1.WorkspaceTemplate{}
workspaceName := helmRepo.Labels[constants.WorkspaceLabelKey]
@@ -189,8 +199,8 @@ func (r *RepoReconciler) Reconcile(ctx context.Context, request reconcile.Reques
}
}
requeueAfter := time.Duration(helmRepo.Spec.SyncPeriod) * time.Second
noSync, err := r.noNeedSync(ctx, helmRepo)
requeueAfter := time.Duration(*helmRepo.Spec.SyncPeriod) * time.Second
noSync, err := r.skipSync(helmRepo)
if err != nil {
return reconcile.Result{}, err
}
@@ -246,7 +256,7 @@ func (r *RepoReconciler) Reconcile(ctx context.Context, request reconcile.Reques
versions = filterVersions(versions)
vRequests, err := repoParseRequest(r.Client, versions, helmRepo, appName)
vRequests, err := repoParseRequest(r.Client, versions, helmRepo, appName, appList)
if err != nil {
klog.Errorf("parse request failed, error: %s", err)
return reconcile.Result{}, err
@@ -255,7 +265,7 @@ func (r *RepoReconciler) Reconcile(ctx context.Context, request reconcile.Reques
continue
}
klog.Infof("found %d/%d versions for %s need to upgrade", len(vRequests), len(versions), appName)
klog.Infof("found %d/%d versions for %s need to upgrade or create", len(vRequests), len(versions), appName)
own := metav1.OwnerReference{
APIVersion: appv2.SchemeGroupVersion.String(),
@@ -281,7 +291,7 @@ func (r *RepoReconciler) Reconcile(ctx context.Context, request reconcile.Reques
return reconcile.Result{RequeueAfter: requeueAfter}, nil
}
func repoParseRequest(cli client.Client, versions helmrepo.ChartVersions, helmRepo *appv2.Repo, appName string) (result []application.AppRequest, err error) {
func repoParseRequest(cli client.Client, versions helmrepo.ChartVersions, helmRepo *appv2.Repo, appName string, appList *appv2.ApplicationList) (createOrUpdateList []application.AppRequest, err error) {
appVersionList := &appv2.ApplicationVersionList{}
appID := fmt.Sprintf("%s-%s", helmRepo.Name, application.GenerateShortNameMD5Hash(appName))
@@ -321,11 +331,10 @@ func repoParseRequest(cli client.Client, versions helmrepo.ChartVersions, helmRe
appVersionDigestMap[key] = i.Spec.Digest
}
}
var legalVersion, shortName string
for _, ver := range versions {
legalVersion := application.FormatVersion(ver.Version)
shortName := application.GenerateShortNameMD5Hash(ver.Name)
legalVersion = application.FormatVersion(ver.Version)
shortName = application.GenerateShortNameMD5Hash(ver.Name)
key := fmt.Sprintf("%s-%s-%s", helmRepo.Name, shortName, legalVersion)
dig := appVersionDigestMap[key]
if dig == ver.Digest {
@@ -334,39 +343,59 @@ func repoParseRequest(cli client.Client, versions helmrepo.ChartVersions, helmRe
if dig != "" {
klog.Infof("digest not match, key: %s, digest: %s, ver.Digest: %s", key, dig, ver.Digest)
}
vRequest := application.AppRequest{
RepoName: helmRepo.Name,
VersionName: ver.Version,
AppName: fmt.Sprintf("%s-%s", helmRepo.Name, shortName),
AliasName: appName,
OriginalName: appName,
AppHome: ver.Home,
Icon: ver.Icon,
Digest: ver.Digest,
Description: ver.Description,
Abstraction: ver.Description,
Maintainers: application.GetMaintainers(ver.Maintainers),
AppType: appv2.AppTypeHelm,
Workspace: helmRepo.GetWorkspace(),
Credential: helmRepo.Spec.Credential,
FromRepo: true,
}
url := ver.URLs[0]
methodList := []string{"https://", "http://", "s3://", "oci://"}
needContact := true
for _, method := range methodList {
if strings.HasPrefix(url, method) {
needContact = false
break
}
}
if needContact {
url = strings.TrimSuffix(helmRepo.Spec.Url, "/") + "/" + url
}
vRequest.PullUrl = url
result = append(result, vRequest)
vRequest := generateVRequest(helmRepo, ver, shortName, appName)
createOrUpdateList = append(createOrUpdateList, vRequest)
}
return result, nil
appNotFound := true
for _, i := range appList.Items {
if i.Name == appID {
appNotFound = false
break
}
}
if len(createOrUpdateList) == 0 && len(versions) > 0 && appNotFound {
//The repo source has been deleted, but the appversion has not been deleted due to the existence of the instance,
//and the appversion that is scheduled to be updated is empty
//so you need to ensure that at least one version is used to create the app
ver := versions[0]
v := generateVRequest(helmRepo, ver, shortName, appName)
createOrUpdateList = append(createOrUpdateList, v)
}
return createOrUpdateList, nil
}
func generateVRequest(helmRepo *appv2.Repo, ver *helmrepo.ChartVersion, shortName string, appName string) application.AppRequest {
vRequest := application.AppRequest{
RepoName: helmRepo.Name,
VersionName: ver.Version,
AppName: fmt.Sprintf("%s-%s", helmRepo.Name, shortName),
AliasName: appName,
OriginalName: appName,
AppHome: ver.Home,
Icon: ver.Icon,
Digest: ver.Digest,
Description: ver.Description,
Abstraction: ver.Description,
Maintainers: application.GetMaintainers(ver.Maintainers),
AppType: appv2.AppTypeHelm,
Workspace: helmRepo.GetWorkspace(),
Credential: helmRepo.Spec.Credential,
FromRepo: true,
}
url := ver.URLs[0]
methodList := []string{"https://", "http://", "s3://", "oci://"}
needContact := true
for _, method := range methodList {
if strings.HasPrefix(url, method) {
needContact = false
break
}
}
if needContact {
url = strings.TrimSuffix(helmRepo.Spec.Url, "/") + "/" + url
}
vRequest.PullUrl = url
return vRequest
}

View File

@@ -9,19 +9,13 @@ import (
"fmt"
goruntime "runtime"
"kubesphere.io/kubesphere/pkg/server/params"
"github.com/emicklei/go-restful/v3"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query"
resv1beta1 "kubesphere.io/kubesphere/pkg/models/resources/v1beta1"
)
func (h *appHandler) conflictedDone(req *restful.Request, resp *restful.Response, pathParam string, obj client.Object) bool {
@@ -74,32 +68,3 @@ func requestDone(err error, resp *restful.Response) bool {
}
return false
}
func removeQueryArg(req *restful.Request, args ...string) {
//The default filter is a whitelist, so delete some of our custom logical parameters
for _, i := range args {
q := req.Request.URL.Query()
q.Del(i)
req.Request.URL.RawQuery = q.Encode()
}
}
func convertToListResult(obj runtime.Object, req *restful.Request) (listResult api.ListResult) {
removeQueryArg(req, params.ConditionsParam, "global", "create")
_ = meta.EachListItem(obj, omitManagedFields)
queryParams := query.ParseQueryParameter(req)
list, _ := meta.ExtractList(obj)
items, _, totalCount := resv1beta1.DefaultList(list, queryParams, resv1beta1.DefaultCompare, resv1beta1.DefaultFilter)
listResult.Items = items
listResult.TotalItems = totalCount
return listResult
}
func omitManagedFields(o runtime.Object) error {
a, err := meta.Accessor(o)
if err != nil {
return err
}
a.SetManagedFields(nil)
return nil
}

View File

@@ -15,9 +15,9 @@ import (
"strings"
"time"
"kubesphere.io/kubesphere/pkg/utils/stringutils"
k8suitl "kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"kubesphere.io/kubesphere/pkg/utils/stringutils"
"github.com/emicklei/go-restful/v3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -48,11 +48,12 @@ func (h *appHandler) CreateOrUpdateApp(req *restful.Request, resp *restful.Respo
api.HandleBadRequest(resp, nil, fmt.Errorf("System has no OSS store, the maximum file size is %d", maxFileSize))
return
}
newReq, err := parseRequest(createAppRequest)
validate, _ := strconv.ParseBool(req.QueryParameter("validate"))
newReq, err := parseRequest(createAppRequest, validate)
if requestDone(err, resp) {
return
}
data := map[string]any{
"icon": newReq.Icon,
"appName": newReq.AppName,
@@ -63,7 +64,6 @@ func (h *appHandler) CreateOrUpdateApp(req *restful.Request, resp *restful.Respo
"resources": newReq.Resources,
}
validate, _ := strconv.ParseBool(req.QueryParameter("validate"))
if validate {
resp.WriteAsJson(data)
return
@@ -163,7 +163,7 @@ func (h *appHandler) ListApps(req *restful.Request, resp *restful.Response) {
filtered.Items = append(filtered.Items, curApp)
}
resp.WriteEntity(convertToListResult(&filtered, req))
resp.WriteEntity(k8suitl.ConvertToListResult(&filtered, req))
}
func (h *appHandler) DescribeApp(req *restful.Request, resp *restful.Response) {

View File

@@ -9,6 +9,8 @@ import (
"bytes"
"encoding/json"
k8suitl "kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/apiserver/request"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -55,7 +57,7 @@ func (h *appHandler) CreateOrUpdateAppRls(req *restful.Request, resp *restful.Re
}
if createRlsRequest.Spec.AppType != appv2.AppTypeHelm {
runtimeClient, _, _, err := h.getCluster(createRlsRequest.GetRlsCluster())
runtimeClient, _, _, err := h.getCluster(req, createRlsRequest.GetRlsCluster())
if requestDone(err, resp) {
return
}
@@ -111,7 +113,7 @@ func (h *appHandler) DescribeAppRls(req *restful.Request, resp *restful.Response
}
app.SetManagedFields(nil)
if app.Spec.AppType == appv2.AppTypeYaml || app.Spec.AppType == appv2.AppTypeEdge {
data, err := h.getRealTimeYaml(ctx, app)
data, err := h.getRealTimeYaml(ctx, req, app)
if err != nil {
klog.Errorf("getRealTimeYaml: %s", err.Error())
app.Status.RealTimeResources = nil
@@ -123,7 +125,7 @@ func (h *appHandler) DescribeAppRls(req *restful.Request, resp *restful.Response
return
}
data, err := h.getRealTimeHelm(ctx, app)
data, err := h.getRealTimeHelm(ctx, req, app)
if err != nil {
klog.Errorf("getRealTimeHelm: %s", err.Error())
app.Status.RealTimeResources = nil
@@ -135,8 +137,8 @@ func (h *appHandler) DescribeAppRls(req *restful.Request, resp *restful.Response
resp.WriteEntity(app)
}
func (h *appHandler) getRealTimeYaml(ctx context.Context, app *appv2.ApplicationRelease) (data []json.RawMessage, err error) {
runtimeClient, dynamicClient, cluster, err := h.getCluster(app.GetRlsCluster())
func (h *appHandler) getRealTimeYaml(ctx context.Context, req *restful.Request, app *appv2.ApplicationRelease) (data []json.RawMessage, err error) {
runtimeClient, dynamicClient, cluster, err := h.getCluster(req, app.GetRlsCluster())
if err != nil {
klog.Errorf("cluster: %s url: %s: %s", cluster.Name, cluster.Spec.Connection.KubernetesAPIEndpoint, err)
return nil, err
@@ -175,8 +177,8 @@ func (h *appHandler) getRealTimeYaml(ctx context.Context, app *appv2.Application
return data, err
}
func (h *appHandler) getRealTimeHelm(ctx context.Context, app *appv2.ApplicationRelease) (data []json.RawMessage, err error) {
runtimeClient, dynamicClient, cluster, err := h.getCluster(app.GetRlsCluster())
func (h *appHandler) getRealTimeHelm(ctx context.Context, req *restful.Request, app *appv2.ApplicationRelease) (data []json.RawMessage, err error) {
runtimeClient, dynamicClient, cluster, err := h.getCluster(req, app.GetRlsCluster())
if err != nil {
klog.Errorf("cluster: %s url: %s: %s", cluster.Name, cluster.Spec.Connection.KubernetesAPIEndpoint, err)
return nil, err
@@ -292,5 +294,5 @@ func (h *appHandler) ListAppRls(req *restful.Request, resp *restful.Response) {
return
}
resp.WriteEntity(convertToListResult(&appList, req))
resp.WriteEntity(k8suitl.ConvertToListResult(&appList, req))
}

View File

@@ -1,8 +1,3 @@
/*
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package v2
import (
@@ -11,12 +6,17 @@ import (
"strconv"
"strings"
k8suitl "kubesphere.io/kubesphere/pkg/utils/k8sutil"
"k8s.io/apimachinery/pkg/selection"
"kubesphere.io/kubesphere/pkg/utils/stringutils"
"github.com/emicklei/go-restful/v3"
"golang.org/x/net/context"
"helm.sh/helm/v3/pkg/chart/loader"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/klog/v2"
appv2 "kubesphere.io/api/application/v2"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -27,7 +27,6 @@ import (
"kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/application"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"kubesphere.io/kubesphere/pkg/utils/stringutils"
)
func (h *appHandler) CreateOrUpdateAppVersion(req *restful.Request, resp *restful.Response) {
@@ -42,7 +41,13 @@ func (h *appHandler) CreateOrUpdateAppVersion(req *restful.Request, resp *restfu
createAppVersionRequest.AppName = req.PathParameter("app")
vRequest, err := parseRequest(createAppVersionRequest)
workspace := req.PathParameter("workspace")
if workspace == "" {
workspace = appv2.SystemWorkspace
}
createAppVersionRequest.Workspace = workspace
validate, _ := strconv.ParseBool(req.QueryParameter("validate"))
vRequest, err := parseRequest(createAppVersionRequest, validate)
if requestDone(err, resp) {
return
}
@@ -55,7 +60,6 @@ func (h *appHandler) CreateOrUpdateAppVersion(req *restful.Request, resp *restfu
"aliasName": vRequest.AliasName,
}
validate, _ := strconv.ParseBool(req.QueryParameter("validate"))
if validate {
resp.WriteAsJson(data)
return
@@ -154,7 +158,7 @@ func (h *appHandler) ListAppVersions(req *restful.Request, resp *restful.Respons
filtered.Items = append(filtered.Items, appv)
}
resp.WriteEntity(convertToListResult(&filtered, req))
resp.WriteEntity(k8suitl.ConvertToListResult(&filtered, req))
}
func (h *appHandler) GetAppVersionPackage(req *restful.Request, resp *restful.Response) {
@@ -322,7 +326,7 @@ func (h *appHandler) ListReviews(req *restful.Request, resp *restful.Response) {
}
if conditions == nil || len(conditions.Match) == 0 {
resp.WriteEntity(convertToListResult(&appVersions, req))
resp.WriteEntity(k8suitl.ConvertToListResult(&appVersions, req))
return
}
@@ -335,5 +339,5 @@ func (h *appHandler) ListReviews(req *restful.Request, resp *restful.Response) {
filteredAppVersions.Items = append(filteredAppVersions.Items, version)
}
resp.WriteEntity(convertToListResult(&filteredAppVersions, req))
resp.WriteEntity(k8suitl.ConvertToListResult(&filteredAppVersions, req))
}

View File

@@ -8,6 +8,8 @@ package v2
import (
"fmt"
k8suitl "kubesphere.io/kubesphere/pkg/utils/k8sutil"
"github.com/emicklei/go-restful/v3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
@@ -99,5 +101,5 @@ func (h *appHandler) ListCategories(req *restful.Request, resp *restful.Response
if requestDone(err, resp) {
return
}
resp.WriteEntity(convertToListResult(cList, req))
resp.WriteEntity(k8suitl.ConvertToListResult(cList, req))
}

View File

@@ -9,6 +9,8 @@ import (
"fmt"
"net/url"
k8suitl "kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/simple/client/application"
"kubesphere.io/kubesphere/pkg/api"
@@ -115,6 +117,24 @@ func (h *appHandler) DeleteRepo(req *restful.Request, resp *restful.Response) {
resp.WriteEntity(errors.None)
}
func (h *appHandler) ManualSync(req *restful.Request, resp *restful.Response) {
repoId := req.PathParameter("repo")
key := runtimeclient.ObjectKey{Name: repoId}
repo := &appv2.Repo{}
err := h.client.Get(req.Request.Context(), key, repo)
if requestDone(err, resp) {
return
}
repo.Status.State = appv2.StatusManualTrigger
err = h.client.Status().Update(req.Request.Context(), repo)
if err != nil {
api.HandleInternalError(resp, nil, err)
return
}
resp.WriteEntity(errors.None)
}
func (h *appHandler) DescribeRepo(req *restful.Request, resp *restful.Response) {
repoId := req.PathParameter("repo")
@@ -146,7 +166,7 @@ func (h *appHandler) ListRepos(req *restful.Request, resp *restful.Response) {
filteredList.Items = append(filteredList.Items, repo)
}
resp.WriteEntity(convertToListResult(filteredList, req))
resp.WriteEntity(k8suitl.ConvertToListResult(filteredList, req))
}
func (h *appHandler) ListRepoEvents(req *restful.Request, resp *restful.Response) {
@@ -163,5 +183,5 @@ func (h *appHandler) ListRepoEvents(req *restful.Request, resp *restful.Response
return
}
resp.WriteEntity(convertToListResult(&list, req))
resp.WriteEntity(k8suitl.ConvertToListResult(&list, req))
}

View File

@@ -7,7 +7,8 @@ package v2
import (
"context"
"fmt"
k8suitl "kubesphere.io/kubesphere/pkg/utils/k8sutil"
"k8s.io/apimachinery/pkg/labels"
@@ -17,7 +18,6 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
appv2 "kubesphere.io/api/application/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
"kubesphere.io/kubesphere/pkg/api"
)
@@ -37,7 +37,7 @@ func (h *appHandler) AppCrList(req *restful.Request, resp *restful.Response) {
return
}
opts.LabelSelector = labelSelector.String()
_, dynamicClient, _, err := h.getCluster(clusterName)
_, dynamicClient, _, err := h.getCluster(req, clusterName)
if err != nil {
api.HandleInternalError(resp, nil, err)
return
@@ -47,41 +47,27 @@ func (h *appHandler) AppCrList(req *restful.Request, resp *restful.Response) {
api.HandleInternalError(resp, nil, err)
return
}
resp.WriteEntity(convertToListResult(list, req))
}
func checkPermissions(gvr schema.GroupVersionResource, app appv2.Application) (allow bool) {
for _, i := range app.Spec.Resources {
if gvr.Resource == i.Resource {
allow = true
break
}
}
return allow
resp.WriteEntity(k8suitl.ConvertToListResult(list, req))
}
func (h *appHandler) CreateOrUpdateCR(req *restful.Request, resp *restful.Response) {
gvr := schema.GroupVersionResource{
Group: req.QueryParameter("group"),
Version: req.QueryParameter("version"),
Resource: req.QueryParameter("resource"),
}
appID := req.QueryParameter("app")
app := appv2.Application{}
err := h.client.Get(req.Request.Context(), client.ObjectKey{Name: appID}, &app)
if err != nil {
api.HandleInternalError(resp, nil, err)
return
}
allow := checkPermissions(gvr, app)
if !allow {
api.HandleForbidden(resp, nil, fmt.Errorf("resource %s not allow", gvr.Resource))
return
}
obj := unstructured.Unstructured{}
err = req.ReadEntity(&obj)
err := req.ReadEntity(&obj)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return
}
lbs := obj.GetLabels()
if lbs == nil {
lbs = make(map[string]string)
}
lbs[appv2.AppReleaseReferenceLabelKey] = req.PathParameter("application")
obj.SetLabels(lbs)
js, err := obj.MarshalJSON()
if err != nil {
@@ -89,7 +75,7 @@ func (h *appHandler) CreateOrUpdateCR(req *restful.Request, resp *restful.Respon
return
}
clusterName := req.QueryParameter("cluster")
_, dynamicClient, _, err := h.getCluster(clusterName)
_, dynamicClient, _, err := h.getCluster(req, clusterName)
if err != nil {
api.HandleInternalError(resp, nil, err)
return
@@ -115,7 +101,7 @@ func (h *appHandler) DescribeAppCr(req *restful.Request, resp *restful.Response)
Resource: req.QueryParameter("resource"),
}
clusterName := req.QueryParameter("cluster")
_, dynamicClient, _, err := h.getCluster(clusterName)
_, dynamicClient, _, err := h.getCluster(req, clusterName)
if err != nil {
api.HandleInternalError(resp, nil, err)
return
@@ -138,7 +124,7 @@ func (h *appHandler) DeleteAppCr(req *restful.Request, resp *restful.Response) {
Resource: req.QueryParameter("resource"),
}
clusterName := req.QueryParameter("cluster")
_, dynamicClient, _, err := h.getCluster(clusterName)
_, dynamicClient, _, err := h.getCluster(req, clusterName)
if err != nil {
api.HandleInternalError(resp, nil, err)
return

View File

@@ -66,6 +66,7 @@ func (h *appHandler) AddToContainer(c *restful.Container) (err error) {
{Route: "/repos", Func: h.CreateOrUpdateRepo, Method: ws.POST, Workspace: true},
{Route: "/repos/{repo}", Func: h.CreateOrUpdateRepo, Method: ws.PATCH, Workspace: true},
{Route: "/repos/{repo}", Func: h.DeleteRepo, Method: ws.DELETE, Workspace: true},
{Route: "/repos/{repo}/action", Func: h.ManualSync, Method: ws.POST, Workspace: true},
{Route: "/repos/{repo}", Func: h.DescribeRepo, Method: ws.GET, Workspace: true},
{Route: "/repos/{repo}/events", Func: h.ListRepoEvents, Method: ws.GET, Workspace: true},
{Route: "/apps/{app}/action", Func: h.DoAppAction, Method: ws.POST},
@@ -98,10 +99,10 @@ func (h *appHandler) AddToContainer(c *restful.Container) (err error) {
{Route: "/attachments/{attachment}", Func: h.DescribeAttachment, Method: ws.GET, Workspace: true},
{Route: "/attachments/{attachment}", Func: h.DeleteAttachments, Method: ws.DELETE, Workspace: true},
{Route: "/apps/{app}/examplecr/{name}", Func: h.exampleCr, Method: ws.GET, Workspace: true},
{Route: "/apps/{app}/cr", Func: h.AppCrList, Method: ws.GET, Workspace: true, Namespace: true},
{Route: "/cr", Func: h.CreateOrUpdateCR, Method: ws.POST, Workspace: true, Namespace: true},
{Route: "/cr/{crname}", Func: h.DescribeAppCr, Method: ws.GET, Workspace: true, Namespace: true},
{Route: "/cr/{crname}", Func: h.DeleteAppCr, Method: ws.DELETE, Workspace: true, Namespace: true},
{Route: "/applications/{application}/cr", Func: h.AppCrList, Method: ws.GET, Workspace: true, Namespace: true},
{Route: "/applications/{application}/cr", Func: h.CreateOrUpdateCR, Method: ws.POST, Workspace: true, Namespace: true},
{Route: "/applications/{application}/cr/{crname}", Func: h.DescribeAppCr, Method: ws.GET, Workspace: true, Namespace: true},
{Route: "/applications/{application}/cr/{crname}", Func: h.DeleteAppCr, Method: ws.DELETE, Workspace: true, Namespace: true},
}
for _, info := range funcInfoList {
builder := info.Method(info.Route).To(info.Func).Doc(info.Doc)

View File

@@ -12,7 +12,12 @@ import (
"io"
"strings"
"github.com/emicklei/go-restful/v3"
"kubesphere.io/kubesphere/pkg/apiserver/request"
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/klog/v2"
@@ -31,9 +36,9 @@ const (
Status = "status"
)
func parseRequest(createRequest application.AppRequest) (appRequest application.AppRequest, err error) {
func parseRequest(createRequest application.AppRequest, validate bool) (appRequest application.AppRequest, err error) {
if createRequest.AppType == appv2.AppTypeHelm {
req, err := parseHelmRequest(createRequest)
req, err := parseHelmRequest(createRequest, validate)
return req, err
}
_, err = application.ReadYaml(createRequest.Package)
@@ -41,7 +46,7 @@ func parseRequest(createRequest application.AppRequest) (appRequest application.
return createRequest, err
}
func parseHelmRequest(createRequest application.AppRequest) (req application.AppRequest, err error) {
func parseHelmRequest(createRequest application.AppRequest, validate bool) (req application.AppRequest, err error) {
if createRequest.Package == nil || len(createRequest.Package) == 0 {
return req, errors.New("package is empty")
}
@@ -49,7 +54,21 @@ func parseHelmRequest(createRequest application.AppRequest) (req application.App
if err != nil {
return createRequest, err
}
if validate {
createRequest, err = getCrdInfo(createRequest, chartPack)
if err != nil {
klog.Errorf("failed to get crd info from %s: %v", chartPack.Metadata.Name, err)
return createRequest, err
}
}
shortName := application.GenerateShortNameMD5Hash(chartPack.Metadata.Name)
fillEmptyFields(&createRequest, chartPack, shortName)
return createRequest, nil
}
func getCrdInfo(createRequest application.AppRequest, chartPack *chart.Chart) (application.AppRequest, error) {
crdFiles := chartPack.CRDObjects()
for _, i := range crdFiles {
dataList, err := readYaml(i.File.Data)
@@ -83,10 +102,6 @@ func parseHelmRequest(createRequest application.AppRequest) (req application.App
createRequest.Resources = append(createRequest.Resources, ins)
}
}
shortName := application.GenerateShortNameMD5Hash(chartPack.Metadata.Name)
fillEmptyFields(&createRequest, chartPack, shortName)
return createRequest, nil
}
@@ -144,7 +159,7 @@ func fillEmptyFields(createRequest *application.AppRequest, chartPack *chart.Cha
}
}
func (h *appHandler) getCluster(clusterName string) (runtimeclient.Client, *dynamic.DynamicClient, *clusterv1alpha1.Cluster, error) {
func (h *appHandler) getCluster(req *restful.Request, clusterName string) (runtimeclient.Client, *dynamic.DynamicClient, *clusterv1alpha1.Cluster, error) {
klog.Infof("get cluster %s", clusterName)
runtimeClient, err := h.clusterClient.GetRuntimeClient(clusterName)
if err != nil {
@@ -154,7 +169,16 @@ func (h *appHandler) getCluster(clusterName string) (runtimeclient.Client, *dyna
if err != nil {
return nil, nil, nil, err
}
dynamicClient, err := dynamic.NewForConfig(clusterClient.RestConfig)
userInfo, _ := request.UserFrom(req.Request.Context())
user := ""
if userInfo != nil {
user = userInfo.GetName()
}
conf := clusterClient.RestConfig
conf.Impersonate.UserName = user
dynamicClient, err := dynamic.NewForConfig(conf)
if err != nil {
return nil, nil, nil, err
}

View File

@@ -0,0 +1,203 @@
package v1alpha1
import (
"fmt"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apiserver/pkg/authentication/user"
tenantv1beta1 "kubesphere.io/api/tenant/v1beta1"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
"k8s.io/klog/v2"
"kubesphere.io/kubesphere/pkg/apiserver/request"
"kubesphere.io/kubesphere/pkg/utils/stringutils"
"github.com/emicklei/go-restful/v3"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"sigs.k8s.io/controller-runtime/pkg/client"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/server/errors"
k8suitl "kubesphere.io/kubesphere/pkg/utils/k8sutil"
)
func (h *templateHandler) listWorkloadTemplate(req *restful.Request, resp *restful.Response) {
secretList := corev1.SecretList{}
requirements, _ := labels.SelectorFromSet(map[string]string{SchemeGroupVersion.Group: "true"}).Requirements()
userSelector := query.ParseQueryParameter(req).Selector()
combinedSelector := labels.NewSelector()
combinedSelector = combinedSelector.Add(requirements...)
if userSelector != nil {
userRequirements, _ := userSelector.Requirements()
combinedSelector = combinedSelector.Add(userRequirements...)
}
opts := []client.ListOption{
client.MatchingLabelsSelector{Selector: combinedSelector},
}
namespace := req.PathParameter("namespace")
if namespace != "" {
opts = append(opts, client.InNamespace(namespace))
}
err := h.client.List(req.Request.Context(), &secretList, opts...)
if err != nil {
api.HandleError(resp, req, err)
return
}
workspace := req.PathParameter("workspace")
if workspace == "" {
resp.WriteEntity(k8suitl.ConvertToListResult(&secretList, req))
return
}
user, ok := request.UserFrom(req.Request.Context())
if !ok {
err := fmt.Errorf("cannot obtain user info")
klog.Errorln(err)
api.HandleForbidden(resp, nil, err)
return
}
filteredList, err := h.FilterByPermissions(workspace, user, secretList)
if err != nil {
api.HandleError(resp, req, err)
return
}
resp.WriteEntity(k8suitl.ConvertToListResult(filteredList, req))
}
func (h *templateHandler) FilterByPermissions(workspace string, user user.Info, secretList corev1.SecretList) (*corev1.SecretList, error) {
listNS := authorizer.AttributesRecord{
User: user,
Verb: "list",
Workspace: workspace,
Resource: "namespaces",
ResourceRequest: true,
ResourceScope: request.WorkspaceScope,
}
decision, _, err := h.authorizer.Authorize(listNS)
if err != nil {
klog.Error(err)
return nil, err
}
var namespaceList []string
if decision == authorizer.DecisionAllow {
queryParam := query.New()
queryParam.Filters[query.FieldLabel] = query.Value(fmt.Sprintf("%s=%s", tenantv1beta1.WorkspaceLabel, workspace))
result, err := h.resourceGetter.List("namespaces", "", queryParam)
if err != nil {
klog.Error(err)
return nil, err
}
for _, item := range result.Items {
ns := item.(*corev1.Namespace)
listWorkLoadTemplate := authorizer.AttributesRecord{
User: user,
Verb: "list",
Namespace: ns.Name,
Resource: "workloadtemplates",
ResourceRequest: true,
ResourceScope: request.NamespaceScope,
}
decision, _, err = h.authorizer.Authorize(listWorkLoadTemplate)
if err != nil {
klog.Error(err)
return nil, err
}
if decision == authorizer.DecisionAllow {
namespaceList = append(namespaceList, ns.Name)
} else {
klog.Infof("user %s has no permission to list workloadtemplate in namespace %s", user.GetName(), ns.Name)
}
}
}
filteredList := &corev1.SecretList{}
for _, item := range secretList.Items {
if !stringutils.StringIn(item.Namespace, namespaceList) {
continue
}
filteredList.Items = append(filteredList.Items, item)
}
return filteredList, nil
}
func (h *templateHandler) applyWorkloadTemplate(req *restful.Request, resp *restful.Response) {
secret := &corev1.Secret{}
err := req.ReadEntity(secret)
if err != nil {
api.HandleError(resp, req, err)
return
}
if req.PathParameter("workloadtemplate") == "" {
// create new
ns := req.PathParameter("namespace")
err = h.client.Get(req.Request.Context(), runtimeclient.ObjectKey{Name: secret.Name, Namespace: ns}, secret)
if err != nil && !apierrors.IsNotFound(err) {
api.HandleError(resp, req, err)
return
}
if err == nil {
api.HandleConflict(resp, req, fmt.Errorf("workloadtemplate %s already exists", secret.Name))
return
}
}
newSecret := &corev1.Secret{}
newSecret.Name = secret.Name
newSecret.Namespace = req.PathParameter("namespace")
mutateFn := func() error {
newSecret.Annotations = secret.Annotations
if secret.Labels == nil {
secret.Labels = make(map[string]string)
}
newSecret.Labels = secret.Labels
newSecret.Labels[SchemeGroupVersion.Group] = "true"
newSecret.StringData = secret.StringData
newSecret.Type = corev1.SecretType(fmt.Sprintf("%s/%s", SchemeGroupVersion.Group, "workloadtemplate"))
return nil
}
_, err = controllerutil.CreateOrUpdate(req.Request.Context(), h.client, newSecret, mutateFn)
if err != nil {
api.HandleError(resp, req, err)
return
}
newSecret.SetManagedFields(nil)
resp.WriteAsJson(newSecret)
}
func (h *templateHandler) deleteWorkloadTemplate(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("workloadtemplate")
secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: name}}
secret.Namespace = req.PathParameter("namespace")
err := h.client.Delete(req.Request.Context(), secret)
if err != nil {
api.HandleError(resp, req, err)
return
}
resp.WriteEntity(errors.None)
}
func (h *templateHandler) getWorkloadTemplate(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("workloadtemplate")
secret := &corev1.Secret{}
ns := req.PathParameter("namespace")
err := h.client.Get(req.Request.Context(), runtimeclient.ObjectKey{Name: name, Namespace: ns}, secret)
if err != nil {
api.HandleError(resp, req, err)
return
}
secret.SetManagedFields(nil)
resp.WriteAsJson(secret)
}

View File

@@ -0,0 +1,89 @@
package v1alpha1
import (
"github.com/Masterminds/semver/v3"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
resourcesv1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource"
"github.com/emicklei/go-restful/v3"
"k8s.io/apimachinery/pkg/runtime/schema"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
"kubesphere.io/kubesphere/pkg/apiserver/rest"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
var (
SchemeGroupVersion = schema.GroupVersion{Group: "workloadtemplate.kubesphere.io", Version: "v1alpha1"}
)
type templateHandler struct {
client runtimeclient.Client
authorizer authorizer.Authorizer
resourceGetter *resourcesv1alpha3.Getter
}
func NewHandler(cacheClient runtimeclient.Client, k8sVersion *semver.Version, authorizer authorizer.Authorizer) rest.Handler {
handler := &templateHandler{
client: cacheClient,
authorizer: authorizer,
resourceGetter: resourcesv1alpha3.NewResourceGetter(cacheClient, k8sVersion),
}
return handler
}
func (h *templateHandler) AddToContainer(c *restful.Container) (err error) {
ws := runtime.NewWebService(SchemeGroupVersion)
ws.Route(ws.GET("/workloadtemplates").
To(h.listWorkloadTemplate).
Doc("List workload templates").
Notes("List workload templates.").
Operation("listWorkloadTemplate"))
ws.Route(ws.GET("/workspaces/{workspace}/workloadtemplates").
To(h.listWorkloadTemplate).
Doc("List workload templates in a workspace").
Notes("List workload templates in a workspace.").
Operation("listWorkloadTemplate").
Param(ws.PathParameter("workspace", "workspace")))
ws.Route(ws.GET("/namespaces/{namespace}/workloadtemplates").
To(h.listWorkloadTemplate).
Doc("List workload templates in a namespace").
Notes("List workload templates in a namespace.").
Operation("listWorkloadTemplate").
Param(ws.PathParameter("namespace", "namespace")))
ws.Route(ws.POST("/namespaces/{namespace}/workloadtemplates").
To(h.applyWorkloadTemplate).
Doc("Apply a workload template in a namespace").
Notes("Apply a workload template in a namespace.").
Operation("applyWorkloadTemplate").
Param(ws.PathParameter("namespace", "namespace")))
ws.Route(ws.PUT("/namespaces/{namespace}/workloadtemplates/{workloadtemplate}").
To(h.applyWorkloadTemplate).
Doc("Update a workload template").
Notes("Update a workload template in a namespace.").
Operation("applyWorkloadTemplate").
Param(ws.PathParameter("namespace", "namespace")).
Param(ws.PathParameter("workloadtemplate", "workloadtemplate")))
ws.Route(ws.DELETE("/namespaces/{namespace}/workloadtemplates/{workloadtemplate}").
To(h.deleteWorkloadTemplate).
Doc("Delete a workload template in a namespace").
Notes("List workload templates in a namespace.").
Operation("deleteWorkloadTemplate").
Param(ws.PathParameter("namespace", "namespace")).
Param(ws.PathParameter("workloadtemplate", "workloadtemplate")))
ws.Route(ws.GET("/namespaces/{namespace}/workloadtemplates/{workloadtemplate}").
To(h.getWorkloadTemplate).
Doc("Get a workload template in a namespace").
Notes("Get a workload template in a namespace.").
Operation("getWorkloadTemplate").
Param(ws.PathParameter("namespace", "namespace")).
Param(ws.PathParameter("workloadtemplate", "workloadtemplate")))
c.Add(ws)
return nil
}

View File

@@ -91,6 +91,7 @@ type AppRequest struct {
Abstraction string `json:"abstraction,omitempty"`
Attachments []string `json:"attachments,omitempty"`
FromRepo bool `json:"fromRepo,omitempty"`
HasCrd string `json:"hasCrd,omitempty"`
Resources []appv2.GroupVersionResource `json:"resources,omitempty"`
}
@@ -123,6 +124,7 @@ func CreateOrUpdateApp(client runtimeclient.Client, vRequests []AppRequest, cmSt
AppType: request.AppType,
Abstraction: request.Abstraction,
Attachments: request.Attachments,
Resources: request.Resources,
}
if len(owns) > 0 {
app.OwnerReferences = owns
@@ -134,6 +136,7 @@ func CreateOrUpdateApp(client runtimeclient.Client, vRequests []AppRequest, cmSt
}
labels[appv2.RepoIDLabelKey] = request.RepoName
labels[appv2.AppTypeLabelKey] = request.AppType
labels[appv2.HasCrdLabelKey] = request.HasCrd
if request.CategoryName != "" {
labels[appv2.AppCategoryNameKey] = request.CategoryName

View File

@@ -6,10 +6,17 @@
package k8sutil
import (
"github.com/emicklei/go-restful/v3"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
tenantv1beta1 "kubesphere.io/api/tenant/v1beta1"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query"
resv1beta1 "kubesphere.io/kubesphere/pkg/models/resources/v1beta1"
)
// IsControlledBy returns whether the ownerReferences contains the specified resource kind
@@ -78,3 +85,23 @@ func GetObjectMeta(obj metav1.Object) metav1.ObjectMeta {
ManagedFields: obj.GetManagedFields(),
}
}
func ConvertToListResult(obj runtime.Object, req *restful.Request) (listResult api.ListResult) {
_ = meta.EachListItem(obj, omitManagedFields)
queryParams := query.ParseQueryParameter(req)
list, _ := meta.ExtractList(obj)
items, _, totalCount := resv1beta1.DefaultList(list, queryParams, resv1beta1.DefaultCompare, resv1beta1.DefaultFilter)
listResult.Items = items
listResult.TotalItems = totalCount
return listResult
}
func omitManagedFields(o runtime.Object) error {
a, err := meta.Accessor(o)
if err != nil {
return err
}
a.SetManagedFields(nil)
return nil
}

View File

@@ -7,6 +7,7 @@ const (
AppOriginalNameLabelKey = "application.kubesphere.io/app-originalName"
AppVersionIDLabelKey = "application.kubesphere.io/appversion-id"
AppTypeLabelKey = "application.kubesphere.io/app-type"
HasCrdLabelKey = "application.kubesphere.io/hascrd"
AppStoreLabelKey = "application.kubesphere.io/app-store"
TimeoutRecheck = "application.kubesphere.io/timeout-recheck"
AppCategoryNameKey = "application.kubesphere.io/app-category-name"
@@ -18,6 +19,7 @@ const (
StatusSuccessful = "successful"
StatusCreating = "creating"
StatusSyncing = "syncing"
StatusManualTrigger = "manualTrigger"
StatusDeleting = "deleting"
StatusUpgrading = "upgrading"
StatusClusterDeleted = "clusterDeleted"

View File

@@ -26,7 +26,7 @@ type RepoSpec struct {
Url string `json:"url"`
Credential RepoCredential `json:"credential,omitempty"`
Description string `json:"description,omitempty"`
SyncPeriod int `json:"syncPeriod,omitempty"`
SyncPeriod *int `json:"syncPeriod"`
}
// RepoStatus defines the observed state of Repo

View File

@@ -542,6 +542,11 @@ func (in *RepoList) DeepCopyObject() runtime.Object {
func (in *RepoSpec) DeepCopyInto(out *RepoSpec) {
*out = *in
in.Credential.DeepCopyInto(&out.Credential)
if in.SyncPeriod != nil {
in, out := &in.SyncPeriod, &out.SyncPeriod
*out = new(int)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RepoSpec.