github.com/devtron-labs/ci-runner@v0.0.0-20240518055909-b2672f3349d7/helper/YamlTaskHelper.go (about)

     1  /*
     2   *  Copyright 2020 Devtron Labs
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17  
    18  package helper
    19  
    20  import (
    21  	"errors"
    22  	"io/ioutil"
    23  	"log"
    24  	"os"
    25  	"path/filepath"
    26  	"regexp"
    27  
    28  	"github.com/devtron-labs/ci-runner/util"
    29  	"gopkg.in/yaml.v2"
    30  )
    31  
    32  type Task struct {
    33  	Id             int    `json:"id"`
    34  	Index          int    `json:"index"`
    35  	Name           string `json:"name" yaml:"name"`
    36  	Script         string `json:"script" yaml:"script"`
    37  	OutputLocation string `json:"outputLocation" yaml:"outputLocation"` // file/dir
    38  	RunStatus      bool   `json:"-"`                                    // task run was attempted or not
    39  }
    40  
    41  type TaskYaml struct {
    42  	Version          string             `yaml:"version"`
    43  	PipelineConf     []PipelineConfig   `yaml:"pipelineConf"`
    44  	CdPipelineConfig []CdPipelineConfig `yaml:"cdPipelineConf"`
    45  }
    46  
    47  type PipelineConfig struct {
    48  	AppliesTo   []AppliesTo `yaml:"appliesTo"`
    49  	BeforeTasks []*Task     `yaml:"beforeDockerBuildStages"`
    50  	AfterTasks  []*Task     `yaml:"afterDockerBuildStages"`
    51  }
    52  
    53  type CdPipelineConfig struct {
    54  	BeforeTasks []*Task `yaml:"beforeStages"`
    55  	AfterTasks  []*Task `yaml:"afterStages"`
    56  }
    57  
    58  type AppliesTo struct {
    59  	Type  string   `yaml:"type"`
    60  	Value []string `yaml:"value"`
    61  }
    62  
    63  const BRANCH_FIXED = "BRANCH_FIXED"
    64  
    65  func GetBeforeDockerBuildTasks(ciRequest *CommonWorkflowRequest, taskYaml *TaskYaml) ([]*Task, error) {
    66  	if taskYaml == nil {
    67  		log.Println(util.DEVTRON, "no tasks, devtron-ci yaml missing")
    68  		return nil, nil
    69  	}
    70  
    71  	if taskYaml.Version != "0.0.1" {
    72  		log.Println("invalid version for devtron-ci.yaml")
    73  		return nil, errors.New("invalid version for devtron-ci.yaml")
    74  	}
    75  
    76  	pipelineConfig := taskYaml.PipelineConf
    77  	log.Println(util.DEVTRON, "pipelineConf length: ", len(pipelineConfig))
    78  
    79  	var tasks []*Task
    80  	filteredOut := false
    81  	for _, p := range pipelineConfig {
    82  		if filteredOut {
    83  			break
    84  		}
    85  		for _, a := range p.AppliesTo {
    86  			triggerType := a.Type
    87  			if triggerType == BRANCH_FIXED {
    88  				branches := a.Value
    89  				branchesMap := make(map[string]bool)
    90  				for _, b := range branches {
    91  					branchesMap[b] = true
    92  				}
    93  				if !isValidBranch(ciRequest, a) {
    94  					log.Println(util.DEVTRON, "skipping current AppliesTo")
    95  					continue
    96  				}
    97  				tasks = append(tasks, p.BeforeTasks...)
    98  				filteredOut = true
    99  			} else {
   100  				log.Println(util.DEVTRON, "unknown triggerType ", triggerType)
   101  			}
   102  		}
   103  
   104  	}
   105  	return tasks, nil
   106  }
   107  
   108  func GetAfterDockerBuildTasks(ciRequest *CommonWorkflowRequest, taskYaml *TaskYaml) ([]*Task, error) {
   109  	if taskYaml == nil {
   110  		log.Println(util.DEVTRON, "no tasks, devtron-ci yaml missing")
   111  		return nil, nil
   112  	}
   113  
   114  	if taskYaml.Version != "0.0.1" { // TODO: Get version from ciRequest based on ci_pipeline
   115  		log.Println("invalid version for devtron-ci.yaml")
   116  		return nil, errors.New("invalid version for devtron-ci.yaml")
   117  	}
   118  
   119  	pipelineConfig := taskYaml.PipelineConf
   120  	log.Println(util.DEVTRON, "pipelineConf length: ", len(pipelineConfig))
   121  
   122  	var tasks []*Task
   123  	filteredOut := false
   124  	for _, p := range pipelineConfig {
   125  		if filteredOut {
   126  			break
   127  		}
   128  		for _, a := range p.AppliesTo {
   129  			triggerType := a.Type
   130  			if triggerType == BRANCH_FIXED {
   131  				isValidSourceType := true
   132  				for _, p := range ciRequest.CiProjectDetails {
   133  					// SOURCE_TYPE_WEBHOOK is not yet supported for pre-ci-stages. so handling here to get rid of fatal
   134  					if p.SourceType != SOURCE_TYPE_BRANCH_FIXED && p.SourceType != SOURCE_TYPE_WEBHOOK {
   135  						log.Println(util.DEVTRON, "skipping invalid source type")
   136  						isValidSourceType = false
   137  						break
   138  					}
   139  				}
   140  				if isValidSourceType {
   141  					if !isValidBranch(ciRequest, a) {
   142  						log.Println(util.DEVTRON, "skipping current AppliesTo")
   143  						continue
   144  					}
   145  					tasks = append(tasks, p.AfterTasks...)
   146  					filteredOut = true
   147  				}
   148  			} else {
   149  				log.Println(util.DEVTRON, "unknown triggerType ", triggerType)
   150  			}
   151  		}
   152  	}
   153  	return tasks, nil
   154  }
   155  
   156  func isValidBranch(ciRequest *CommonWorkflowRequest, a AppliesTo) bool {
   157  	branches := a.Value
   158  	branchesMap := make(map[string]bool)
   159  	for _, b := range branches {
   160  		branchesMap[b] = true
   161  	}
   162  	isValidBranch := true
   163  	for _, prj := range ciRequest.CiProjectDetails {
   164  		if _, ok := branchesMap[prj.SourceValue]; !ok {
   165  			log.Println(util.DEVTRON, "invalid branch")
   166  			isValidBranch = false
   167  			break
   168  		}
   169  	}
   170  	return isValidBranch
   171  }
   172  
   173  func isValidTag(ciRequest *CommonWorkflowRequest, a AppliesTo) bool {
   174  	tagsRegex := a.Value
   175  	for _, prj := range ciRequest.CiProjectDetails {
   176  		for _, t := range tagsRegex {
   177  			match, _ := regexp.MatchString(t, prj.GitTag)
   178  			if match {
   179  				return true
   180  			}
   181  		}
   182  	}
   183  	return false
   184  }
   185  
   186  func GetTaskYaml(yamlLocation string) (*TaskYaml, error) {
   187  	filename := filepath.Join(yamlLocation, "devtron-ci.yaml")
   188  	if _, err := os.Stat(filename); os.IsNotExist(err) {
   189  		log.Println("file not found", filename)
   190  		return nil, nil
   191  	}
   192  
   193  	yamlFile, err := ioutil.ReadFile(filename)
   194  	if err != nil {
   195  		log.Println(err)
   196  		return nil, err
   197  	}
   198  
   199  	if yamlFile == nil || string(yamlFile) == "" {
   200  		log.Println("file not found", filename)
   201  		return nil, nil
   202  	}
   203  
   204  	taskYaml, err := ToTaskYaml(yamlFile)
   205  	if err != nil {
   206  		log.Println(err)
   207  		return nil, err
   208  	}
   209  
   210  	log.Println("yaml version: ", taskYaml.Version)
   211  	return taskYaml, nil
   212  }
   213  
   214  func ToTaskYaml(yamlFile []byte) (*TaskYaml, error) {
   215  	taskYaml := &TaskYaml{}
   216  	err := yaml.Unmarshal(yamlFile, taskYaml)
   217  	return taskYaml, err
   218  }