add RolloutHistory controller
Signed-off-by: yike21 <yike21@qq.com>
This commit is contained in:
parent
f4269ce618
commit
219cbb0965
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
2
go.mod
|
|
@ -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
8
go.sum
|
|
@ -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
12
main.go
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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})
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
Loading…
Reference in New Issue