mirror of
https://github.com/ysoftdevs/gardener-extension-shoot-fleet-agent.git
synced 2026-04-25 01:38:49 +02:00
Initial v1.0.0 commit
This commit is contained in:
201
vendor/sigs.k8s.io/cli-utils/LICENSE
generated
vendored
Normal file
201
vendor/sigs.k8s.io/cli-utils/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
2
vendor/sigs.k8s.io/cli-utils/LICENSE_TEMPLATE
generated
vendored
Normal file
2
vendor/sigs.k8s.io/cli-utils/LICENSE_TEMPLATE
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
Copyright {{.Year}} {{.Holder}}
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
585
vendor/sigs.k8s.io/cli-utils/pkg/kstatus/status/core.go
generated
vendored
Normal file
585
vendor/sigs.k8s.io/cli-utils/pkg/kstatus/status/core.go
generated
vendored
Normal file
@@ -0,0 +1,585 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package status
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// GetConditionsFn defines the signature for functions to compute the
|
||||
// status of a built-in resource.
|
||||
type GetConditionsFn func(*unstructured.Unstructured) (*Result, error)
|
||||
|
||||
// legacyTypes defines the mapping from GroupKind to a function that can
|
||||
// compute the status for the given resource.
|
||||
var legacyTypes = map[string]GetConditionsFn{
|
||||
"Service": serviceConditions,
|
||||
"Pod": podConditions,
|
||||
"Secret": alwaysReady,
|
||||
"PersistentVolumeClaim": pvcConditions,
|
||||
"apps/StatefulSet": stsConditions,
|
||||
"apps/DaemonSet": daemonsetConditions,
|
||||
"extensions/DaemonSet": daemonsetConditions,
|
||||
"apps/Deployment": deploymentConditions,
|
||||
"extensions/Deployment": deploymentConditions,
|
||||
"apps/ReplicaSet": replicasetConditions,
|
||||
"extensions/ReplicaSet": replicasetConditions,
|
||||
"policy/PodDisruptionBudget": pdbConditions,
|
||||
"batch/CronJob": alwaysReady,
|
||||
"ConfigMap": alwaysReady,
|
||||
"batch/Job": jobConditions,
|
||||
"apiextensions.k8s.io/CustomResourceDefinition": crdConditions,
|
||||
}
|
||||
|
||||
const (
|
||||
tooFewReady = "LessReady"
|
||||
tooFewAvailable = "LessAvailable"
|
||||
tooFewUpdated = "LessUpdated"
|
||||
tooFewReplicas = "LessReplicas"
|
||||
extraPods = "ExtraPods"
|
||||
|
||||
onDeleteUpdateStrategy = "OnDelete"
|
||||
|
||||
// How long a pod can be unscheduled before it is reported as
|
||||
// unschedulable.
|
||||
scheduleWindow = 15 * time.Second
|
||||
)
|
||||
|
||||
// GetLegacyConditionsFn returns a function that can compute the status for the
|
||||
// given resource, or nil if the resource type is not known.
|
||||
func GetLegacyConditionsFn(u *unstructured.Unstructured) GetConditionsFn {
|
||||
gvk := u.GroupVersionKind()
|
||||
g := gvk.Group
|
||||
k := gvk.Kind
|
||||
key := g + "/" + k
|
||||
if g == "" {
|
||||
key = k
|
||||
}
|
||||
return legacyTypes[key]
|
||||
}
|
||||
|
||||
// alwaysReady Used for resources that are always ready
|
||||
func alwaysReady(u *unstructured.Unstructured) (*Result, error) {
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: "Resource is always ready",
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// stsConditions return standardized Conditions for Statefulset
|
||||
//
|
||||
// StatefulSet does define the .status.conditions property, but the controller never
|
||||
// actually sets any Conditions. Thus, status must be computed only based on the other
|
||||
// properties under .status. We don't have any way to find out if a reconcile for a
|
||||
// StatefulSet has failed.
|
||||
func stsConditions(u *unstructured.Unstructured) (*Result, error) {
|
||||
obj := u.UnstructuredContent()
|
||||
|
||||
// updateStrategy==ondelete is a user managed statefulset.
|
||||
updateStrategy := GetStringField(obj, ".spec.updateStrategy.type", "")
|
||||
if updateStrategy == onDeleteUpdateStrategy {
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: "StatefulSet is using the ondelete update strategy",
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Replicas
|
||||
specReplicas := GetIntField(obj, ".spec.replicas", 1)
|
||||
readyReplicas := GetIntField(obj, ".status.readyReplicas", 0)
|
||||
currentReplicas := GetIntField(obj, ".status.currentReplicas", 0)
|
||||
updatedReplicas := GetIntField(obj, ".status.updatedReplicas", 0)
|
||||
statusReplicas := GetIntField(obj, ".status.replicas", 0)
|
||||
partition := GetIntField(obj, ".spec.updateStrategy.rollingUpdate.partition", -1)
|
||||
|
||||
if specReplicas > statusReplicas {
|
||||
message := fmt.Sprintf("Replicas: %d/%d", statusReplicas, specReplicas)
|
||||
return newInProgressStatus(tooFewReplicas, message), nil
|
||||
}
|
||||
|
||||
if specReplicas > readyReplicas {
|
||||
message := fmt.Sprintf("Ready: %d/%d", readyReplicas, specReplicas)
|
||||
return newInProgressStatus(tooFewReady, message), nil
|
||||
}
|
||||
|
||||
if statusReplicas > specReplicas {
|
||||
message := fmt.Sprintf("Pending termination: %d", statusReplicas-specReplicas)
|
||||
return newInProgressStatus(extraPods, message), nil
|
||||
}
|
||||
|
||||
// https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions
|
||||
if partition != -1 {
|
||||
if updatedReplicas < (specReplicas - partition) {
|
||||
message := fmt.Sprintf("updated: %d/%d", updatedReplicas, specReplicas-partition)
|
||||
return newInProgressStatus("PartitionRollout", message), nil
|
||||
}
|
||||
// Partition case All ok
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: fmt.Sprintf("Partition rollout complete. updated: %d", updatedReplicas),
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
if specReplicas > currentReplicas {
|
||||
message := fmt.Sprintf("current: %d/%d", currentReplicas, specReplicas)
|
||||
return newInProgressStatus("LessCurrent", message), nil
|
||||
}
|
||||
|
||||
// Revision
|
||||
currentRevision := GetStringField(obj, ".status.currentRevision", "")
|
||||
updatedRevision := GetStringField(obj, ".status.updateRevision", "")
|
||||
if currentRevision != updatedRevision {
|
||||
message := "Waiting for updated revision to match current"
|
||||
return newInProgressStatus("RevisionMismatch", message), nil
|
||||
}
|
||||
|
||||
// All ok
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: fmt.Sprintf("All replicas scheduled as expected. Replicas: %d", statusReplicas),
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// deploymentConditions return standardized Conditions for Deployment.
|
||||
//
|
||||
// For Deployments, we look at .status.conditions as well as the other properties
|
||||
// under .status. Status will be Failed if the progress deadline has been exceeded.
|
||||
func deploymentConditions(u *unstructured.Unstructured) (*Result, error) {
|
||||
obj := u.UnstructuredContent()
|
||||
|
||||
progressing := false
|
||||
|
||||
// Check if progressDeadlineSeconds is set. If not, the controller will not set
|
||||
// the `Progressing` condition, so it will always consider a deployment to be
|
||||
// progressing. The use of math.MaxInt32 is due to special handling in the
|
||||
// controller:
|
||||
// https://github.com/kubernetes/kubernetes/blob/a3ccea9d8743f2ff82e41b6c2af6dc2c41dc7b10/pkg/controller/deployment/util/deployment_util.go#L886
|
||||
progressDeadline := GetIntField(obj, ".spec.progressDeadlineSeconds", math.MaxInt32)
|
||||
if progressDeadline == math.MaxInt32 {
|
||||
progressing = true
|
||||
}
|
||||
|
||||
available := false
|
||||
|
||||
objc, err := GetObjectWithConditions(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, c := range objc.Status.Conditions {
|
||||
switch c.Type {
|
||||
case "Progressing": //appsv1.DeploymentProgressing:
|
||||
// https://github.com/kubernetes/kubernetes/blob/a3ccea9d8743f2ff82e41b6c2af6dc2c41dc7b10/pkg/controller/deployment/progress.go#L52
|
||||
if c.Reason == "ProgressDeadlineExceeded" {
|
||||
return &Result{
|
||||
Status: FailedStatus,
|
||||
Message: "Progress deadline exceeded",
|
||||
Conditions: []Condition{{ConditionStalled, corev1.ConditionTrue, c.Reason, c.Message}},
|
||||
}, nil
|
||||
}
|
||||
if c.Status == corev1.ConditionTrue && c.Reason == "NewReplicaSetAvailable" {
|
||||
progressing = true
|
||||
}
|
||||
case "Available": //appsv1.DeploymentAvailable:
|
||||
if c.Status == corev1.ConditionTrue {
|
||||
available = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// replicas
|
||||
specReplicas := GetIntField(obj, ".spec.replicas", 1) // Controller uses 1 as default if not specified.
|
||||
statusReplicas := GetIntField(obj, ".status.replicas", 0)
|
||||
updatedReplicas := GetIntField(obj, ".status.updatedReplicas", 0)
|
||||
readyReplicas := GetIntField(obj, ".status.readyReplicas", 0)
|
||||
availableReplicas := GetIntField(obj, ".status.availableReplicas", 0)
|
||||
|
||||
// TODO spec.replicas zero case ??
|
||||
|
||||
if specReplicas > statusReplicas {
|
||||
message := fmt.Sprintf("replicas: %d/%d", statusReplicas, specReplicas)
|
||||
return newInProgressStatus(tooFewReplicas, message), nil
|
||||
}
|
||||
|
||||
if specReplicas > updatedReplicas {
|
||||
message := fmt.Sprintf("Updated: %d/%d", updatedReplicas, specReplicas)
|
||||
return newInProgressStatus(tooFewUpdated, message), nil
|
||||
}
|
||||
|
||||
if statusReplicas > specReplicas {
|
||||
message := fmt.Sprintf("Pending termination: %d", statusReplicas-specReplicas)
|
||||
return newInProgressStatus(extraPods, message), nil
|
||||
}
|
||||
|
||||
if updatedReplicas > availableReplicas {
|
||||
message := fmt.Sprintf("Available: %d/%d", availableReplicas, updatedReplicas)
|
||||
return newInProgressStatus(tooFewAvailable, message), nil
|
||||
}
|
||||
|
||||
if specReplicas > readyReplicas {
|
||||
message := fmt.Sprintf("Ready: %d/%d", readyReplicas, specReplicas)
|
||||
return newInProgressStatus(tooFewReady, message), nil
|
||||
}
|
||||
|
||||
// check conditions
|
||||
if !progressing {
|
||||
message := "ReplicaSet not Available"
|
||||
return newInProgressStatus("ReplicaSetNotAvailable", message), nil
|
||||
}
|
||||
if !available {
|
||||
message := "Deployment not Available"
|
||||
return newInProgressStatus("DeploymentNotAvailable", message), nil
|
||||
}
|
||||
// All ok
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: fmt.Sprintf("Deployment is available. Replicas: %d", statusReplicas),
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// replicasetConditions return standardized Conditions for Replicaset
|
||||
func replicasetConditions(u *unstructured.Unstructured) (*Result, error) {
|
||||
obj := u.UnstructuredContent()
|
||||
|
||||
// Conditions
|
||||
objc, err := GetObjectWithConditions(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, c := range objc.Status.Conditions {
|
||||
// https://github.com/kubernetes/kubernetes/blob/a3ccea9d8743f2ff82e41b6c2af6dc2c41dc7b10/pkg/controller/replicaset/replica_set_utils.go
|
||||
if c.Type == "ReplicaFailure" && c.Status == corev1.ConditionTrue {
|
||||
message := "Replica Failure condition. Check Pods"
|
||||
return newInProgressStatus("ReplicaFailure", message), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Replicas
|
||||
specReplicas := GetIntField(obj, ".spec.replicas", 1) // Controller uses 1 as default if not specified.
|
||||
statusReplicas := GetIntField(obj, ".status.replicas", 0)
|
||||
readyReplicas := GetIntField(obj, ".status.readyReplicas", 0)
|
||||
availableReplicas := GetIntField(obj, ".status.availableReplicas", 0)
|
||||
fullyLabelledReplicas := GetIntField(obj, ".status.fullyLabeledReplicas", 0)
|
||||
|
||||
if specReplicas > fullyLabelledReplicas {
|
||||
message := fmt.Sprintf("Labelled: %d/%d", fullyLabelledReplicas, specReplicas)
|
||||
return newInProgressStatus("LessLabelled", message), nil
|
||||
}
|
||||
|
||||
if specReplicas > availableReplicas {
|
||||
message := fmt.Sprintf("Available: %d/%d", availableReplicas, specReplicas)
|
||||
return newInProgressStatus(tooFewAvailable, message), nil
|
||||
}
|
||||
|
||||
if specReplicas > readyReplicas {
|
||||
message := fmt.Sprintf("Ready: %d/%d", readyReplicas, specReplicas)
|
||||
return newInProgressStatus(tooFewReady, message), nil
|
||||
}
|
||||
|
||||
if statusReplicas > specReplicas {
|
||||
message := fmt.Sprintf("Pending termination: %d", statusReplicas-specReplicas)
|
||||
return newInProgressStatus(extraPods, message), nil
|
||||
}
|
||||
// All ok
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: fmt.Sprintf("ReplicaSet is available. Replicas: %d", statusReplicas),
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// daemonsetConditions return standardized Conditions for DaemonSet
|
||||
func daemonsetConditions(u *unstructured.Unstructured) (*Result, error) {
|
||||
obj := u.UnstructuredContent()
|
||||
|
||||
// replicas
|
||||
desiredNumberScheduled := GetIntField(obj, ".status.desiredNumberScheduled", -1)
|
||||
currentNumberScheduled := GetIntField(obj, ".status.currentNumberScheduled", 0)
|
||||
updatedNumberScheduled := GetIntField(obj, ".status.updatedNumberScheduled", 0)
|
||||
numberAvailable := GetIntField(obj, ".status.numberAvailable", 0)
|
||||
numberReady := GetIntField(obj, ".status.numberReady", 0)
|
||||
|
||||
if desiredNumberScheduled == -1 {
|
||||
message := "Missing .status.desiredNumberScheduled"
|
||||
return newInProgressStatus("NoDesiredNumber", message), nil
|
||||
}
|
||||
|
||||
if desiredNumberScheduled > currentNumberScheduled {
|
||||
message := fmt.Sprintf("Current: %d/%d", currentNumberScheduled, desiredNumberScheduled)
|
||||
return newInProgressStatus("LessCurrent", message), nil
|
||||
}
|
||||
|
||||
if desiredNumberScheduled > updatedNumberScheduled {
|
||||
message := fmt.Sprintf("Updated: %d/%d", updatedNumberScheduled, desiredNumberScheduled)
|
||||
return newInProgressStatus(tooFewUpdated, message), nil
|
||||
}
|
||||
|
||||
if desiredNumberScheduled > numberAvailable {
|
||||
message := fmt.Sprintf("Available: %d/%d", numberAvailable, desiredNumberScheduled)
|
||||
return newInProgressStatus(tooFewAvailable, message), nil
|
||||
}
|
||||
|
||||
if desiredNumberScheduled > numberReady {
|
||||
message := fmt.Sprintf("Ready: %d/%d", numberReady, desiredNumberScheduled)
|
||||
return newInProgressStatus(tooFewReady, message), nil
|
||||
}
|
||||
|
||||
// All ok
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: fmt.Sprintf("All replicas scheduled as expected. Replicas: %d", desiredNumberScheduled),
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// pvcConditions return standardized Conditions for PVC
|
||||
func pvcConditions(u *unstructured.Unstructured) (*Result, error) {
|
||||
obj := u.UnstructuredContent()
|
||||
|
||||
phase := GetStringField(obj, ".status.phase", "unknown")
|
||||
if phase != "Bound" { // corev1.ClaimBound
|
||||
message := fmt.Sprintf("PVC is not Bound. phase: %s", phase)
|
||||
return newInProgressStatus("NotBound", message), nil
|
||||
}
|
||||
// All ok
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: "PVC is Bound",
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// podConditions return standardized Conditions for Pod
|
||||
func podConditions(u *unstructured.Unstructured) (*Result, error) {
|
||||
obj := u.UnstructuredContent()
|
||||
objc, err := GetObjectWithConditions(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
phase := GetStringField(obj, ".status.phase", "")
|
||||
|
||||
switch phase {
|
||||
case "Succeeded":
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: "Pod has completed successfully",
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
case "Failed":
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: "Pod has completed, but not successfully",
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
case "Running":
|
||||
if hasConditionWithStatus(objc.Status.Conditions, "Ready", corev1.ConditionTrue) {
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: "Pod is Ready",
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
containerNames, isCrashLooping, err := getCrashLoopingContainers(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isCrashLooping {
|
||||
return newFailedStatus("ContainerCrashLooping",
|
||||
fmt.Sprintf("Containers in CrashLoop state: %s", strings.Join(containerNames, ","))), nil
|
||||
}
|
||||
|
||||
return newInProgressStatus("PodRunningNotReady", "Pod is running but is not Ready"), nil
|
||||
case "Pending":
|
||||
c, found := getConditionWithStatus(objc.Status.Conditions, "PodScheduled", corev1.ConditionFalse)
|
||||
if found && c.Reason == "Unschedulable" {
|
||||
if time.Now().Add(-scheduleWindow).Before(u.GetCreationTimestamp().Time) {
|
||||
// We give the pod 15 seconds to be scheduled before we report it
|
||||
// as unschedulable.
|
||||
return newInProgressStatus("PodNotScheduled", "Pod has not been scheduled"), nil
|
||||
}
|
||||
return newFailedStatus("PodUnschedulable", "Pod could not be scheduled"), nil
|
||||
}
|
||||
return newInProgressStatus("PodPending", "Pod is in the Pending phase"), nil
|
||||
default:
|
||||
// If the controller hasn't observed the pod yet, there is no phase. We consider this as it
|
||||
// still being in progress.
|
||||
if phase == "" {
|
||||
return newInProgressStatus("PodNotObserved", "Pod phase not available"), nil
|
||||
}
|
||||
return nil, fmt.Errorf("unknown phase %s", phase)
|
||||
}
|
||||
}
|
||||
|
||||
func getCrashLoopingContainers(obj map[string]interface{}) ([]string, bool, error) {
|
||||
var containerNames []string
|
||||
css, found, err := unstructured.NestedSlice(obj, "status", "containerStatuses")
|
||||
if !found || err != nil {
|
||||
return containerNames, found, err
|
||||
}
|
||||
for _, item := range css {
|
||||
cs := item.(map[string]interface{})
|
||||
n, found := cs["name"]
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
name := n.(string)
|
||||
s, found := cs["state"]
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
state := s.(map[string]interface{})
|
||||
|
||||
ws, found := state["waiting"]
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
waitingState := ws.(map[string]interface{})
|
||||
|
||||
r, found := waitingState["reason"]
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
reason := r.(string)
|
||||
if reason == "CrashLoopBackOff" {
|
||||
containerNames = append(containerNames, name)
|
||||
}
|
||||
}
|
||||
if len(containerNames) > 0 {
|
||||
return containerNames, true, nil
|
||||
}
|
||||
return containerNames, false, nil
|
||||
}
|
||||
|
||||
// pdbConditions computes the status for PodDisruptionBudgets. A PDB
|
||||
// is currently considered Current if the disruption controller has
|
||||
// observed the latest version of the PDB resource and has computed
|
||||
// the AllowedDisruptions. PDBs do have ObservedGeneration in the
|
||||
// Status object, so if this function gets called we know that
|
||||
// the controller has observed the latest changes.
|
||||
// The disruption controller does not set any conditions if
|
||||
// computing the AllowedDisruptions fails (and there are many ways
|
||||
// it can fail), but there is PR against OSS Kubernetes to address
|
||||
// this: https://github.com/kubernetes/kubernetes/pull/86929
|
||||
func pdbConditions(_ *unstructured.Unstructured) (*Result, error) {
|
||||
// All ok
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: "AllowedDisruptions has been computed.",
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// jobConditions return standardized Conditions for Job
|
||||
//
|
||||
// A job will have the InProgress status until it starts running. Then it will have the Current
|
||||
// status while the job is running and after it has been completed successfully. It
|
||||
// will have the Failed status if it the job has failed.
|
||||
func jobConditions(u *unstructured.Unstructured) (*Result, error) {
|
||||
obj := u.UnstructuredContent()
|
||||
|
||||
parallelism := GetIntField(obj, ".spec.parallelism", 1)
|
||||
completions := GetIntField(obj, ".spec.completions", parallelism)
|
||||
succeeded := GetIntField(obj, ".status.succeeded", 0)
|
||||
active := GetIntField(obj, ".status.active", 0)
|
||||
failed := GetIntField(obj, ".status.failed", 0)
|
||||
starttime := GetStringField(obj, ".status.startTime", "")
|
||||
|
||||
// Conditions
|
||||
// https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/job/utils.go#L24
|
||||
objc, err := GetObjectWithConditions(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, c := range objc.Status.Conditions {
|
||||
switch c.Type {
|
||||
case "Complete":
|
||||
if c.Status == corev1.ConditionTrue {
|
||||
message := fmt.Sprintf("Job Completed. succeeded: %d/%d", succeeded, completions)
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: message,
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
case "Failed":
|
||||
if c.Status == corev1.ConditionTrue {
|
||||
return newFailedStatus("JobFailed",
|
||||
fmt.Sprintf("Job Failed. failed: %d/%d", failed, completions)), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// replicas
|
||||
if starttime == "" {
|
||||
message := "Job not started"
|
||||
return newInProgressStatus("JobNotStarted", message), nil
|
||||
}
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: fmt.Sprintf("Job in progress. success:%d, active: %d, failed: %d", succeeded, active, failed),
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// serviceConditions return standardized Conditions for Service
|
||||
func serviceConditions(u *unstructured.Unstructured) (*Result, error) {
|
||||
obj := u.UnstructuredContent()
|
||||
|
||||
specType := GetStringField(obj, ".spec.type", "ClusterIP")
|
||||
specClusterIP := GetStringField(obj, ".spec.clusterIP", "")
|
||||
|
||||
if specType == "LoadBalancer" {
|
||||
if specClusterIP == "" {
|
||||
message := "ClusterIP not set. Service type: LoadBalancer"
|
||||
return newInProgressStatus("NoIPAssigned", message), nil
|
||||
}
|
||||
}
|
||||
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: "Service is ready",
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func crdConditions(u *unstructured.Unstructured) (*Result, error) {
|
||||
obj := u.UnstructuredContent()
|
||||
|
||||
objc, err := GetObjectWithConditions(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, c := range objc.Status.Conditions {
|
||||
if c.Type == "NamesAccepted" && c.Status == corev1.ConditionFalse {
|
||||
return newFailedStatus(c.Reason, c.Message), nil
|
||||
}
|
||||
if c.Type == "Established" {
|
||||
if c.Status == corev1.ConditionFalse && c.Reason != "Installing" {
|
||||
return newFailedStatus(c.Reason, c.Message), nil
|
||||
}
|
||||
if c.Status == corev1.ConditionTrue {
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: "CRD is established",
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return newInProgressStatus("Installing", "Install in progress"), nil
|
||||
}
|
||||
41
vendor/sigs.k8s.io/cli-utils/pkg/kstatus/status/doc.go
generated
vendored
Normal file
41
vendor/sigs.k8s.io/cli-utils/pkg/kstatus/status/doc.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package kstatus contains functionality for computing the status
|
||||
// of Kubernetes resources.
|
||||
//
|
||||
// The statuses defined in this package are:
|
||||
// * InProgress
|
||||
// * Current
|
||||
// * Failed
|
||||
// * Terminating
|
||||
// * Unknown
|
||||
//
|
||||
// Computing the status of a resources can be done by calling the
|
||||
// Compute function in the status package.
|
||||
//
|
||||
// import (
|
||||
// "sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
||||
// )
|
||||
//
|
||||
// res, err := status.Compute(resource)
|
||||
//
|
||||
// The package also defines a set of new conditions:
|
||||
// * InProgress
|
||||
// * Failed
|
||||
// These conditions have been chosen to follow the
|
||||
// "abnormal-true" pattern where conditions should be set to true
|
||||
// for error/abnormal conditions and the absence of a condition means
|
||||
// things are normal.
|
||||
//
|
||||
// The Augment function augments any unstructured resource with
|
||||
// the standard conditions described above. The values of
|
||||
// these conditions are decided based on other status information
|
||||
// available in the resources.
|
||||
//
|
||||
// import (
|
||||
// "sigs.k8s.io/cli-utils/pkg/kstatus/status
|
||||
// )
|
||||
//
|
||||
// err := status.Augment(resource)
|
||||
package status
|
||||
100
vendor/sigs.k8s.io/cli-utils/pkg/kstatus/status/generic.go
generated
vendored
Normal file
100
vendor/sigs.k8s.io/cli-utils/pkg/kstatus/status/generic.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package status
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// checkGenericProperties looks at the properties that are available on
|
||||
// all or most of the Kubernetes resources. If a decision can be made based
|
||||
// on this information, there is no need to look at the resource-specidic
|
||||
// rules.
|
||||
// This also checks for the presence of the conditions defined in this package.
|
||||
// If any of these are set on the resource, a decision is made solely based
|
||||
// on this and none of the resource specific rules will be used. The goal here
|
||||
// is that if controllers, built-in or custom, use these conditions, we can easily
|
||||
// find status of resources.
|
||||
func checkGenericProperties(u *unstructured.Unstructured) (*Result, error) {
|
||||
obj := u.UnstructuredContent()
|
||||
|
||||
// Check if the resource is scheduled for deletion
|
||||
deletionTimestamp, found, err := unstructured.NestedString(obj, "metadata", "deletionTimestamp")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "looking up metadata.deletionTimestamp from resource")
|
||||
}
|
||||
if found && deletionTimestamp != "" {
|
||||
return &Result{
|
||||
Status: TerminatingStatus,
|
||||
Message: "Resource scheduled for deletion",
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
res, err := checkGeneration(u)
|
||||
if res != nil || err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Check if the resource has any of the standard conditions. If so, we just use them
|
||||
// and no need to look at anything else.
|
||||
objWithConditions, err := GetObjectWithConditions(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, cond := range objWithConditions.Status.Conditions {
|
||||
if cond.Type == string(ConditionReconciling) && cond.Status == corev1.ConditionTrue {
|
||||
return newInProgressStatus(cond.Reason, cond.Message), nil
|
||||
}
|
||||
if cond.Type == string(ConditionStalled) && cond.Status == corev1.ConditionTrue {
|
||||
return &Result{
|
||||
Status: FailedStatus,
|
||||
Message: cond.Message,
|
||||
Conditions: []Condition{
|
||||
{
|
||||
Type: ConditionStalled,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: cond.Reason,
|
||||
Message: cond.Message,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func checkGeneration(u *unstructured.Unstructured) (*Result, error) {
|
||||
// ensure that the meta generation is observed
|
||||
generation, found, err := unstructured.NestedInt64(u.Object, "metadata", "generation")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "looking up metadata.generation from resource")
|
||||
}
|
||||
if !found {
|
||||
return nil, nil
|
||||
}
|
||||
observedGeneration, found, err := unstructured.NestedInt64(u.Object, "status", "observedGeneration")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "looking up status.observedGeneration from resource")
|
||||
}
|
||||
if found {
|
||||
// Resource does not have this field, so we can't do this check.
|
||||
// TODO(mortent): Verify behavior of not set vs does not exist.
|
||||
if observedGeneration != generation {
|
||||
message := fmt.Sprintf("%s generation is %d, but latest observed generation is %d", u.GetKind(), generation, observedGeneration)
|
||||
return &Result{
|
||||
Status: InProgressStatus,
|
||||
Message: message,
|
||||
Conditions: []Condition{newReconcilingCondition("LatestGenerationNotObserved", message)},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
241
vendor/sigs.k8s.io/cli-utils/pkg/kstatus/status/status.go
generated
vendored
Normal file
241
vendor/sigs.k8s.io/cli-utils/pkg/kstatus/status/status.go
generated
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package status
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
const (
|
||||
// The set of standard conditions defined in this package. These follow the "abnormality-true"
|
||||
// convention where conditions should have a true value for abnormal/error situations and the absence
|
||||
// of a condition should be interpreted as a false value, i.e. everything is normal.
|
||||
ConditionStalled ConditionType = "Stalled"
|
||||
ConditionReconciling ConditionType = "Reconciling"
|
||||
|
||||
// The set of status conditions which can be assigned to resources.
|
||||
InProgressStatus Status = "InProgress"
|
||||
FailedStatus Status = "Failed"
|
||||
CurrentStatus Status = "Current"
|
||||
TerminatingStatus Status = "Terminating"
|
||||
NotFoundStatus Status = "NotFound"
|
||||
UnknownStatus Status = "Unknown"
|
||||
)
|
||||
|
||||
var (
|
||||
Statuses = []Status{InProgressStatus, FailedStatus, CurrentStatus, TerminatingStatus, UnknownStatus}
|
||||
)
|
||||
|
||||
// ConditionType defines the set of condition types allowed inside a Condition struct.
|
||||
type ConditionType string
|
||||
|
||||
// String returns the ConditionType as a string.
|
||||
func (c ConditionType) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
// Status defines the set of statuses a resource can have.
|
||||
type Status string
|
||||
|
||||
// String returns the status as a string.
|
||||
func (s Status) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// StatusFromString turns a string into a Status. Will panic if the provided string is
|
||||
// not a valid status.
|
||||
func FromStringOrDie(text string) Status {
|
||||
s := Status(text)
|
||||
for _, r := range Statuses {
|
||||
if s == r {
|
||||
return s
|
||||
}
|
||||
}
|
||||
panic(fmt.Errorf("string has invalid status: %s", s))
|
||||
}
|
||||
|
||||
// Result contains the results of a call to compute the status of
|
||||
// a resource.
|
||||
type Result struct {
|
||||
//Status
|
||||
Status Status
|
||||
// Message
|
||||
Message string
|
||||
// Conditions list of extracted conditions from Resource
|
||||
Conditions []Condition
|
||||
}
|
||||
|
||||
// Condition defines the general format for conditions on Kubernetes resources.
|
||||
// In practice, each kubernetes resource defines their own format for conditions, but
|
||||
// most (maybe all) follows this structure.
|
||||
type Condition struct {
|
||||
// Type condition type
|
||||
Type ConditionType `json:"type,omitempty"`
|
||||
// Status String that describes the condition status
|
||||
Status corev1.ConditionStatus `json:"status,omitempty"`
|
||||
// Reason one work CamelCase reason
|
||||
Reason string `json:"reason,omitempty"`
|
||||
// Message Human readable reason string
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// Compute finds the status of a given unstructured resource. It does not
|
||||
// fetch the state of the resource from a cluster, so the provided unstructured
|
||||
// must have the complete state, including status.
|
||||
//
|
||||
// The returned result contains the status of the resource, which will be
|
||||
// one of
|
||||
// * InProgress
|
||||
// * Current
|
||||
// * Failed
|
||||
// * Terminating
|
||||
// It also contains a message that provides more information on why
|
||||
// the resource has the given status. Finally, the result also contains
|
||||
// a list of standard resources that would belong on the given resource.
|
||||
func Compute(u *unstructured.Unstructured) (*Result, error) {
|
||||
res, err := checkGenericProperties(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If res is not nil, it means the generic checks was able to determine
|
||||
// the status of the resource. We don't need to check the type-specific
|
||||
// rules.
|
||||
if res != nil {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
fn := GetLegacyConditionsFn(u)
|
||||
if fn != nil {
|
||||
return fn(u)
|
||||
}
|
||||
|
||||
// If neither the generic properties of the resource-specific rules
|
||||
// can determine status, we do one last check to see if the resource
|
||||
// does expose a Ready condition. Ready conditions do not adhere
|
||||
// to the Kubernetes design recommendations, but they are pretty widely
|
||||
// used.
|
||||
res, err = checkReadyCondition(u)
|
||||
if res != nil || err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// The resource is not one of the built-in types with specific
|
||||
// rules and we were unable to make a decision based on the
|
||||
// generic rules. In this case we assume that the absence of any known
|
||||
// conditions means the resource is current.
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: "Resource is current",
|
||||
Conditions: []Condition{},
|
||||
}, err
|
||||
}
|
||||
|
||||
// checkReadyCondition checks if a resource has a Ready condition, and
|
||||
// if so, it will use the value of this condition to determine the
|
||||
// status.
|
||||
// There are a few challenges with this:
|
||||
// - If a resource doesn't set the Ready condition until it is True,
|
||||
// the library have no way of telling whether the resource is using the
|
||||
// Ready condition, so it will fall back to the strategy for unknown
|
||||
// resources, which is to assume they are always reconciled.
|
||||
// - If the library sees the resource before the controller has had
|
||||
// a chance to update the conditions, it also will not realize the
|
||||
// resource use the Ready condition.
|
||||
// - There is no way to determine if a resource with the Ready condition
|
||||
// set to False is making progress or is doomed.
|
||||
func checkReadyCondition(u *unstructured.Unstructured) (*Result, error) {
|
||||
objWithConditions, err := GetObjectWithConditions(u.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, cond := range objWithConditions.Status.Conditions {
|
||||
if cond.Type != "Ready" {
|
||||
continue
|
||||
}
|
||||
switch cond.Status {
|
||||
case corev1.ConditionTrue:
|
||||
return &Result{
|
||||
Status: CurrentStatus,
|
||||
Message: "Resource is Ready",
|
||||
Conditions: []Condition{},
|
||||
}, nil
|
||||
case corev1.ConditionFalse:
|
||||
return newInProgressStatus(cond.Reason, cond.Message), nil
|
||||
case corev1.ConditionUnknown:
|
||||
// For now we just treat an unknown condition value as
|
||||
// InProgress. We should consider if there are better ways
|
||||
// to handle it.
|
||||
return newInProgressStatus(cond.Reason, cond.Message), nil
|
||||
default:
|
||||
// Do nothing in this case.
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Augment takes a resource and augments the resource with the
|
||||
// standard status conditions.
|
||||
func Augment(u *unstructured.Unstructured) error {
|
||||
res, err := Compute(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conditions, found, err := unstructured.NestedSlice(u.Object, "status", "conditions")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !found {
|
||||
conditions = make([]interface{}, 0)
|
||||
}
|
||||
|
||||
currentTime := time.Now().UTC().Format(time.RFC3339)
|
||||
|
||||
for _, resCondition := range res.Conditions {
|
||||
present := false
|
||||
for _, c := range conditions {
|
||||
condition, ok := c.(map[string]interface{})
|
||||
if !ok {
|
||||
return errors.New("condition does not have the expected structure")
|
||||
}
|
||||
conditionType, ok := condition["type"].(string)
|
||||
if !ok {
|
||||
return errors.New("condition type does not have the expected type")
|
||||
}
|
||||
if conditionType == string(resCondition.Type) {
|
||||
conditionStatus, ok := condition["status"].(string)
|
||||
if !ok {
|
||||
return errors.New("condition status does not have the expected type")
|
||||
}
|
||||
if conditionStatus != string(resCondition.Status) {
|
||||
condition["lastTransitionTime"] = currentTime
|
||||
}
|
||||
condition["status"] = string(resCondition.Status)
|
||||
condition["lastUpdateTime"] = currentTime
|
||||
condition["reason"] = resCondition.Reason
|
||||
condition["message"] = resCondition.Message
|
||||
present = true
|
||||
}
|
||||
}
|
||||
if !present {
|
||||
conditions = append(conditions, map[string]interface{}{
|
||||
"lastTransitionTime": currentTime,
|
||||
"lastUpdateTime": currentTime,
|
||||
"message": resCondition.Message,
|
||||
"reason": resCondition.Reason,
|
||||
"status": string(resCondition.Status),
|
||||
"type": string(resCondition.Type),
|
||||
})
|
||||
}
|
||||
}
|
||||
return unstructured.SetNestedSlice(u.Object, conditions, "status", "conditions")
|
||||
}
|
||||
143
vendor/sigs.k8s.io/cli-utils/pkg/kstatus/status/util.go
generated
vendored
Normal file
143
vendor/sigs.k8s.io/cli-utils/pkg/kstatus/status/util.go
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package status
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apiunstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// newReconcilingCondition creates an reconciling condition with the given
|
||||
// reason and message.
|
||||
func newReconcilingCondition(reason, message string) Condition {
|
||||
return Condition{
|
||||
Type: ConditionReconciling,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func newStalledCondition(reason, message string) Condition {
|
||||
return Condition{
|
||||
Type: ConditionStalled,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// newInProgressStatus creates a status Result with the InProgress status
|
||||
// and an InProgress condition.
|
||||
func newInProgressStatus(reason, message string) *Result {
|
||||
return &Result{
|
||||
Status: InProgressStatus,
|
||||
Message: message,
|
||||
Conditions: []Condition{newReconcilingCondition(reason, message)},
|
||||
}
|
||||
}
|
||||
|
||||
func newFailedStatus(reason, message string) *Result {
|
||||
return &Result{
|
||||
Status: FailedStatus,
|
||||
Message: message,
|
||||
Conditions: []Condition{newStalledCondition(reason, message)},
|
||||
}
|
||||
}
|
||||
|
||||
// ObjWithConditions Represent meta object with status.condition array
|
||||
type ObjWithConditions struct {
|
||||
// Status as expected to be present in most compliant kubernetes resources
|
||||
Status ConditionStatus `json:"status" yaml:"status"`
|
||||
}
|
||||
|
||||
// ConditionStatus represent status with condition array
|
||||
type ConditionStatus struct {
|
||||
// Array of Conditions as expected to be present in kubernetes resources
|
||||
Conditions []BasicCondition `json:"conditions" yaml:"conditions"`
|
||||
}
|
||||
|
||||
// BasicCondition fields that are expected in a condition
|
||||
type BasicCondition struct {
|
||||
// Type Condition type
|
||||
Type string `json:"type" yaml:"type"`
|
||||
// Status is one of True,False,Unknown
|
||||
Status corev1.ConditionStatus `json:"status" yaml:"status"`
|
||||
// Reason simple single word reason in CamleCase
|
||||
// +optional
|
||||
Reason string `json:"reason,omitempty" yaml:"reason"`
|
||||
// Message human readable reason
|
||||
// +optional
|
||||
Message string `json:"message,omitempty" yaml:"message"`
|
||||
}
|
||||
|
||||
// GetObjectWithConditions return typed object
|
||||
func GetObjectWithConditions(in map[string]interface{}) (*ObjWithConditions, error) {
|
||||
var out = new(ObjWithConditions)
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(in, out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func hasConditionWithStatus(conditions []BasicCondition, conditionType string, status corev1.ConditionStatus) bool {
|
||||
_, found := getConditionWithStatus(conditions, conditionType, status)
|
||||
return found
|
||||
}
|
||||
|
||||
func getConditionWithStatus(conditions []BasicCondition, conditionType string, status corev1.ConditionStatus) (BasicCondition, bool) {
|
||||
for _, c := range conditions {
|
||||
if c.Type == conditionType && c.Status == status {
|
||||
return c, true
|
||||
}
|
||||
}
|
||||
return BasicCondition{}, false
|
||||
}
|
||||
|
||||
// GetStringField return field as string defaulting to value if not found
|
||||
func GetStringField(obj map[string]interface{}, fieldPath string, defaultValue string) string {
|
||||
var rv = defaultValue
|
||||
|
||||
fields := strings.Split(fieldPath, ".")
|
||||
if fields[0] == "" {
|
||||
fields = fields[1:]
|
||||
}
|
||||
|
||||
val, found, err := apiunstructured.NestedFieldNoCopy(obj, fields...)
|
||||
if !found || err != nil {
|
||||
return rv
|
||||
}
|
||||
|
||||
if v, ok := val.(string); ok {
|
||||
return v
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
||||
// GetIntField return field as string defaulting to value if not found
|
||||
func GetIntField(obj map[string]interface{}, fieldPath string, defaultValue int) int {
|
||||
fields := strings.Split(fieldPath, ".")
|
||||
if fields[0] == "" {
|
||||
fields = fields[1:]
|
||||
}
|
||||
|
||||
val, found, err := apiunstructured.NestedFieldNoCopy(obj, fields...)
|
||||
if !found || err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
switch v := val.(type) {
|
||||
case int:
|
||||
return v
|
||||
case int32:
|
||||
return int(v)
|
||||
case int64:
|
||||
return int(v)
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
Reference in New Issue
Block a user