add RolloutHistory controller

Signed-off-by: yike21 <yike21@qq.com>
This commit is contained in:
yike21 2022-10-21 20:20:11 +08:00
parent f4269ce618
commit 219cbb0965
14 changed files with 1999 additions and 55 deletions

View File

@ -92,22 +92,12 @@ type RolloutHistoryStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
// Phase indicates phase of RolloutHistory, such as "pending", "updated", "completed"
// Phase indicates phase of RolloutHistory, just "" or "completed"
Phase string `json:"phase,omitempty"`
// RolloutState indicates the rollouts status
RolloutState RolloutState `json:"rolloutState,omitempty"`
// CanarySteps indicates the pods released each step
CanarySteps []CanaryStepInfo `json:"canarySteps,omitempty"`
}
// RolloutState indicates the rollouts status
type RolloutState struct {
// RolloutPhase is the rollout phase.
RolloutPhase RolloutPhase `json:"phase,omitempty"`
// Message provides details on why the rollout is in its current phase
Message string `json:"message,omitempty"`
}
// CanaryStepInfo indicates the pods for a revision
type CanaryStepInfo struct {
// CanaryStepIndex indicates step this revision
@ -129,16 +119,11 @@ type Pod struct {
// State string `json:"state, omitempty"`
}
// Phase indicates rollouthistory status/phase
// Phase indicates rollouthistory phase
const (
PhasePending string = "pending"
PhaseUpdated string = "updated"
PhaseCompleted string = "completed"
)
// MaxRolloutHistoryNum indicates how many rollouthistories there can be at most
const MaxRolloutHistoryNum int = 10
// +genclient
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status

View File

@ -536,7 +536,6 @@ func (in *RolloutHistorySpec) DeepCopy() *RolloutHistorySpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RolloutHistoryStatus) DeepCopyInto(out *RolloutHistoryStatus) {
*out = *in
out.RolloutState = in.RolloutState
if in.CanarySteps != nil {
in, out := &in.CanarySteps, &out.CanarySteps
*out = make([]CanaryStepInfo, len(*in))
@ -641,21 +640,6 @@ func (in *RolloutSpec) DeepCopy() *RolloutSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RolloutState) DeepCopyInto(out *RolloutState) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutState.
func (in *RolloutState) DeepCopy() *RolloutState {
if in == nil {
return nil
}
out := new(RolloutState)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RolloutStatus) DeepCopyInto(out *RolloutStatus) {
*out = *in

View File

@ -160,20 +160,8 @@ spec:
type: object
type: array
phase:
description: Phase indicates phase of RolloutHistory, such as "pending",
"updated", "completed"
description: Phase indicates phase of RolloutHistory, just "" or "completed"
type: string
rolloutState:
description: RolloutState indicates the rollouts status
properties:
message:
description: Message provides details on why the rollout is in
its current phase
type: string
phase:
description: RolloutPhase is the rollout phase.
type: string
type: object
type: object
type: object
served: true

View File

@ -4,6 +4,7 @@
resources:
- bases/rollouts.kruise.io_rollouts.yaml
- bases/rollouts.kruise.io_batchreleases.yaml
- bases/rollouts.kruise.io_rollouthistories.yaml
#+kubebuilder:scaffold:crdkustomizeresource
patchesStrategicMerge:

View File

@ -262,6 +262,32 @@ rules:
- get
- patch
- update
- apiGroups:
- rollouts.kruise.io
resources:
- rollouthistories
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- rollouts.kruise.io
resources:
- rollouthistories/finalizers
verbs:
- update
- apiGroups:
- rollouts.kruise.io
resources:
- rollouthistories/status
verbs:
- get
- patch
- update
- apiGroups:
- rollouts.kruise.io
resources:

2
go.mod
View File

@ -7,11 +7,13 @@ require (
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.17.0
github.com/openkruise/kruise-api v1.0.0
github.com/spf13/pflag v1.0.5
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.22.6
k8s.io/apiextensions-apiserver v0.22.6
k8s.io/apimachinery v0.22.6
k8s.io/client-go v0.22.6
k8s.io/component-base v0.22.6
k8s.io/klog/v2 v2.10.0
k8s.io/utils v0.0.0-20210820185131-d34e5cb4466e
sigs.k8s.io/controller-runtime v0.10.3

8
go.sum
View File

@ -404,8 +404,6 @@ github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/openkruise/kruise-api v1.0.0 h1:ScA0LxRRNBsgbcyLhTzR9B+KpGNWsIMptzzmjTqfYQo=
github.com/openkruise/kruise-api v1.0.0/go.mod h1:kxV/UA/vrf/hz3z+kL21c0NOawC6K1ZjaKcJFgiOwsE=
github.com/openkruise/kruise-api v1.2.0 h1:MhoQtYT2tRdjrpb51xhn3lhEDWSlRGiMYQQ0Sh3zCkk=
github.com/openkruise/kruise-api v1.2.0/go.mod h1:BKMffjLFufZkj/yVpF5TjXG9gMU3Y9A3FxrVOJ5LJUI=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@ -976,7 +974,6 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.20.10/go.mod h1:0kei3F6biGjtRQBo5dUeujq6Ji3UCh9aOSfp/THYd7I=
k8s.io/api v0.20.15/go.mod h1:X3JDf1BiTRQQ6xNAxTuhgi6yL2dHc6fSr9LGzE+Z3YU=
k8s.io/api v0.21.3/go.mod h1:hUgeYHUbBp23Ue4qdX9tR8/ANi/g3ehylAqDn9NWVOg=
k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY=
k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8=
@ -987,7 +984,6 @@ k8s.io/apiextensions-apiserver v0.22.2/go.mod h1:2E0Ve/isxNl7tWLSUDgi6+cmwHi5fQR
k8s.io/apiextensions-apiserver v0.22.6 h1:TH+9+EGtoVzzbrlfSDnObzFTnyXKqw1NBfT5XFATeJI=
k8s.io/apiextensions-apiserver v0.22.6/go.mod h1:wNsLwy8mfIkGThiv4Qq/Hy4qRazViKXqmH5pfYiRKyY=
k8s.io/apimachinery v0.20.10/go.mod h1:kQa//VOAwyVwJ2+L9kOREbsnryfsGSkSM1przND4+mw=
k8s.io/apimachinery v0.20.15/go.mod h1:4KFiDSxCoGviCiRk9kTXIROsIf4VSGkVYjVJjJln3pg=
k8s.io/apimachinery v0.21.3/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI=
k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
@ -997,14 +993,12 @@ k8s.io/apiserver v0.21.3/go.mod h1:eDPWlZG6/cCCMj/JBcEpDoK+I+6i3r9GsChYBHSbAzU=
k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI=
k8s.io/apiserver v0.22.6/go.mod h1:OlL1rGa2kKWGj2JEXnwBcul/BwC9Twe95gm4ohtiIIs=
k8s.io/client-go v0.20.10/go.mod h1:fFg+aLoasv/R+xiVaWjxeqGFYltzgQcOQzkFaSRfnJ0=
k8s.io/client-go v0.20.15/go.mod h1:q/vywQFfGT3jw+lXQGA9sEJDH0QEX7XUT2PwrQ2qm/I=
k8s.io/client-go v0.21.3/go.mod h1:+VPhCgTsaFmGILxR/7E1N0S+ryO010QBeNCv5JwRGYU=
k8s.io/client-go v0.22.1/go.mod h1:BquC5A4UOo4qVDUtoc04/+Nxp1MeHcVc1HJm1KmG8kk=
k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U=
k8s.io/client-go v0.22.6 h1:ugAXeC312xeGXsn7zTRz+btgtLBnW3qYhtUUpVQL7YE=
k8s.io/client-go v0.22.6/go.mod h1:TffU4AV2idZGeP+g3kdFZP+oHVHWPL1JYFySOALriw0=
k8s.io/code-generator v0.20.10/go.mod h1:i6FmG+QxaLxvJsezvZp0q/gAEzzOz3U53KFibghWToU=
k8s.io/code-generator v0.20.15/go.mod h1:MW85KuhTjX9nzhFYpRqUOYh4et0xeEBHTEjwBzFYGaM=
k8s.io/code-generator v0.21.3/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo=
k8s.io/code-generator v0.22.0/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=
k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=
@ -1031,8 +1025,6 @@ k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iL
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c h1:jvamsI1tn9V0S8jicyX82qaFC0H/NKxv2e5mbqsgR80=
k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
k8s.io/kube-openapi v0.0.0-20211110013926-83f114cd0513 h1:pbudjNtv90nOgR0/DUhPwKHnQ55Khz8+sNhJBIK7A5M=
k8s.io/kube-openapi v0.0.0-20211110013926-83f114cd0513/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=

12
main.go
View File

@ -25,9 +25,12 @@ import (
rolloutsv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
br "github.com/openkruise/rollouts/pkg/controller/batchrelease"
"github.com/openkruise/rollouts/pkg/controller/rollout"
"github.com/openkruise/rollouts/pkg/controller/rollouthistory"
"github.com/openkruise/rollouts/pkg/util"
utilclient "github.com/openkruise/rollouts/pkg/util/client"
utilfeature "github.com/openkruise/rollouts/pkg/util/feature"
"github.com/openkruise/rollouts/pkg/webhook"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
@ -66,8 +69,10 @@ func main() {
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
utilfeature.DefaultMutableFeatureGate.AddFlag(pflag.CommandLine)
klog.InitFlags(nil)
flag.Parse()
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()
ctrl.SetLogger(klogr.New())
cfg := ctrl.GetConfigOrDie()
@ -109,6 +114,11 @@ func main() {
os.Exit(1)
}
if err = rollouthistory.Add(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "rollouthistory")
os.Exit(1)
}
//+kubebuilder:scaffold:builder
setupLog.Info("setup webhook")
if err = webhook.SetupWithManager(mgr); err != nil {

View File

@ -0,0 +1,395 @@
/*
Copyright 2022 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rollouthistory
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"reflect"
rolloutv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
"github.com/openkruise/rollouts/pkg/feature"
"github.com/openkruise/rollouts/pkg/util"
utilfeature "github.com/openkruise/rollouts/pkg/util/feature"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/klog/v2"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
"sigs.k8s.io/gateway-api/apis/v1alpha2"
)
var (
concurrentReconciles = 3
)
func init() {
flag.IntVar(&concurrentReconciles, "rollouthistory-workers", 3, "Max concurrent workers for rolloutHistory controller.")
}
// Add creates a new Rollout Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
if utilfeature.DefaultFeatureGate.Enabled(feature.RolloutHistoryGate) {
return add(mgr, newReconciler(mgr))
}
return nil
}
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &RolloutHistoryReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Finder: newControllerFinder2(mgr.GetClient()),
}
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("rollouthistory-controller", mgr, controller.Options{
Reconciler: r, MaxConcurrentReconciles: concurrentReconciles})
if err != nil {
return err
}
// Watch for changes to rollout
if err = c.Watch(&source.Kind{Type: &rolloutv1alpha1.Rollout{}}, &enqueueRequestForRollout{}); err != nil {
return err
}
// watch for changes to rolloutHistory
if err = c.Watch(&source.Kind{Type: &rolloutv1alpha1.RolloutHistory{}}, &enqueueRequestForRolloutHistory{}); err != nil {
return err
}
return nil
}
// RolloutHistoryReconciler reconciles a RolloutHistory object
type RolloutHistoryReconciler struct {
client.Client
Scheme *runtime.Scheme
Finder *controllerFinder2
}
//+kubebuilder:rbac:groups=rollouts.kruise.io,resources=rollouthistories,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=rollouts.kruise.io,resources=rollouthistories/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=rollouts.kruise.io,resources=rollouthistories/finalizers,verbs=update
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the RolloutHistory object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.10.0/pkg/reconcile
func (r *RolloutHistoryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// get rollout
rollout := &rolloutv1alpha1.Rollout{}
err := r.Get(ctx, req.NamespacedName, rollout)
if err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
klog.Infof("Begin to reconcile Rollout %v", klog.KObj(rollout))
// ignore rollout without ObservedRolloutID
if rollout.Status.CanaryStatus == nil ||
rollout.Status.CanaryStatus.ObservedRolloutID == "" {
return ctrl.Result{}, nil
}
// get RolloutHistory which is not completed and related to the rollout (only one or zero)
var rolloutHistory *rolloutv1alpha1.RolloutHistory
if rolloutHistory, err = r.getRolloutHistoryForRollout(rollout); err != nil {
klog.Errorf("get rollout(%s/%s) rolloutHistory(%s=%s) failed: %s", rollout.Namespace, rollout.Name, rolloutIDLabel, rollout.Status.CanaryStatus.ObservedRolloutID, err.Error())
return ctrl.Result{}, err
}
// create a rolloutHistory when user does a new rollout
if rolloutHistory == nil {
// just create rolloutHistory for rollouts which are progressing, otherwise it's possible to create more than one rollouthistory when user does one rollout
if rollout.Status.Phase != rolloutv1alpha1.RolloutPhaseProgressing {
return ctrl.Result{}, nil
}
if err = r.createRolloutHistoryForProgressingRollout(rollout); err != nil {
klog.Errorf("create rollout(%s/%s) rolloutHistory(%s=%s) failed: %s", rollout.Namespace, rollout.Name, rolloutIDLabel, rollout.Status.CanaryStatus.ObservedRolloutID, err.Error())
return ctrl.Result{}, err
}
klog.Infof("create rollout(%s/%s) rolloutHistory success", rollout.Namespace, rollout.Name)
return ctrl.Result{}, nil
}
klog.Infof("get rollout(%s/%s) rolloutHistory(%s) success", rollout.Namespace, rollout.Name, rolloutHistory.Name)
// update RolloutHistory which is waiting for rollout completed
if rolloutHistory.Status.Phase != rolloutv1alpha1.PhaseCompleted {
// update RolloutHistory when rollout .status.phase is equl to RolloutPhaseHealthy
if err = r.updateRolloutHistoryWhenRolloutIsCompeleted(rollout, rolloutHistory); err != nil {
klog.Errorf("update rollout(%s/%s) rolloutHistory(%s=%s) failed: %s", rollout.Namespace, rollout.Name, rolloutIDLabel, rollout.Status.CanaryStatus.ObservedRolloutID, err.Error())
return ctrl.Result{}, err
}
// update rollouthistory success
klog.Infof("update rollout(%s/%s) rolloutHistory(%s) success", rollout.Namespace, rollout.Name, rolloutHistory.Name)
return ctrl.Result{}, nil
}
return ctrl.Result{}, nil
}
// getRolloutHistoryForRollout get rolloutHistory according to rolloutID and rolloutName for this new rollout.
func (r *RolloutHistoryReconciler) getRolloutHistoryForRollout(rollout *rolloutv1alpha1.Rollout) (*rolloutv1alpha1.RolloutHistory, error) {
// get labelSelector including rolloutBathID, rolloutID
lableSelectorString := fmt.Sprintf("%v=%v,%v=%v", rolloutIDLabel, rollout.Status.CanaryStatus.ObservedRolloutID, rolloutNameLabel, rollout.Name)
labelSelector, err := labels.Parse(lableSelectorString)
if err != nil {
return nil, err
}
// get rollouthistories according to labels, in fact there is only one or zero rolloutHistory with the labelSelector
rollouthistories := &rolloutv1alpha1.RolloutHistoryList{}
err = r.List(context.TODO(), rollouthistories, &client.ListOptions{LabelSelector: labelSelector}, client.InNamespace(rollout.Namespace))
if err != nil {
return nil, err
}
// if there is no rollouthistory found, return
if len(rollouthistories.Items) == 0 {
return nil, nil
}
// find the rollouthistory
return &rollouthistories.Items[0], nil
}
// createRolloutHistoryForProgressingRollout create a new rolloutHistory, which indicates that user does a new rollout
func (r *RolloutHistoryReconciler) createRolloutHistoryForProgressingRollout(rollout *rolloutv1alpha1.Rollout) error {
// init the rolloutHistory
rolloutHistory := &rolloutv1alpha1.RolloutHistory{
ObjectMeta: v1.ObjectMeta{
Name: rollout.Name + "-" + randAllString(6),
Namespace: rollout.Namespace,
Labels: map[string]string{
rolloutIDLabel: rollout.Status.CanaryStatus.ObservedRolloutID,
rolloutNameLabel: rollout.Name,
},
},
}
// create the rolloutHistory
return r.Create(context.TODO(), rolloutHistory, &client.CreateOptions{})
}
// getRolloutHistorySpec get RolloutHistorySpec for rolloutHistory according to rollout
func (r *RolloutHistoryReconciler) getRolloutHistorySpec(rollout *rolloutv1alpha1.Rollout) (rolloutv1alpha1.RolloutHistorySpec, error) {
rolloutHistorySpec := rolloutv1alpha1.RolloutHistorySpec{}
var err error
// get rolloutInfo
if rolloutHistorySpec.Rollout, err = r.getRolloutInfo(rollout); err != nil {
return rolloutHistorySpec, err
}
// get workloadInfo
var workload *rolloutv1alpha1.WorkloadInfo
if workload, err = r.Finder.getWorkloadInfoForRef(rollout.Namespace, rollout.Spec.ObjectRef.WorkloadRef); err != nil {
return rolloutHistorySpec, err
}
rolloutHistorySpec.Workload = *workload
// get serviceInfo
if rolloutHistorySpec.Service, err = r.getServiceInfo(rollout); err != nil {
return rolloutHistorySpec, err
}
// get trafficRoutingInfo
if rolloutHistorySpec.TrafficRouting, err = r.getTrafficRoutingInfo(rollout); err != nil {
return rolloutHistorySpec, err
}
return rolloutHistorySpec, nil
}
// getRolloutInfo get RolloutInfo to for rolloutHistorySpec
func (r *RolloutHistoryReconciler) getRolloutInfo(rollout *rolloutv1alpha1.Rollout) (rolloutv1alpha1.RolloutInfo, error) {
rolloutInfo := rolloutv1alpha1.RolloutInfo{}
var err error
rolloutInfo.Name = rollout.Name
rolloutInfo.RolloutID = rollout.Status.CanaryStatus.ObservedRolloutID
if rolloutInfo.Data.Raw, err = json.Marshal(rollout.Spec); err != nil {
return rolloutInfo, err
}
return rolloutInfo, nil
}
// getServiceInfo get ServiceInfo for rolloutHistorySpec
func (r *RolloutHistoryReconciler) getServiceInfo(rollout *rolloutv1alpha1.Rollout) (rolloutv1alpha1.ServiceInfo, error) {
// get service
service := &corev1.Service{}
serviceInfo := rolloutv1alpha1.ServiceInfo{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: rollout.Namespace, Name: rollout.Spec.Strategy.Canary.TrafficRoutings[0].Service}, service)
if err != nil {
return serviceInfo, errors.New("service not find")
}
// marshal service into serviceInfo
serviceInfo.Name = service.Name
if serviceInfo.Data.Raw, err = json.Marshal(service.Spec); err != nil {
return serviceInfo, err
}
return serviceInfo, nil
}
// getTrafficRoutingInfo get TrafficRoutingInfo for rolloutHistorySpec
func (r *RolloutHistoryReconciler) getTrafficRoutingInfo(rollout *rolloutv1alpha1.Rollout) (rolloutv1alpha1.TrafficRoutingInfo, error) {
trafficRoutingInfo := rolloutv1alpha1.TrafficRoutingInfo{}
var err error
// if gateway is configured, get it
if rollout.Spec.Strategy.Canary.TrafficRoutings[0].Gateway != nil &&
rollout.Spec.Strategy.Canary.TrafficRoutings[0].Gateway.HTTPRouteName != nil {
trafficRoutingInfo.HTTPRoute, err = r.getGateWayInfo(rollout)
if err != nil {
return trafficRoutingInfo, err
}
}
// if ingress is configured, get it
if rollout.Spec.Strategy.Canary.TrafficRoutings[0].Ingress != nil &&
rollout.Spec.Strategy.Canary.TrafficRoutings[0].Ingress.Name != "" {
trafficRoutingInfo.Ingress, err = r.getIngressInfo(rollout)
if err != nil {
return trafficRoutingInfo, err
}
}
return trafficRoutingInfo, nil
}
// getGateWayInfo get HTTPRouteInfo for TrafficRoutingInfo
func (r *RolloutHistoryReconciler) getGateWayInfo(rollout *rolloutv1alpha1.Rollout) (*rolloutv1alpha1.HTTPRouteInfo, error) {
// get HTTPRoute
gatewayName := *rollout.Spec.Strategy.Canary.TrafficRoutings[0].Gateway.HTTPRouteName
HTTPRoute := &v1alpha2.HTTPRoute{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: rollout.Namespace, Name: gatewayName}, HTTPRoute)
if err != nil {
return nil, errors.New("initGateway error: HTTPRoute " + gatewayName + " not find")
}
// marshal HTTPRoute into HTTPRouteInfo for rolloutHistory
gatewayInfo := &rolloutv1alpha1.HTTPRouteInfo{}
gatewayInfo.Name = HTTPRoute.Name
if gatewayInfo.Data.Raw, err = json.Marshal(HTTPRoute.Spec); err != nil {
return nil, err
}
return gatewayInfo, nil
}
// getIngressInfo get IngressInfo for TrafficRoutingInfo
func (r *RolloutHistoryReconciler) getIngressInfo(rollout *rolloutv1alpha1.Rollout) (*rolloutv1alpha1.IngressInfo, error) {
// get Ingress
ingressName := rollout.Spec.Strategy.Canary.TrafficRoutings[0].Ingress.Name
ingress := &networkingv1.Ingress{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: rollout.Namespace, Name: ingressName}, ingress)
if err != nil {
return nil, errors.New("initIngressInfo error: Ingress " + ingressName + " not find")
}
// marshal ingress into ingressInfo
ingressInfo := &rolloutv1alpha1.IngressInfo{}
ingressInfo.Name = ingressName
if ingressInfo.Data.Raw, err = json.Marshal(ingress.Spec); err != nil {
return nil, err
}
return ingressInfo, nil
}
// updateRolloutHistoryWhenRolloutIsCompeleted record all pods released when rollout phase is healthy
func (r *RolloutHistoryReconciler) updateRolloutHistoryWhenRolloutIsCompeleted(rollout *rolloutv1alpha1.Rollout, rolloutHistory *rolloutv1alpha1.RolloutHistory) error {
// do update until rollout.status.Phase is equl to RolloutPhaseHealthy
if rollout.Status.Phase != rolloutv1alpha1.RolloutPhaseHealthy {
return nil
}
// when this rollot's phase has been healthy, rolloutHistory record steps information and rollout.spec
// record .spec for rolloutHistory
var err error
spec, err := r.getRolloutHistorySpec(rollout)
if err != nil {
return err
}
if !reflect.DeepEqual(rolloutHistory.Spec.Rollout.RolloutID, spec.Rollout.RolloutID) {
// update rolloutHistory Spec
rolloutHistory.Spec = spec
err = r.Update(context.TODO(), rolloutHistory, &client.UpdateOptions{})
if err != nil {
return err
}
return nil
}
// make .status.phase PhaseCompleted
rolloutHistory.Status.Phase = rolloutv1alpha1.PhaseCompleted
// record all pods information for rolloutHistory .status.canarySteps
err = r.recordStatusCanarySteps(rollout, rolloutHistory)
if err != nil {
return err
}
// update rolloutHistory subresource
return r.Status().Update(context.TODO(), rolloutHistory, &client.UpdateOptions{})
}
// recordStatusCanarySteps record all pods information which are canary released
func (r *RolloutHistoryReconciler) recordStatusCanarySteps(rollout *rolloutv1alpha1.Rollout, rolloutHistory *rolloutv1alpha1.RolloutHistory) error {
rolloutHistory.Status.CanarySteps = make([]rolloutv1alpha1.CanaryStepInfo, 0)
for i := 0; i < len(rollout.Spec.Strategy.Canary.Steps); i++ {
podList := &corev1.PodList{}
var extraSelector labels.Selector
// get workload labelSelector
workloadLabelSelector, _ := r.Finder.getLabelSelectorForRef(rollout.Namespace, rollout.Spec.ObjectRef.WorkloadRef)
selector, err := v1.LabelSelectorAsSelector(workloadLabelSelector)
if err != nil {
return err
}
// get extra labelSelector including rolloutBathID, rolloutID and workload selector
lableSelectorString := fmt.Sprintf("%v=%v,%v=%v,%v", util.RolloutBatchIDLabel, len(rolloutHistory.Status.CanarySteps)+1, util.RolloutIDLabel, rolloutHistory.Spec.Rollout.RolloutID, selector.String())
extraSelector, err = labels.Parse(lableSelectorString)
if err != nil {
return err
}
// get pods according to extra lableSelector
err = r.List(context.TODO(), podList, &client.ListOptions{LabelSelector: extraSelector}, client.InNamespace(rollout.Namespace))
if err != nil {
return err
}
// if num of pods is empty, append a empty CanaryStepInfo{} to canarySteps
if len(podList.Items) == 0 {
index := int32(len(rolloutHistory.Status.CanarySteps)) + 1
rolloutHistory.Status.CanarySteps = append(rolloutHistory.Status.CanarySteps, rolloutv1alpha1.CanaryStepInfo{CanaryStepIndex: index})
continue
}
// get current step pods released
currentStepInfo := rolloutv1alpha1.CanaryStepInfo{}
var pods []rolloutv1alpha1.Pod
// get pods name, ip, node and add them to .status.canarySteps
for i := range podList.Items {
pod := &podList.Items[i]
if pod.DeletionTimestamp.IsZero() {
cur := rolloutv1alpha1.Pod{Name: pod.Name, IP: pod.Status.PodIP, NodeName: pod.Spec.NodeName}
pods = append(pods, cur)
}
}
currentStepInfo.Pods = pods
currentStepInfo.CanaryStepIndex = int32(len(rolloutHistory.Status.CanarySteps)) + 1
// add current step pods to .status.canarySteps
rolloutHistory.Status.CanarySteps = append(rolloutHistory.Status.CanarySteps, currentStepInfo)
}
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
/*
Copyright 2022 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rollouthistory
import (
rolloutv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var _ handler.EventHandler = &enqueueRequestForRolloutHistory{}
type enqueueRequestForRolloutHistory struct {
}
func (w *enqueueRequestForRolloutHistory) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
w.handleEvent(q, evt.Object)
}
func (w *enqueueRequestForRolloutHistory) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
}
func (w *enqueueRequestForRolloutHistory) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
}
func (w *enqueueRequestForRolloutHistory) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
w.handleEvent(q, evt.ObjectNew)
}
func (w *enqueueRequestForRolloutHistory) handleEvent(q workqueue.RateLimitingInterface, obj client.Object) {
// In fact, rolloutHistory which is created by controller must have rolloutNameLabel and rolloutIDLabe
rolloutName, ok1 := obj.(*rolloutv1alpha1.RolloutHistory).Labels[rolloutNameLabel]
_, ok2 := obj.(*rolloutv1alpha1.RolloutHistory).Labels[rolloutIDLabel]
if !ok1 || !ok2 {
return
}
// add rollout which just creates a rolloutHistory to queue
nsn := types.NamespacedName{Namespace: obj.GetNamespace(), Name: rolloutName}
q.Add(reconcile.Request{NamespacedName: nsn})
}
var _ handler.EventHandler = &enqueueRequestForRollout{}
type enqueueRequestForRollout struct {
}
func (w *enqueueRequestForRollout) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
w.handleEvent(q, evt.Object)
}
func (w *enqueueRequestForRollout) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
}
func (w *enqueueRequestForRollout) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
}
func (w *enqueueRequestForRollout) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
w.handleEvent(q, evt.ObjectNew)
}
func (w *enqueueRequestForRollout) handleEvent(q workqueue.RateLimitingInterface, obj client.Object) {
// RolloutID shouldn't be empty
rollout := obj.(*rolloutv1alpha1.Rollout)
if rollout.Status.CanaryStatus == nil || rollout.Status.CanaryStatus.ObservedRolloutID == "" {
return
}
// add rollout with RolloutID to queue
nsn := types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()}
q.Add(reconcile.Request{NamespacedName: nsn})
}

View File

@ -0,0 +1,342 @@
/*
Copyright 2022 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rollouthistory
import (
"context"
"crypto/rand"
"encoding/json"
"math/big"
"strings"
appsv1alpha1 "github.com/openkruise/kruise-api/apps/v1alpha1"
appsv1beta1 "github.com/openkruise/kruise-api/apps/v1beta1"
rolloutv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
"github.com/openkruise/rollouts/pkg/util"
apps "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// For RolloutHistory
const (
// rolloutIDLabel is designed to distinguish each rollout revision publications.
// The value of RolloutIDLabel corresponds Rollout.Spec.RolloutID.
rolloutIDLabel = "rollouts.kruise.io/rollout-id"
// rolloutName is a label key that will be patched to rolloutHistory.
// Only when rolloutIDLabel is set, rolloutNameLabel will be patched to rolloutHistory.
rolloutNameLabel = "rollouts.kruise.io/rollout-name"
)
// controllerFinderFunc2 is a function type that maps <namespace, workloadRef> to a rolloutHistory's WorkloadInfo
type controllerFinderFunc2 func(namespace string, ref *rolloutv1alpha1.WorkloadRef) (*rolloutv1alpha1.WorkloadInfo, error)
type controllerFinderFunc2LabelSelector func(namespace string, ref *rolloutv1alpha1.WorkloadRef) (*v1.LabelSelector, error)
type controllerFinder2 struct {
client.Client
}
func newControllerFinder2(c client.Client) *controllerFinder2 {
return &controllerFinder2{
Client: c,
}
}
func (r *controllerFinder2) getWorkloadInfoForRef(namespace string, ref *rolloutv1alpha1.WorkloadRef) (*rolloutv1alpha1.WorkloadInfo, error) {
for _, finder := range r.finders2() {
workloadInfo, err := finder(namespace, ref)
if workloadInfo != nil || err != nil {
return workloadInfo, err
}
}
return nil, nil
}
func (r *controllerFinder2) getLabelSelectorForRef(namespace string, ref *rolloutv1alpha1.WorkloadRef) (*v1.LabelSelector, error) {
for _, finder := range r.finders2LabelSelector() {
labelSelector, err := finder(namespace, ref)
if labelSelector != nil || err != nil {
return labelSelector, err
}
}
return nil, nil
}
func (r *controllerFinder2) finders2LabelSelector() []controllerFinderFunc2LabelSelector {
return []controllerFinderFunc2LabelSelector{r.getLabelSelectorWithAdvancedStatefulSet, r.getLabelSelectorWithCloneSet,
r.getLabelSelectorWithDeployment, r.getLabelSelectorWithNativeStatefulSet}
}
func (r *controllerFinder2) finders2() []controllerFinderFunc2 {
return []controllerFinderFunc2{r.getWorkloadInfoWithAdvancedStatefulSet, r.getWorkloadInfoWithCloneSet,
r.getWorkloadInfoWithDeployment, r.getWorkloadInfoWithNativeStatefulSet}
}
// getWorkloadInfoWithAdvancedStatefulSet returns WorkloadInfo with kruise statefulset referenced by the provided controllerRef
func (r *controllerFinder2) getWorkloadInfoWithAdvancedStatefulSet(namespace string, ref *rolloutv1alpha1.WorkloadRef) (*rolloutv1alpha1.WorkloadInfo, error) {
// This error is irreversible, so there is no need to return error
ok, _ := verifyGroupKind(ref, util.ControllerKruiseKindSts.Kind, []string{util.ControllerKruiseKindSts.Group})
if !ok {
return nil, nil
}
// get workload
workload := &appsv1beta1.StatefulSet{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: ref.Name}, workload)
if err != nil {
// when error is NotFound, it is ok here.
if errors.IsNotFound(err) {
return nil, nil
}
return nil, err
}
// assign value
workloadInfo := &rolloutv1alpha1.WorkloadInfo{}
workloadInfo.APIVersion = workload.APIVersion
workloadInfo.Kind = workload.Kind
workloadInfo.Name = workload.Name
if workloadInfo.Data.Raw, err = json.Marshal(workload.Spec); err != nil {
return nil, err
}
return workloadInfo, nil
}
// getLabelSelectorWithAdvancedStatefulSet returns selector with kruise statefulset referenced by the provided controllerRef
func (r *controllerFinder2) getLabelSelectorWithAdvancedStatefulSet(namespace string, ref *rolloutv1alpha1.WorkloadRef) (*v1.LabelSelector, error) {
// This error is irreversible, so there is no need to return error
ok, _ := verifyGroupKind(ref, util.ControllerKruiseKindSts.Kind, []string{util.ControllerKruiseKindSts.Group})
if !ok {
return nil, nil
}
// get workload
workload := &appsv1beta1.StatefulSet{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: ref.Name}, workload)
if err != nil {
// when error is NotFound, it is ok here.
if errors.IsNotFound(err) {
return nil, nil
}
return nil, err
}
// assign value
labelSelector := workload.Spec.Selector
return labelSelector, nil
}
// getWorkloadInfoWithCloneSet returns WorkloadInfo with kruise cloneset referenced by the provided controllerRef
func (r *controllerFinder2) getWorkloadInfoWithCloneSet(namespace string, ref *rolloutv1alpha1.WorkloadRef) (*rolloutv1alpha1.WorkloadInfo, error) {
// This error is irreversible, so there is no need to return error
ok, _ := verifyGroupKind(ref, util.ControllerKruiseKindCS.Kind, []string{util.ControllerKruiseKindCS.Group})
if !ok {
return nil, nil
}
// get workload
workload := &appsv1alpha1.CloneSet{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: ref.Name}, workload)
if err != nil {
// when error is NotFound, it is ok here.
if errors.IsNotFound(err) {
return nil, nil
}
return nil, err
}
// assign value
workloadInfo := &rolloutv1alpha1.WorkloadInfo{}
workloadInfo.APIVersion = workload.APIVersion
workloadInfo.Kind = workload.Kind
workloadInfo.Name = workload.Name
if workloadInfo.Data.Raw, err = json.Marshal(workload.Spec); err != nil {
return nil, err
}
return workloadInfo, nil
}
// getLabelSelectorWithCloneSet returns selector with kruise cloneset referenced by the provided controllerRef
func (r *controllerFinder2) getLabelSelectorWithCloneSet(namespace string, ref *rolloutv1alpha1.WorkloadRef) (*v1.LabelSelector, error) {
// This error is irreversible, so there is no need to return error
ok, _ := verifyGroupKind(ref, util.ControllerKruiseKindCS.Kind, []string{util.ControllerKruiseKindCS.Group})
if !ok {
return nil, nil
}
// get workload
workload := &appsv1alpha1.CloneSet{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: ref.Name}, workload)
if err != nil {
// when error is NotFound, it is ok here.
if errors.IsNotFound(err) {
return nil, nil
}
return nil, err
}
// assign value
labelSelector := workload.Spec.Selector
return labelSelector, nil
}
// getWorkloadInfoWithDeployment returns WorkloadInfo with k8s native deployment referenced by the provided controllerRef
func (r *controllerFinder2) getWorkloadInfoWithDeployment(namespace string, ref *rolloutv1alpha1.WorkloadRef) (*rolloutv1alpha1.WorkloadInfo, error) {
// This error is irreversible, so there is no need to return error
ok, _ := verifyGroupKind(ref, util.ControllerKindDep.Kind, []string{util.ControllerKindDep.Group})
if !ok {
return nil, nil
}
// get deployment
workload := &apps.Deployment{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: ref.Name}, workload)
if err != nil {
// when error is NotFound, it is ok here.
if errors.IsNotFound(err) {
return nil, nil
}
return nil, err
}
// assign value
workloadInfo := &rolloutv1alpha1.WorkloadInfo{}
workloadInfo.APIVersion = workload.APIVersion
workloadInfo.Kind = workload.Kind
workloadInfo.Name = workload.Name
if workloadInfo.Data.Raw, err = json.Marshal(workload.Spec); err != nil {
return nil, err
}
return workloadInfo, nil
}
// getLabelSelectorWithDeployment returns selector with deployment referenced by the provided controllerRef
func (r *controllerFinder2) getLabelSelectorWithDeployment(namespace string, ref *rolloutv1alpha1.WorkloadRef) (*v1.LabelSelector, error) {
// This error is irreversible, so there is no need to return error
ok, _ := verifyGroupKind(ref, util.ControllerKindDep.Kind, []string{util.ControllerKindDep.Group})
if !ok {
return nil, nil
}
// get workload
workload := &apps.Deployment{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: ref.Name}, workload)
if err != nil {
// when error is NotFound, it is ok here.
if errors.IsNotFound(err) {
return nil, nil
}
return nil, err
}
// assign value
labelSelector := workload.Spec.Selector
return labelSelector, nil
}
// getWorkloadInfoWithNativeStatefulSet returns WorkloadInfo with k8s native statefulset referenced by the provided controllerRef
func (r *controllerFinder2) getWorkloadInfoWithNativeStatefulSet(namespace string, ref *rolloutv1alpha1.WorkloadRef) (*rolloutv1alpha1.WorkloadInfo, error) {
// This error is irreversible, so there is no need to return error
ok, _ := verifyGroupKind(ref, util.ControllerKindSts.Kind, []string{util.ControllerKindSts.Group})
if !ok {
return nil, nil
}
// get workload
workload := &apps.StatefulSet{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: ref.Name}, workload)
if err != nil {
// when error is NotFound, it is ok here.
if errors.IsNotFound(err) {
return nil, nil
}
return nil, err
}
// assign value
workloadInfo := &rolloutv1alpha1.WorkloadInfo{}
workloadInfo.APIVersion = workload.APIVersion
workloadInfo.Kind = workload.Kind
workloadInfo.Name = workload.Name
if workloadInfo.Data.Raw, err = json.Marshal(workload.Spec); err != nil {
return nil, err
}
return workloadInfo, nil
}
// getLabelSelectorWithNativeStatefulSet returns selector with deployment referenced by the provided controllerRef
func (r *controllerFinder2) getLabelSelectorWithNativeStatefulSet(namespace string, ref *rolloutv1alpha1.WorkloadRef) (*v1.LabelSelector, error) {
// This error is irreversible, so there is no need to return error
ok, _ := verifyGroupKind(ref, util.ControllerKindSts.Kind, []string{util.ControllerKindSts.Group})
if !ok {
return nil, nil
}
// get workload
workload := &apps.StatefulSet{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: ref.Name}, workload)
if err != nil {
// when error is NotFound, it is ok here.
if errors.IsNotFound(err) {
return nil, nil
}
return nil, err
}
// assign value
labelSelector := workload.Spec.Selector
return labelSelector, nil
}
func verifyGroupKind(ref *rolloutv1alpha1.WorkloadRef, expectedKind string, expectedGroups []string) (bool, error) {
gv, err := schema.ParseGroupVersion(ref.APIVersion)
if err != nil {
return false, err
}
if ref.Kind != expectedKind {
return false, nil
}
for _, group := range expectedGroups {
if group == gv.Group {
return true, nil
}
}
return false, nil
}
var chars = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}
// randAllString get random string
func randAllString(lenNum int) string {
str := strings.Builder{}
for i := 0; i < lenNum; i++ {
n, err := rand.Int(rand.Reader, big.NewInt(36))
if err != nil {
return ""
}
l := chars[n.Int64()]
str.WriteString(l)
}
return str.String()
}

View File

@ -0,0 +1,37 @@
/*
Copyright 2022 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package feature
import (
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/component-base/featuregate"
utilfeature "github.com/openkruise/rollouts/pkg/util/feature"
)
const (
// PodProbeMarkerGate enable Kruise provide the ability to execute custom Probes.
// Note: custom probe execution requires kruise daemon, so currently only traditional Kubelet is supported, not virtual-kubelet.
RolloutHistoryGate featuregate.Feature = "RolloutHistoryGate"
)
var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
RolloutHistoryGate: {Default: false, PreRelease: featuregate.Alpha},
}
func init() {
runtime.Must(utilfeature.DefaultMutableFeatureGate.Add(defaultFeatureGates))
}

View File

@ -0,0 +1,32 @@
/*
Copyright 2022 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package feature
import (
"k8s.io/component-base/featuregate"
)
var (
// DefaultMutableFeatureGate is a mutable version of DefaultFeatureGate.
// Only top-level commands/options setup and the k8s.io/component-base/featuregate/testing package should make use of this.
// Tests that need to modify feature gates for the duration of their test should use:
// defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.<FeatureName>, <value>)()
DefaultMutableFeatureGate featuregate.MutableFeatureGate = featuregate.NewFeatureGate()
// DefaultFeatureGate is a shared global FeatureGate.
// Top-level commands/options setup that needs to modify this feature gate should use DefaultMutableFeatureGate.
DefaultFeatureGate featuregate.FeatureGate = DefaultMutableFeatureGate
)