...

Source file src/edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/policy/approval/evaluate.go

Documentation: edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/policy/approval

     1  // Copyright 2018 Palantir Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package approval
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  
    21  	"github.com/rs/zerolog"
    22  
    23  	"edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/pull"
    24  
    25  	"edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/policy/common"
    26  )
    27  
    28  type evaluator struct {
    29  	root common.Evaluator
    30  }
    31  
    32  func (eval *evaluator) Trigger() common.Trigger {
    33  	if eval.root != nil {
    34  		return eval.root.Trigger()
    35  	}
    36  	return common.TriggerStatic
    37  }
    38  
    39  func (eval *evaluator) Evaluate(ctx context.Context, prctx pull.Context) (res common.Result) {
    40  	if eval.root != nil {
    41  		res = eval.root.Evaluate(ctx, prctx)
    42  	} else {
    43  		zerolog.Ctx(ctx).Debug().Msg("No approval policy defined; skipping")
    44  
    45  		res.Status = common.StatusApproved
    46  		res.StatusDescription = "No approval policy defined"
    47  	}
    48  
    49  	res.Name = "approval"
    50  	return
    51  }
    52  
    53  type RuleRequirement struct {
    54  	rule *Rule
    55  }
    56  
    57  func (r *RuleRequirement) Trigger() common.Trigger {
    58  	return r.rule.Trigger()
    59  }
    60  
    61  func (r *RuleRequirement) Evaluate(ctx context.Context, prctx pull.Context) common.Result {
    62  	log := zerolog.Ctx(ctx).With().Str("rule", r.rule.Name).Logger()
    63  	ctx = log.WithContext(ctx)
    64  
    65  	result := r.rule.Evaluate(ctx, prctx)
    66  	if result.Error == nil {
    67  		log.Debug().Msgf("rule evaluation resulted in %s:\"%s\"", result.Status, result.StatusDescription)
    68  	}
    69  
    70  	return result
    71  }
    72  
    73  type OrRequirement struct {
    74  	requirements []common.Evaluator
    75  }
    76  
    77  func (r *OrRequirement) Trigger() common.Trigger {
    78  	var t common.Trigger
    79  	for _, child := range r.requirements {
    80  		t |= child.Trigger()
    81  	}
    82  	return t
    83  }
    84  
    85  func (r *OrRequirement) Evaluate(ctx context.Context, prctx pull.Context) common.Result {
    86  	var children []*common.Result
    87  	for _, req := range r.requirements {
    88  		res := req.Evaluate(ctx, prctx)
    89  		children = append(children, &res)
    90  	}
    91  
    92  	var err error
    93  	var pending, approved, skipped int
    94  	for _, c := range children {
    95  		if c.Error != nil {
    96  			err = c.Error
    97  			continue
    98  		}
    99  
   100  		switch c.Status {
   101  		case common.StatusApproved:
   102  			approved++
   103  		case common.StatusPending:
   104  			pending++
   105  		case common.StatusSkipped:
   106  			skipped++
   107  		}
   108  	}
   109  
   110  	var status common.EvaluationStatus
   111  	description := "All of the rules are skipped"
   112  
   113  	switch {
   114  	case approved > 0:
   115  		status = common.StatusApproved
   116  		description = "One or more rules approved"
   117  		err = nil
   118  	case pending > 0:
   119  		status = common.StatusPending
   120  		description = "None of the rules are satisfied"
   121  		err = nil
   122  	}
   123  
   124  	return common.Result{
   125  		Name:              "or",
   126  		Status:            status,
   127  		StatusDescription: description,
   128  		Error:             err,
   129  		Children:          children,
   130  	}
   131  }
   132  
   133  type AndRequirement struct {
   134  	requirements []common.Evaluator
   135  }
   136  
   137  func (r *AndRequirement) Trigger() common.Trigger {
   138  	var t common.Trigger
   139  	for _, child := range r.requirements {
   140  		t |= child.Trigger()
   141  	}
   142  	return t
   143  }
   144  
   145  func (r *AndRequirement) Evaluate(ctx context.Context, prctx pull.Context) common.Result {
   146  	var children []*common.Result
   147  	for _, req := range r.requirements {
   148  		res := req.Evaluate(ctx, prctx)
   149  		children = append(children, &res)
   150  	}
   151  
   152  	var err error
   153  	var pending, approved, skipped int
   154  	for _, c := range children {
   155  		if c.Error != nil {
   156  			err = c.Error
   157  			continue
   158  		}
   159  
   160  		switch c.Status {
   161  		case common.StatusApproved:
   162  			approved++
   163  		case common.StatusPending:
   164  			pending++
   165  		case common.StatusSkipped:
   166  			skipped++
   167  		}
   168  	}
   169  
   170  	var status common.EvaluationStatus
   171  	description := "All of the rules are skipped"
   172  
   173  	switch {
   174  	case approved > 0 && pending == 0:
   175  		status = common.StatusApproved
   176  		description = fmt.Sprintf("All rules are approved")
   177  	case pending > 0:
   178  		status = common.StatusPending
   179  		description = fmt.Sprintf("%d/%d rules approved", approved, approved+pending)
   180  	}
   181  
   182  	return common.Result{
   183  		Name:              "and",
   184  		Status:            status,
   185  		StatusDescription: description,
   186  		Error:             err,
   187  		Children:          children,
   188  	}
   189  }
   190  

View as plain text