Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DaemonSet updates - take 2 #41116

Merged
merged 5 commits into from Feb 27, 2017
Merged

DaemonSet updates - take 2 #41116

merged 5 commits into from Feb 27, 2017

Conversation

lukaszo
Copy link
Contributor

@lukaszo lukaszo commented Feb 8, 2017

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Feb 8, 2017
@k8s-reviewable
Copy link

This change is Reviewable

@k8s-github-robot k8s-github-robot added kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. release-note-label-needed labels Feb 8, 2017
@lukaszo lukaszo mentioned this pull request Feb 8, 2017
@0xmichalis
Copy link
Contributor

@kubernetes/sig-apps-pr-reviews

@0xmichalis 0xmichalis added release-note Denotes a PR that will be considered when it comes time to generate release notes. and removed release-note-label-needed labels Feb 8, 2017
// available (ready for at least minReadySeconds)
NumberAvailable int32

// NumberUnavailable is the number of nodes that should be running the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, for Deployments, this is the number of pods running but not considered ready/available.

unavailableReplicas := totalReplicas - availableReplicas

Copy link
Member

@janetkuo janetkuo Feb 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, for Deployments, this is the number of pods running but not considered ready/available.

unavailableReplicas := totalReplicas - availableReplicas

That means the same thing as !(running && available) as this comment described, right?

@@ -219,6 +225,73 @@ func Convert_v1beta1_RollingUpdateDeployment_To_extensions_RollingUpdateDeployme
return nil
}

func Convert_extensions_DaemonSetSpec_To_v1beta1_DaemonSetSpec(in *extensions.DaemonSetSpec, out *DaemonSetSpec, s conversion.Scope) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need custom conversions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I had some errors with validation earlier and thought that it will help. It turns out that only
Convert_extensions_RollingUpdateDaemonSet_To_v1beta1_RollingUpdateDaemonSet Convert_v1beta1_RollingUpdateDaemonSet_To_extensions_RollingUpdateDaemonSet,
are required

}
allErrs = append(allErrs, ValidateRollingUpdateDaemonSet(strategy.RollingUpdate, fldPath.Child("rollingUpdate"))...)
} else {
allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), strategy.Type, "RollingUpdate and OnDelete are only supported types"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use field.NotSupported

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


