Support manual triggering of a repository update. (#6414)
* 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 --------- Co-authored-by: inksnw <inksnw@gmail.com>
This commit is contained in:
committed by
GitHub
parent
c9c856dfda
commit
bb60d39434
@@ -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
|
||||
|
||||
@@ -85,6 +85,7 @@ spec:
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
- syncPeriod
|
||||
- url
|
||||
type: object
|
||||
status:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: |-
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1569,6 +1569,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"}'
|
||||
@@ -1794,6 +1851,7 @@ metadata:
|
||||
kubesphere.io/managed: "true"
|
||||
iam.kubesphere.io/aggregate-to-self-provisioner: ""
|
||||
iam.kubesphere.io/aggregate-to-viewer: ""
|
||||
iam.kubesphere.io/aggregate-to-regular: ""
|
||||
name: workspace-view-app-repos
|
||||
spec:
|
||||
description:
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -119,25 +121,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
|
||||
}
|
||||
@@ -172,6 +171,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]
|
||||
@@ -188,8 +198,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
|
||||
}
|
||||
@@ -245,7 +255,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
|
||||
@@ -254,7 +264,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(),
|
||||
@@ -280,7 +290,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))
|
||||
@@ -320,11 +330,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 {
|
||||
@@ -333,39 +342,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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
203
pkg/kapis/workloadtemplate/v1alpha1/handler.go
Normal file
203
pkg/kapis/workloadtemplate/v1alpha1/handler.go
Normal 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)
|
||||
}
|
||||
89
pkg/kapis/workloadtemplate/v1alpha1/register.go
Normal file
89
pkg/kapis/workloadtemplate/v1alpha1/register.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user