github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/model/patch/patch.go (about)

     1  package patch
     2  
     3  import (
     4  	"io/ioutil"
     5  	"time"
     6  
     7  	"github.com/evergreen-ci/evergreen"
     8  	"github.com/evergreen-ci/evergreen/db"
     9  	"gopkg.in/mgo.v2"
    10  	"gopkg.in/mgo.v2/bson"
    11  )
    12  
    13  // Hard limit on patch size.
    14  const SizeLimit = 1024 * 1024 * 100
    15  
    16  // VariantTasks contains the variant ID and  the set of tasks to be scheduled for that variant
    17  type VariantTasks struct {
    18  	Variant string
    19  	Tasks   []string
    20  }
    21  
    22  // Stores all details related to a patch request
    23  type Patch struct {
    24  	Id            bson.ObjectId  `bson:"_id,omitempty"`
    25  	Description   string         `bson:"desc"`
    26  	Project       string         `bson:"branch"`
    27  	Githash       string         `bson:"githash"`
    28  	PatchNumber   int            `bson:"patch_number"`
    29  	Author        string         `bson:"author"`
    30  	Version       string         `bson:"version"`
    31  	Status        string         `bson:"status"`
    32  	CreateTime    time.Time      `bson:"create_time"`
    33  	StartTime     time.Time      `bson:"start_time"`
    34  	FinishTime    time.Time      `bson:"finish_time"`
    35  	BuildVariants []string       `bson:"build_variants"`
    36  	Tasks         []string       `bson:"tasks"`
    37  	VariantsTasks []VariantTasks `bson:"variants_tasks"`
    38  	Patches       []ModulePatch  `bson:"patches"`
    39  	Activated     bool           `bson:"activated"`
    40  	PatchedConfig string         `bson:"patched_config"`
    41  }
    42  
    43  // this stores request details for a patch
    44  type ModulePatch struct {
    45  	ModuleName string   `bson:"name"`
    46  	Githash    string   `bson:"githash"`
    47  	PatchSet   PatchSet `bson:"patch_set"`
    48  }
    49  
    50  // this stores information about the actual patch
    51  type PatchSet struct {
    52  	Patch       string    `bson:"patch,omitempty"`
    53  	PatchFileId string    `bson:"patch_file_id,omitempty"`
    54  	Summary     []Summary `bson:"summary"`
    55  }
    56  
    57  // this stores summary patch information
    58  type Summary struct {
    59  	Name      string `bson:"filename"`
    60  	Additions int    `bson:"additions"`
    61  	Deletions int    `bson:"deletions"`
    62  }
    63  
    64  func (p *Patch) SetDescription(desc string) error {
    65  	p.Description = desc
    66  	return UpdateOne(
    67  		bson.M{IdKey: p.Id},
    68  		bson.M{
    69  			"$set": bson.M{
    70  				DescriptionKey: desc,
    71  			},
    72  		},
    73  	)
    74  }
    75  
    76  // ClearPatchData removes any inline patch data stored in this patch object for patches that have
    77  // an associated id in gridfs, so that it can be stored properly.
    78  func (p *Patch) ClearPatchData() {
    79  	for i, patchPart := range p.Patches {
    80  		// If the patch isn't stored externally, no need to do anything.
    81  		if patchPart.PatchSet.PatchFileId != "" {
    82  			p.Patches[i].PatchSet.Patch = ""
    83  		}
    84  	}
    85  }
    86  
    87  // FetchPatchFiles dereferences externally-stored patch diffs by fetching them from gridfs
    88  // and placing their contents into the patch object.
    89  func (p *Patch) FetchPatchFiles() error {
    90  	for i, patchPart := range p.Patches {
    91  		// If the patch isn't stored externally, no need to do anything.
    92  		if patchPart.PatchSet.PatchFileId == "" {
    93  			continue
    94  		}
    95  
    96  		file, err := db.GetGridFile(GridFSPrefix, patchPart.PatchSet.PatchFileId)
    97  		if err != nil {
    98  			return err
    99  		}
   100  		defer file.Close()
   101  		raw, err := ioutil.ReadAll(file)
   102  		if err != nil {
   103  			return err
   104  		}
   105  		p.Patches[i].PatchSet.Patch = string(raw)
   106  	}
   107  	return nil
   108  }
   109  
   110  // SyncVariantsTasks updates the patch's Tasks and BuildVariants fields to match with the set
   111  // in the given list of VariantTasks. This is to ensure schema backwards compatibility for T shaped
   112  // patches. This mutates the patch in memory but does not update it in the database; for that, use
   113  // SetVariantsTasks.
   114  func (p *Patch) SyncVariantsTasks(variantsTasks []VariantTasks) {
   115  	taskSet := map[string]bool{}
   116  	variantSet := map[string]bool{}
   117  
   118  	// TODO after fully switching over to new schema, remove support for standalone
   119  	// Variants and Tasks field
   120  	for _, v := range variantsTasks {
   121  		variantSet[v.Variant] = true
   122  		for _, t := range v.Tasks {
   123  			taskSet[t] = true
   124  		}
   125  	}
   126  	tasks := []string{}
   127  	variants := []string{}
   128  	for k := range variantSet {
   129  		variants = append(variants, k)
   130  	}
   131  
   132  	for k := range taskSet {
   133  		tasks = append(tasks, k)
   134  	}
   135  
   136  	p.VariantsTasks = variantsTasks
   137  	p.Tasks = tasks
   138  	p.BuildVariants = variants
   139  }
   140  
   141  // Updates the variant/tasks pairs in the database.
   142  // Also updates the Tasks and Variants fields to maintain backwards compatibility between
   143  // the old and new fields.
   144  func (p *Patch) SetVariantsTasks(variantsTasks []VariantTasks) error {
   145  	p.SyncVariantsTasks(variantsTasks)
   146  	return UpdateOne(
   147  		bson.M{IdKey: p.Id},
   148  		bson.M{
   149  			"$set": bson.M{
   150  				VariantsTasksKey: variantsTasks,
   151  				BuildVariantsKey: p.BuildVariants,
   152  				TasksKey:         p.Tasks,
   153  			},
   154  		},
   155  	)
   156  }
   157  
   158  // AddBuildVariants adds more buildvarints to a patch document.
   159  // This is meant to be used after initial patch creation.
   160  func (p *Patch) AddBuildVariants(bvs []string) error {
   161  	change := mgo.Change{
   162  		Update: bson.M{
   163  			"$addToSet": bson.M{BuildVariantsKey: bson.M{"$each": bvs}},
   164  		},
   165  		ReturnNew: true,
   166  	}
   167  	_, err := db.FindAndModify(Collection, bson.M{IdKey: p.Id}, nil, change, p)
   168  	return err
   169  }
   170  
   171  // AddTasks adds more tasks to a patch document.
   172  // This is meant to be used after initial patch creation, to reconfigure the patch.
   173  func (p *Patch) AddTasks(tasks []string) error {
   174  	change := mgo.Change{
   175  		Update: bson.M{
   176  			"$addToSet": bson.M{TasksKey: bson.M{"$each": tasks}},
   177  		},
   178  		ReturnNew: true,
   179  	}
   180  	_, err := db.FindAndModify(Collection, bson.M{IdKey: p.Id}, nil, change, p)
   181  	return err
   182  }
   183  
   184  // TryMarkStarted attempts to mark a patch as started if it
   185  // isn't already marked as such
   186  func TryMarkStarted(versionId string, startTime time.Time) error {
   187  	filter := bson.M{
   188  		VersionKey: versionId,
   189  		StatusKey:  evergreen.PatchCreated,
   190  	}
   191  	update := bson.M{
   192  		"$set": bson.M{
   193  			StartTimeKey: startTime,
   194  			StatusKey:    evergreen.PatchStarted,
   195  		},
   196  	}
   197  	err := UpdateOne(filter, update)
   198  	if err == mgo.ErrNotFound {
   199  		return nil
   200  	}
   201  	return err
   202  }
   203  
   204  // TryMarkFinished attempts to mark a patch of a given version as finished.
   205  func TryMarkFinished(versionId string, finishTime time.Time, status string) error {
   206  	filter := bson.M{VersionKey: versionId}
   207  	update := bson.M{
   208  		"$set": bson.M{
   209  			FinishTimeKey: finishTime,
   210  			StatusKey:     status,
   211  		},
   212  	}
   213  	return UpdateOne(filter, update)
   214  }
   215  
   216  // Insert inserts the patch into the db, returning any errors that occur
   217  func (p *Patch) Insert() error {
   218  	return db.Insert(Collection, p)
   219  }
   220  
   221  // ConfigChanged looks through the parts of the patch and returns true if the
   222  // passed in remotePath is in the the name of the changed files that are part
   223  // of the patch
   224  func (p *Patch) ConfigChanged(remotePath string) bool {
   225  	for _, patchPart := range p.Patches {
   226  		if patchPart.ModuleName == "" {
   227  			for _, summary := range patchPart.PatchSet.Summary {
   228  				if summary.Name == remotePath {
   229  					return true
   230  				}
   231  			}
   232  			return false
   233  		}
   234  	}
   235  	return false
   236  }
   237  
   238  // SetActivated sets the patch to activated in the db
   239  func (p *Patch) SetActivated(versionId string) error {
   240  	p.Version = versionId
   241  	p.Activated = true
   242  	return UpdateOne(
   243  		bson.M{IdKey: p.Id},
   244  		bson.M{
   245  			"$set": bson.M{
   246  				ActivatedKey: true,
   247  				VersionKey:   versionId,
   248  			},
   249  		},
   250  	)
   251  
   252  }
   253  
   254  // Add or update a module within a patch.
   255  func (p *Patch) UpdateModulePatch(modulePatch ModulePatch) error {
   256  	// check that a patch for this module exists
   257  	query := bson.M{
   258  		IdKey: p.Id,
   259  		PatchesKey + "." + ModulePatchNameKey: modulePatch.ModuleName,
   260  	}
   261  	update := bson.M{
   262  		"$set": bson.M{PatchesKey + ".$": modulePatch},
   263  	}
   264  	result, err := UpdateAll(query, update)
   265  	if err != nil {
   266  		return err
   267  	}
   268  
   269  	// The patch already existed in the array, and it's been updated.
   270  	if result.Updated > 0 {
   271  		return nil
   272  	}
   273  
   274  	//it wasn't in the array, we need to add it.
   275  	query = bson.M{IdKey: p.Id}
   276  	update = bson.M{
   277  		"$push": bson.M{PatchesKey: modulePatch},
   278  	}
   279  	return UpdateOne(query, update)
   280  }
   281  
   282  // RemoveModulePatch removes a module that's part of a patch request
   283  func (p *Patch) RemoveModulePatch(moduleName string) error {
   284  	// check that a patch for this module exists
   285  	query := bson.M{
   286  		IdKey: p.Id,
   287  	}
   288  	update := bson.M{
   289  		"$pull": bson.M{
   290  			PatchesKey: bson.M{ModulePatchNameKey: moduleName},
   291  		},
   292  	}
   293  	return UpdateOne(query, update)
   294  }