func ValidateDaemonSetUpdateStrategy(strategy *extensions.DaemonSetUpdateStrategy, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if strategy.Type == extensions.OnDeleteDaemonSetStrategyType {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: make this a switch

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -548,10 +560,12 @@ func (dsc *DaemonSetsController) manage(ds *extensions.DaemonSet) error {
glog.V(4).Infof("Nodes needing daemon pods for daemon set %s: %+v, creating %d", ds.Name, nodesNeedingDaemonPods, createDiff)
createWait := sync.WaitGroup{}
createWait.Add(createDiff)
dsPodTemplateSpecHash := podutil.GetPodTemplateSpecHashFnv(ds.Spec.Template)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a TODO to this helper to be moved in a more generic location.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return true
}
} else {
// XXX: Hash does not exist. It's old pod. For now returning true.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In such a case I think you should compare (apimachinery.Semantic.DeepEqual) the ds pod template spec with the pod spec AND the labels and annotations found in the metadata of the ds podtemplate vs pod metadata.

Copy link
Member

@janetkuo janetkuo Feb 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From @lukaszo's earlier comment it seems hard to compare pod spec with DS template because a lot of default values are applied while a pod is created. @lukaszo does this mean we won't be able to rolling update a DS created prior to 1.6 (unless users manually kill those pods without hash)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we have a helper that will do the Pod->PodTemplateSpec conversion?

badPods := []*v1.Pod{}
goodPods := []*v1.Pod{}
for _, pod := range pods {
if pod.Status.Phase == v1.PodRunning {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Terminating pods are still running pods, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also we should probably use IsPodReady (uses Conditions) in favor of Phase.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

func GetNumUnavailable(ds *extensions.DaemonSet, newPods, oldPods []*v1.Pod) int {
unavailable := int(ds.Status.DesiredNumberScheduled) - len(newPods) - len(oldPods)
for _, pod := range newPods {
if pod.Status.Phase != v1.PodRunning {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to use IsPodAvailable and provide MinReadySeconds from the ds spec

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also pods that are marked for deletion should count as unavailable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

glog.V(4).Infof("Marking all bad old pods for deletion")
for _, pod := range badPods {
glog.V(4).Infof("Marking pod %s/%s for deletion", ds.Name, pod.Name)
podsToDelete = append(podsToDelete, pod.Name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't retry the deletion if the pod is already marked for deletion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure. manage doesn't check it. Also RollingUpdate shouldn't be executed until all expected deletions are complete.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pods won't be deleted immediately after Delete returns; it may be terminating depending on its termination grace period

@@ -716,6 +744,17 @@ func (dsc *DaemonSetsController) syncDaemonSet(key string) error {
}
}

dsNeedsSync = dsc.expectations.SatisfiedExpectations(dsKey)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already called above. Although expectations never worked as expected and we should probably get rid of them from this controller too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

manage can set new expectations. I want to run updates only if manage didn't change anything.

}

func GetNumUnavailable(ds *extensions.DaemonSet, newPods, oldPods []*v1.Pod) int {
unavailable := int(ds.Status.DesiredNumberScheduled) - len(newPods) - len(oldPods)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DesiredNumberScheduled may not be synced when you call GetNumUnavailable. Generally, utilities for constructing the status of an object should not treat fields from the status object as correct. Instead try to identify how much DesiredNumberScheduled really is.

if err != nil {
return fmt.Errorf("couldn't get list of nodes during rolling update of daemon set %#v: %v", ds, err)
}
maxUnavailable, err := intstrutil.GetValueFromIntOrPercent(ds.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable, len(nodeList), true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a helper for this in the deployment utilities.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return utilerrors.NewAggregate(errors)
}

func (dsc *DaemonSetsController) syncNodes(ds *extensions.DaemonSet, podsToDelete, nodesNeedingDaemonPods []string) []error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

godoc what syncNodes is supposed to do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

podutil "k8s.io/kubernetes/pkg/controller/deployment/util"
)

func (dsc *DaemonSetsController) rollingUpdate(ds *extensions.DaemonSet) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

godoc what rollingUpdate is doing exactly. It seems that it simply deletes pods if possible and then manage will make sure the new pods will spin up?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, manage will create new pods but I'm not sure if this is a correct approach. I need to run more tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment to describe that? // rollingUpdate does such and such...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

func (dsc *DaemonSetsController) getAllDaemonSetPods(ds *extensions.DaemonSet) ([]*v1.Pod, []*v1.Pod, error) {
newPods := []*v1.Pod{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use var newPods []*v1.Pod instead - consistent with what's the conventional way of defining stuff in the codebase (avoids an extra allocation)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

updated = true
}
if ready && updated {
numberAvailable++
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not what available should be. Available is the # of pods (old or new) that are ready for at least minReadySeconds.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, ref https://github.com/kubernetes/community/blob/master/contributors/design-proposals/daemonset-update.md#daemonset-controller:

  • .status.numberAvailable = the total number of DaemonSet pods that have become Ready for MinReadySeconds
  • .status.numberUnavailable = .status.desiredNumberScheduled - .status.numberAvailable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@0xmichalis 0xmichalis self-assigned this Feb 8, 2017
@idvoretskyi
Copy link
Member

cc @kubernetes/mirantis

@0xmichalis
Copy link
Contributor

0xmichalis commented Feb 8, 2017 via email

@janetkuo janetkuo self-assigned this Feb 8, 2017
@0xmichalis
Copy link
Contributor

We need more e2e tests and kubectl rollout status should probably respect strategy params and quit as soon as the minimum available pods are updated. Fine with a follow-up

lgtm

@lukaszo
Copy link
Contributor Author

lukaszo commented Feb 26, 2017

@k8s-bot non-cri e2e test this

@lukaszo
Copy link
Contributor Author

lukaszo commented Feb 26, 2017

@k8s-bot gce etcd3 e2e test this

@k8s-github-robot k8s-github-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 27, 2017
@k8s-github-robot k8s-github-robot removed lgtm "Looks good to me", indicates that a PR is ready to be merged. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Feb 27, 2017
@lukaszo
Copy link
Contributor Author

lukaszo commented Feb 27, 2017

Nothing changed, just rebased and updated generated part.

@k8s-github-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

The following people have approved this PR: janetkuo, k8s-merge-robot, lukaszo

Needs approval from an approver in each of these OWNERS Files:

We suggest the following people:
cc @smarterclayton
You can indicate your approval by writing /approve in a comment
You can cancel your approval by writing /approve cancel in a comment

@@ -508,8 +507,20 @@ type DaemonSet struct {
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
// +optional
Status DaemonSetStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`

// A sequence number representing a specific generation of the template.
// Populated by the system. Read-only.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not entirely true since users can populate it at creation time, right?

Copy link
Contributor Author

@lukaszo lukaszo Feb 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will prepare a follow up PR with a fix

}
available := false
for _, pod := range daemonPods {
if v1.IsPodAvailable(pod, ds.Spec.MinReadySeconds, metav1.Now()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getNodesToDaemonPods does not filter out terminating pods and I don't think IsPodAvailable filter them out either. You should consider them as unavailable so before this check you probably want to continue on deletiontimestamp != nil.

@0xmichalis
Copy link
Contributor

@k8s-bot unit test this issue #39471

@0xmichalis 0xmichalis added lgtm "Looks good to me", indicates that a PR is ready to be merged. priority/P2 labels Feb 27, 2017
@0xmichalis
Copy link
Contributor

0xmichalis commented Feb 27, 2017

P2 because it has been rebased a couple of times already and needs to land sooner than most of the PRs in the queue.

@lukaszo
Copy link
Contributor Author

lukaszo commented Feb 27, 2017

@k8s-bot unit test this

@k8s-github-robot
Copy link

Automatic merge from submit-queue (batch tested with PRs 41116, 41804, 42104, 42111, 42120)

@k8s-github-robot k8s-github-robot merged commit 73c9fd8 into kubernetes:master Feb 27, 2017
jcbsmpsn pushed a commit to jcbsmpsn/kubernetes that referenced this pull request Mar 10, 2017
Automatic merge from submit-queue (batch tested with PRs 42024, 42780, 42808, 42640)

kubectl: respect DaemonSet strategy parameters for rollout status

It handles "after-merge" comments from kubernetes#41116

cc @Kargakis @janetkuo 

I will add one more e2e test later. I need to handle some in company stuff.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API lgtm "Looks good to me", indicates that a PR is ready to be merged. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet