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

Adds ability to set required and optional checks #7983

Merged
merged 5 commits into from
May 17, 2018

Conversation

sebastienvas
Copy link
Contributor

@sebastienvas sebastienvas commented May 9, 2018

Redoing #7457 as it takes forever to load.

Ref: https://goo.gl/cd9MEu which was discussed with @fejta @cjwagner @BenTheElder

Instead of getting required checks from the github api, it will use the branch protection configuration and infere the required context from it. We are also adding a optional options to Prow Presubmits.

Config would look like this:

...
tide:
  ...
  context_options:
    policy:
      # Use branch protection options to define required and optional contexts
      from-branch-protection: true
      # Treat unknown contexts as optional
      skip-unknown-contexts: true
    orgs:
      org:
        repo:
          branch:
            policy:
              from-branch-protection: false
              required-contexts:
              - "required_test"
              optional-contexts:
              - "optional_test"
  

#7140

@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels May 9, 2018
@stevekuznetsov
Copy link
Contributor

/ok-to-test
/area prow
/kind feature

@k8s-ci-robot k8s-ci-robot added area/prow Issues or PRs related to prow kind/feature Categorizes issue or PR as related to a new feature. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels May 9, 2018
}
return required
return
Copy link
Contributor

Choose a reason for hiding this comment

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

-1 to named returns

Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed on not using named returns

Copy link
Contributor Author

Choose a reason for hiding this comment

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

May I ask why ?

Copy link
Contributor

Choose a reason for hiding this comment

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

harder to maintain, it's not obvious what's the returned value

Copy link
Contributor

Choose a reason for hiding this comment

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

I find almost universally they are harder to reason about and read through. They are almost nonexistant in the codebase as it is. Stats:

$ grep -rh func prow/ | grep -Po "(?<=\) \()[^\(\)]+(?=\) \{)" | tr ',' '\n' | awk '{print $2 }' | grep -v "^$" | wc -l 
19
$ grep -rh func prow/ | wc -l 
2084

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 actually discovered them in k8s codebase :) but right it may be confusing ! fixed.

p, ok := presubmits[org+"/"+repo]
if !ok {
return nil
return
Copy link
Contributor

Choose a reason for hiding this comment

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

-1 to named returns

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@@ -298,11 +298,11 @@ func TestJobRequirements(t *testing.T) {
}

for _, tc := range cases {
masterActual := jobRequirements(tc.config, "master", false)
masterActual, _ := jobRequirements(tc.config, "master", false)
Copy link
Contributor

Choose a reason for hiding this comment

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

extend the test to test both?

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

@@ -22,6 +22,8 @@ branch-protection:
# Ensure that the extra-process-followed github status context passes.
# In addition, adds any required prow jobs (aka always_run: true)
require-contexts: ["extra-process-followed"]
# Tell Tide to merge when unspecified contexts are pending or failing
skip-unknown-contexts: true
Copy link
Contributor

Choose a reason for hiding this comment

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

Configuration for tide actions should not live in the branch protector's config.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1, this should be part of the job spec

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For prow jobs we already have the optional setting. For optional jobs that are not prowjob we need to define it somewhere. I had a global setting in tide configuration, but that was not practical. It makes sense for me to define it in branch protection since this option only works when branch protection is set and you might want to apply this globally, on a repo or on a branch which branch protection already does. I had discussed this with @cjwagner

Copy link
Contributor Author

Choose a reason for hiding this comment

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

also skipunknown context does not have any meaning if we don't have a branch protection, meaning no jobs defined at all in Prow config since all are unknown.

Copy link
Contributor

Choose a reason for hiding this comment

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

but that was not practical.

Why not? It's a simple mapping with the same parent/child scheme as in this config. I would expect it to live in tide config exclusively. It's configuring tide to run. Members can protect their branches without using the branch-protector and can run the branch-protector without running tide.

Copy link
Contributor Author

@sebastienvas sebastienvas May 10, 2018

Choose a reason for hiding this comment

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

So the original idea was to use the github required check to figure that out, but that was doing too many request to the github api. After a meeting with the @fejta, @cjwagner and @BenTheElder it was decided to use branch protection instead. You don't have to actually enforce branch protection, you can just use the config and set protect to false, and then use that to define the required checks that you required for a PR to be merged. I don't see the point of duplicating those configs since they map back to protecting a branch. I think the Prow config is big and complicated enough.

Copy link
Contributor

Choose a reason for hiding this comment

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

You don't have to actually enforce branch protection, you can just use the config and set protect to false, and then use that to define the required checks that you required for a PR to be merged.

Err, what? If you want to configure branch protection for statuses, do that. Then let tide honor required statuses. If you want to configure statuses for tide, do that, and don't touch the branch protection config (which you might have never even written since you don't use the branch protector). IDK. These seem like two entirely separate use cases that are being put in one place.

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 agree I am defining a tide options which will basically allow you to use branch protection or otherwise allow you to define your own if you don't want to use branch protection. If that option is not set, it will use combined status from github as it is today.


"github.com/shurcooL/githubql"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/test-infra/prow/config"
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: indent of this repo should be in separate block at bottom

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

string(pr.BaseRef.Name),
sc.ca.Config())
if err != nil {
log.WithError(err).Error("Getting branch protection")
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: bad error

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

var failed []Context
for _, ctx := range contexts {
if string(ctx.Context) == statusContext {
if cc.ignoreContext(ctx) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this seems like a regression if we mark the tide status required

Copy link
Contributor Author

Choose a reason for hiding this comment

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

cc already ignores tide

@@ -944,7 +954,7 @@ func (c *Controller) presubmitsByPull(sp subpool) (map[int]sets.String, error) {
}()

for _, ps := range c.ca.Config().Presubmits[sp.org+"/"+sp.repo] {
if ps.SkipReport || !ps.RunsAgainstBranch(sp.branch) {
if !ps.ContextRequired() || !ps.RunsAgainstBranch(sp.branch) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we changing this functionality? If we want to do this, let's do it in a separate PR?

Copy link
Contributor Author

@sebastienvas sebastienvas May 10, 2018

Choose a reason for hiding this comment

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

The goal of this PR is to allow optional jobs to be skipped for tide. So yes this is in scope.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right now we have the SkipReport as the means by which we accomplish that. Why do we want to change that right now? Can we continue honoring the SkipReport as well? Breaking changes for cluster admins are annoying.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason is that SkipReport = false does not mean the check is required. In our use case you might want to push a status on a PR for a test that is not required, this is the reason I added Optional a while back. Optional is false by default so it will not impact anything, but then if you define a prowjob with Optional set to true, you defintely want to skip it to merge a PR. So this is the reason I added this.

Copy link
Contributor

Choose a reason for hiding this comment

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

I understand they are not identical features, what I am saying is today we use SkipReport to mean "tide ignore" because whether or not it's required, if it's not reported, tide ignores it. As long as SkipReport=true continues to mean that tide ignores the job, but now in addition, SkipReport=false Optional=true works, that is OK.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok.

combinedContexts map[string]string
}{
{
name: "required checks disabled should not impact - passing",
Copy link
Contributor

Choose a reason for hiding this comment

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

these names are a bit confusing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks for pointing that out. After reviewing some of the test I discovered some issues.

{
name: "required checks disabled should not impact - passing",
passing: true,
config: &config.Policy{
Copy link
Contributor

Choose a reason for hiding this comment

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

@fejta is it even valid config to have a policy with explicitly listed required checks but have the policy be set to not enforce? why?

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 think there could multiple use cases. When you define policy at the repo or org level, and then you create a branch for testing and you don't want to protect it or if you just want to disable temporarily.

@sebastienvas
Copy link
Contributor Author

PTAL

@stevekuznetsov
Copy link
Contributor

Please no merge commits. Rebase on master

@sebastienvas
Copy link
Contributor Author

@stevekuznetsov I think we got it. I think you are going to like this.

Copy link
Contributor Author

@sebastienvas sebastienvas left a comment

Choose a reason for hiding this comment

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

PTAL


// contextRegister implements contextChecker and allow registering of required and optional contexts.
type contextRegister struct {
lock sync.RWMutex
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 had it since tide code is using multiple go routine, but since the object is created inside a funciton, the lock is overkill.

optional: sets.NewString(),
skipUnknownContexts: skipUnknownContexts,
}
r.registerOptionalContexts(statusContext)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

it is coming from tide.go (its the tide status). I know the name is not very well chosen, but I am trying to do the least amount of changes.

// - context is registered as optional
// - required contexts are registered and the context provided is not required
// Will return false otherwise. Every context is required.
func (r *contextRegister) ignoreContext(c Context) bool {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we do, that s tide status. its registered by default. This implementation could be use in another context, so I did not want to hard code in there.

func (r *contextRegister) missingRequiredContexts(contexts []Context) []Context {
r.lock.RLock()
defer r.lock.RUnlock()
if r.required.Len() == 0 {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

it will but why bother doing all that when you already know the answer.

return b
}
c := TideContextPolicy{}
c.FromBranchProtection = mergeBool(c.FromBranchProtection, b.FromBranchProtection)
Copy link
Member

Choose a reason for hiding this comment

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

Merge a and b, not c and b.

Copy link
Member

Choose a reason for hiding this comment

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

Please also add a test case to catch this.

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

if bp == nil {
return TideContextPolicy{}, errors.New("branch protection is not set")
}
if bp.Protect == nil || !*bp.Protect || bp.RequiredStatusChecks == nil {
Copy link
Member

Choose a reason for hiding this comment

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

I don't think that we should consider this an error. If I set FromBranchProtection at the org level I wouldn't expect an error if some repo in the org does not have RequiredStatusChecks. In this case we should probably just not add anything to required?

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

}
required.Insert(bp.RequiredStatusChecks.Contexts...)
}

Copy link
Member

Choose a reason for hiding this comment

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

Please add a TideContextPolicy.Validate function and call it here to validate the policy before it is returned.
Specifically, validate that no required context is also an optional context.

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 and added tc

}

// contextRegister implements contextChecker and allow registering of required and optional contexts.
type contextRegister struct {
Copy link
Member

Choose a reason for hiding this comment

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

I don't think that we need this any more. Its functionality could be moved into the TideContextPolicy struct.
In other words, instead of constructing a contextRegister from a TideContextPolicy in order to get a contextChecker, just make the TideContextPolicy match the contextChecker interface itself.

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 tend to agree with you, but that would create circular dependency and I would have to move the Context struct out as an example. Plus I would have to hard code the tide status in there. I am trying to keep this as the minimum amount of change. However, I don't think that the context checker interface is something that we need. The implementation is very general and straightforward. What do you think ?

Copy link
Member

Choose a reason for hiding this comment

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

What is the circular dependency? The contextRegister is basically copying the fields from TideContextPolicy and defining new methods so I'd really prefer to condense these into one struct and just configure the packages and imports properly to avoid a circular dependency.

The tide status shouldn't need to be hard coded? It can be passed down from the tide package.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The circular deps is on the Context object defined in prow/tide/tide.go

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

@@ -991,6 +1002,10 @@ func (c *Controller) syncSubpool(sp subpool) (Pool, error) {
if err != nil {
return Pool{}, fmt.Errorf("error determining required presubmits: %v", err)
}
cr, err := newContextRegisterFromConfig(sp.org, sp.repo, sp.branch, c.ca.Config())
if err != nil {
return Pool{}, fmt.Errorf("error parsing branch protection: %v", err)
Copy link
Member

Choose a reason for hiding this comment

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

This error message is no longer accurate.

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

@k8s-ci-robot k8s-ci-robot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels May 16, 2018
@sebastienvas
Copy link
Contributor Author

@cjwagner Please take a look

Copy link
Member

@cjwagner cjwagner left a comment

Choose a reason for hiding this comment

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

Just a couple nits. This is looking ready.


// RegisterRequiredContexts register required contexts.
// Once required contexts are registered other contexts will be considered optional.
func (cp *TideContextPolicy) RegisterRequiredContexts(c ...string) {
Copy link
Member

Choose a reason for hiding this comment

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

This func is no longer needed.

}

// RegisterOptionalContexts registers optional contexts
func (cp *TideContextPolicy) RegisterOptionalContexts(c ...string) {
Copy link
Member

Choose a reason for hiding this comment

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

It might make more sense to just pass the tide context name to GetTideContextPolicy and have that func make the tide context required. That is all that RegisterOptionalContexts is used for now.

Not a big deal either way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can also just revert part of my change such that tide skip its own status. What do you think ?

@sebastienvas
Copy link
Contributor Author

@cjwagner Please take a look. I ll be deploying this today for istio. Will keep you up to date.

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 17, 2018
Copy link
Member

@cjwagner cjwagner left a comment

Choose a reason for hiding this comment

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

/lgtm

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: cjwagner, sebastienvas

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label May 17, 2018
@k8s-ci-robot k8s-ci-robot merged commit d387f27 into kubernetes:master May 17, 2018
@sebastienvas sebastienvas deleted the tide branch May 17, 2018 23:54
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. area/prow Issues or PRs related to prow cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/feature Categorizes issue or PR as related to a new feature. lgtm "Looks good to me", indicates that a PR is ready to be merged. 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.

5 participants