mirror of
https://github.com/TECHNOFAB11/zfs-localpv.git
synced 2026-02-03 16:15:11 +01:00
test(zfspv): minikube setup for travis
to run integration test cases Signed-off-by: Pawan <pawan@mayadata.io>
This commit is contained in:
parent
7ab6156b98
commit
d933b47c75
27 changed files with 4933 additions and 5 deletions
556
tests/deploy/deployment.go
Normal file
556
tests/deploy/deployment.go
Normal file
|
|
@ -0,0 +1,556 @@
|
|||
/*
|
||||
Copyright 2019 The OpenEBS 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 deploy
|
||||
|
||||
import (
|
||||
"github.com/openebs/zfs-localpv/pkg/common/errors"
|
||||
templatespec "github.com/openebs/zfs-localpv/tests/pts"
|
||||
"github.com/openebs/zfs-localpv/tests/stringer"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Predicate abstracts conditional logic w.r.t the deployment instance
|
||||
//
|
||||
// NOTE:
|
||||
// predicate is a functional approach versus traditional approach to mix
|
||||
// conditions such as *if-else* within blocks of business logic
|
||||
//
|
||||
// NOTE:
|
||||
// predicate approach enables clear separation of conditionals from
|
||||
// imperatives i.e. actions that form the business logic
|
||||
type Predicate func(*Deploy) bool
|
||||
|
||||
// Deploy is the wrapper over k8s deployment Object
|
||||
type Deploy struct {
|
||||
// kubernetes deployment instance
|
||||
object *appsv1.Deployment
|
||||
}
|
||||
|
||||
// Builder enables building an instance of
|
||||
// deployment
|
||||
type Builder struct {
|
||||
deployment *Deploy // kubernetes deployment instance
|
||||
checks []Predicate // predicate list for deploy
|
||||
errors []error
|
||||
}
|
||||
|
||||
// PredicateName type is wrapper over string.
|
||||
// It is used to refer predicate and status msg.
|
||||
type PredicateName string
|
||||
|
||||
const (
|
||||
// PredicateProgressDeadlineExceeded refer to
|
||||
// predicate IsProgressDeadlineExceeded.
|
||||
PredicateProgressDeadlineExceeded PredicateName = "ProgressDeadlineExceeded"
|
||||
// PredicateNotSpecSynced refer to predicate IsNotSpecSynced
|
||||
PredicateNotSpecSynced PredicateName = "NotSpecSynced"
|
||||
// PredicateOlderReplicaActive refer to predicate IsOlderReplicaActive
|
||||
PredicateOlderReplicaActive PredicateName = "OlderReplicaActive"
|
||||
// PredicateTerminationInProgress refer to predicate IsTerminationInProgress
|
||||
PredicateTerminationInProgress PredicateName = "TerminationInProgress"
|
||||
// PredicateUpdateInProgress refer to predicate IsUpdateInProgress.
|
||||
PredicateUpdateInProgress PredicateName = "UpdateInProgress"
|
||||
)
|
||||
|
||||
// String implements the stringer interface
|
||||
func (d *Deploy) String() string {
|
||||
return stringer.Yaml("deployment", d.object)
|
||||
}
|
||||
|
||||
// GoString implements the goStringer interface
|
||||
func (d *Deploy) GoString() string {
|
||||
return d.String()
|
||||
}
|
||||
|
||||
// NewBuilder returns a new instance of builder meant for deployment
|
||||
func NewBuilder() *Builder {
|
||||
return &Builder{
|
||||
deployment: &Deploy{
|
||||
object: &appsv1.Deployment{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// WithName sets the Name field of deployment with provided value.
|
||||
func (b *Builder) WithName(name string) *Builder {
|
||||
if len(name) == 0 {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment: missing name"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
b.deployment.object.Name = name
|
||||
return b
|
||||
}
|
||||
|
||||
// WithNamespace sets the Namespace field of deployment with provided value.
|
||||
func (b *Builder) WithNamespace(namespace string) *Builder {
|
||||
if len(namespace) == 0 {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment: missing namespace"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
b.deployment.object.Namespace = namespace
|
||||
return b
|
||||
}
|
||||
|
||||
// WithAnnotations merges existing annotations if any
|
||||
// with the ones that are provided here
|
||||
func (b *Builder) WithAnnotations(annotations map[string]string) *Builder {
|
||||
if len(annotations) == 0 {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment object: missing annotations"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
if b.deployment.object.Annotations == nil {
|
||||
return b.WithAnnotationsNew(annotations)
|
||||
}
|
||||
|
||||
for key, value := range annotations {
|
||||
b.deployment.object.Annotations[key] = value
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WithAnnotationsNew resets existing annotaions if any with
|
||||
// ones that are provided here
|
||||
func (b *Builder) WithAnnotationsNew(annotations map[string]string) *Builder {
|
||||
if len(annotations) == 0 {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment object: no new annotations"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
// copy of original map
|
||||
newannotations := map[string]string{}
|
||||
for key, value := range annotations {
|
||||
newannotations[key] = value
|
||||
}
|
||||
|
||||
// override
|
||||
b.deployment.object.Annotations = newannotations
|
||||
return b
|
||||
}
|
||||
|
||||
// WithNodeSelector Sets the node selector with the provided argument.
|
||||
func (b *Builder) WithNodeSelector(selector map[string]string) *Builder {
|
||||
if len(selector) == 0 {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment object: no node selector"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
if b.deployment.object.Spec.Template.Spec.NodeSelector == nil {
|
||||
return b.WithNodeSelectorNew(selector)
|
||||
}
|
||||
|
||||
for key, value := range selector {
|
||||
b.deployment.object.Spec.Template.Spec.NodeSelector[key] = value
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WithNodeSelector Sets the node selector with the provided argument.
|
||||
func (b *Builder) WithNodeSelectorNew(selector map[string]string) *Builder {
|
||||
if len(selector) == 0 {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment object: no new node selector"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
b.deployment.object.Spec.Template.Spec.NodeSelector = selector
|
||||
return b
|
||||
}
|
||||
|
||||
// WithOwnerReferenceNew sets ownerreference if any with
|
||||
// ones that are provided here
|
||||
func (b *Builder) WithOwnerReferenceNew(ownerRefernce []metav1.OwnerReference) *Builder {
|
||||
if len(ownerRefernce) == 0 {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment object: no new ownerRefernce"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
b.deployment.object.OwnerReferences = ownerRefernce
|
||||
return b
|
||||
}
|
||||
|
||||
// WithLabels merges existing labels if any
|
||||
// with the ones that are provided here
|
||||
func (b *Builder) WithLabels(labels map[string]string) *Builder {
|
||||
if len(labels) == 0 {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment object: missing labels"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
if b.deployment.object.Labels == nil {
|
||||
return b.WithLabelsNew(labels)
|
||||
}
|
||||
|
||||
for key, value := range labels {
|
||||
b.deployment.object.Labels[key] = value
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WithLabelsNew resets existing labels if any with
|
||||
// ones that are provided here
|
||||
func (b *Builder) WithLabelsNew(labels map[string]string) *Builder {
|
||||
if len(labels) == 0 {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment object: no new labels"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
// copy of original map
|
||||
newlbls := map[string]string{}
|
||||
for key, value := range labels {
|
||||
newlbls[key] = value
|
||||
}
|
||||
|
||||
// override
|
||||
b.deployment.object.Labels = newlbls
|
||||
return b
|
||||
}
|
||||
|
||||
// WithSelectorMatchLabels merges existing matchlabels if any
|
||||
// with the ones that are provided here
|
||||
func (b *Builder) WithSelectorMatchLabels(matchlabels map[string]string) *Builder {
|
||||
if len(matchlabels) == 0 {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment object: missing matchlabels"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
if b.deployment.object.Spec.Selector == nil {
|
||||
return b.WithSelectorMatchLabelsNew(matchlabels)
|
||||
}
|
||||
|
||||
for key, value := range matchlabels {
|
||||
b.deployment.object.Spec.Selector.MatchLabels[key] = value
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WithSelectorMatchLabelsNew resets existing matchlabels if any with
|
||||
// ones that are provided here
|
||||
func (b *Builder) WithSelectorMatchLabelsNew(matchlabels map[string]string) *Builder {
|
||||
if len(matchlabels) == 0 {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment object: no new matchlabels"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
// copy of original map
|
||||
newmatchlabels := map[string]string{}
|
||||
for key, value := range matchlabels {
|
||||
newmatchlabels[key] = value
|
||||
}
|
||||
|
||||
newselector := &metav1.LabelSelector{
|
||||
MatchLabels: newmatchlabels,
|
||||
}
|
||||
|
||||
// override
|
||||
b.deployment.object.Spec.Selector = newselector
|
||||
return b
|
||||
}
|
||||
|
||||
// WithReplicas sets the replica field of deployment
|
||||
func (b *Builder) WithReplicas(replicas *int32) *Builder {
|
||||
|
||||
if replicas == nil {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment object: nil replicas"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
newreplicas := *replicas
|
||||
|
||||
if newreplicas < 0 {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.Errorf(
|
||||
"failed to build deployment object: invalid replicas {%d}",
|
||||
newreplicas,
|
||||
),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
b.deployment.object.Spec.Replicas = &newreplicas
|
||||
return b
|
||||
}
|
||||
|
||||
//WithStrategyType sets the strategy field of the deployment
|
||||
func (b *Builder) WithStrategyType(
|
||||
strategytype appsv1.DeploymentStrategyType,
|
||||
) *Builder {
|
||||
if len(strategytype) == 0 {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment object: missing strategytype"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
b.deployment.object.Spec.Strategy.Type = strategytype
|
||||
return b
|
||||
}
|
||||
|
||||
// WithPodTemplateSpecBuilder sets the template field of the deployment
|
||||
func (b *Builder) WithPodTemplateSpecBuilder(
|
||||
tmplbuilder *templatespec.Builder,
|
||||
) *Builder {
|
||||
if tmplbuilder == nil {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.New("failed to build deployment: nil templatespecbuilder"),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
templatespecObj, err := tmplbuilder.Build()
|
||||
|
||||
if err != nil {
|
||||
b.errors = append(
|
||||
b.errors,
|
||||
errors.Wrap(
|
||||
err,
|
||||
"failed to build deployment",
|
||||
),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
b.deployment.object.Spec.Template = *templatespecObj.Object
|
||||
return b
|
||||
}
|
||||
|
||||
type deployBuildOption func(*Deploy)
|
||||
|
||||
// NewForAPIObject returns a new instance of builder
|
||||
// for a given deployment Object
|
||||
func NewForAPIObject(
|
||||
obj *appsv1.Deployment,
|
||||
opts ...deployBuildOption,
|
||||
) *Deploy {
|
||||
d := &Deploy{object: obj}
|
||||
for _, o := range opts {
|
||||
o(d)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// Build returns a deployment instance
|
||||
func (b *Builder) Build() (*appsv1.Deployment, error) {
|
||||
err := b.validate()
|
||||
// TODO: err in Wrapf is not logged. Fix is required
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err,
|
||||
"failed to build a deployment: %s",
|
||||
b.deployment.object.Name)
|
||||
}
|
||||
return b.deployment.object, nil
|
||||
}
|
||||
|
||||
func (b *Builder) validate() error {
|
||||
if len(b.errors) != 0 {
|
||||
return errors.Errorf(
|
||||
"failed to validate: build errors were found: %+v",
|
||||
b.errors,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsRollout range over rolloutChecks map and check status of each predicate
|
||||
// also it generates status message from rolloutStatuses using predicate key
|
||||
func (d *Deploy) IsRollout() (PredicateName, bool) {
|
||||
for pk, p := range rolloutChecks {
|
||||
if p(d) {
|
||||
return pk, false
|
||||
}
|
||||
}
|
||||
return "", true
|
||||
}
|
||||
|
||||
// FailedRollout returns rollout status message for fail condition
|
||||
func (d *Deploy) FailedRollout(name PredicateName) *RolloutOutput {
|
||||
return &RolloutOutput{
|
||||
Message: rolloutStatuses[name](d),
|
||||
IsRolledout: false,
|
||||
}
|
||||
}
|
||||
|
||||
// SuccessRollout returns rollout status message for success condition
|
||||
func (d *Deploy) SuccessRollout() *RolloutOutput {
|
||||
return &RolloutOutput{
|
||||
Message: "deployment successfully rolled out",
|
||||
IsRolledout: true,
|
||||
}
|
||||
}
|
||||
|
||||
// RolloutStatus returns rollout message of deployment instance
|
||||
func (d *Deploy) RolloutStatus() (op *RolloutOutput, err error) {
|
||||
pk, ok := d.IsRollout()
|
||||
if ok {
|
||||
return d.SuccessRollout(), nil
|
||||
}
|
||||
return d.FailedRollout(pk), nil
|
||||
}
|
||||
|
||||
// RolloutStatusRaw returns rollout message of deployment instance
|
||||
// in byte format
|
||||
func (d *Deploy) RolloutStatusRaw() (op []byte, err error) {
|
||||
message, err := d.RolloutStatus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewRollout(
|
||||
withOutputObject(message)).
|
||||
Raw()
|
||||
}
|
||||
|
||||
// AddCheck adds the predicate as a condition to be validated
|
||||
// against the deployment instance
|
||||
func (b *Builder) AddCheck(p Predicate) *Builder {
|
||||
b.checks = append(b.checks, p)
|
||||
return b
|
||||
}
|
||||
|
||||
// AddChecks adds the provided predicates as conditions to be
|
||||
// validated against the deployment instance
|
||||
func (b *Builder) AddChecks(p []Predicate) *Builder {
|
||||
for _, check := range p {
|
||||
b.AddCheck(check)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// IsProgressDeadlineExceeded is used to check update is timed out or not.
|
||||
// If `Progressing` condition's reason is `ProgressDeadlineExceeded` then
|
||||
// it is not rolled out.
|
||||
func IsProgressDeadlineExceeded() Predicate {
|
||||
return func(d *Deploy) bool {
|
||||
return d.IsProgressDeadlineExceeded()
|
||||
}
|
||||
}
|
||||
|
||||
// IsProgressDeadlineExceeded is used to check update is timed out or not.
|
||||
// If `Progressing` condition's reason is `ProgressDeadlineExceeded` then
|
||||
// it is not rolled out.
|
||||
func (d *Deploy) IsProgressDeadlineExceeded() bool {
|
||||
for _, cond := range d.object.Status.Conditions {
|
||||
if cond.Type == appsv1.DeploymentProgressing &&
|
||||
cond.Reason == "ProgressDeadlineExceeded" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsOlderReplicaActive check if older replica's are still active or not if
|
||||
// Status.UpdatedReplicas < *Spec.Replicas then some of the replicas are
|
||||
// updated and some of them are not.
|
||||
func IsOlderReplicaActive() Predicate {
|
||||
return func(d *Deploy) bool {
|
||||
return d.IsOlderReplicaActive()
|
||||
}
|
||||
}
|
||||
|
||||
// IsOlderReplicaActive check if older replica's are still active or not if
|
||||
// Status.UpdatedReplicas < *Spec.Replicas then some of the replicas are
|
||||
// updated and some of them are not.
|
||||
func (d *Deploy) IsOlderReplicaActive() bool {
|
||||
return d.object.Spec.Replicas != nil &&
|
||||
d.object.Status.UpdatedReplicas < *d.object.Spec.Replicas
|
||||
}
|
||||
|
||||
// IsTerminationInProgress checks for older replicas are waiting to
|
||||
// terminate or not. If Status.Replicas > Status.UpdatedReplicas then
|
||||
// some of the older replicas are in running state because newer
|
||||
// replicas are not in running state. It waits for newer replica to
|
||||
// come into running state then terminate.
|
||||
func IsTerminationInProgress() Predicate {
|
||||
return func(d *Deploy) bool {
|
||||
return d.IsTerminationInProgress()
|
||||
}
|
||||
}
|
||||
|
||||
// IsTerminationInProgress checks for older replicas are waiting to
|
||||
// terminate or not. If Status.Replicas > Status.UpdatedReplicas then
|
||||
// some of the older replicas are in running state because newer
|
||||
// replicas are not in running state. It waits for newer replica to
|
||||
// come into running state then terminate.
|
||||
func (d *Deploy) IsTerminationInProgress() bool {
|
||||
return d.object.Status.Replicas > d.object.Status.UpdatedReplicas
|
||||
}
|
||||
|
||||
// IsUpdateInProgress Checks if all the replicas are updated or not.
|
||||
// If Status.AvailableReplicas < Status.UpdatedReplicas then all the
|
||||
//older replicas are not there but there are less number of availableReplicas
|
||||
func IsUpdateInProgress() Predicate {
|
||||
return func(d *Deploy) bool {
|
||||
return d.IsUpdateInProgress()
|
||||
}
|
||||
}
|
||||
|
||||
// IsUpdateInProgress Checks if all the replicas are updated or not.
|
||||
// If Status.AvailableReplicas < Status.UpdatedReplicas then all the
|
||||
// older replicas are not there but there are less number of availableReplicas
|
||||
func (d *Deploy) IsUpdateInProgress() bool {
|
||||
return d.object.Status.AvailableReplicas < d.object.Status.UpdatedReplicas
|
||||
}
|
||||
|
||||
// IsNotSyncSpec compare generation in status and spec and check if
|
||||
// deployment spec is synced or not. If Generation <= Status.ObservedGeneration
|
||||
// then deployment spec is not updated yet.
|
||||
func IsNotSyncSpec() Predicate {
|
||||
return func(d *Deploy) bool {
|
||||
return d.IsNotSyncSpec()
|
||||
}
|
||||
}
|
||||
|
||||
// IsNotSyncSpec compare generation in status and spec and check if
|
||||
// deployment spec is synced or not. If Generation <= Status.ObservedGeneration
|
||||
// then deployment spec is not updated yet.
|
||||
func (d *Deploy) IsNotSyncSpec() bool {
|
||||
return d.object.Generation > d.object.Status.ObservedGeneration
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue