// Copyright 2019 Drone IO, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // +build !oss package converter import ( "context" "fmt" "github.com/drone/drone/core" lru "github.com/hashicorp/golang-lru" "github.com/sirupsen/logrus" ) // cache key pattern used in the cache, comprised of the // repository slug and commit sha. const keyf = "%d|%s|%s|%s|%s|%s" // Memoize caches the conversion results for subsequent calls. // This micro-optimization is intended for multi-pipeline // projects that would otherwise covert the file for each // pipeline execution. func Memoize(base core.ConvertService, size int) core.ConvertService { // simple cache prevents the same yaml file from being // requested multiple times in a short period. cache, _ := lru.New(10) return &memoize{base: base, cache: cache, size: size} } type memoize struct { base core.ConvertService cache *lru.Cache size int } func (c *memoize) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) { // this is a minor optimization that prevents caching if the // base converter is a remote converter and is disabled. if remote, ok := c.base.(*remote); ok == true && remote.client == nil { return nil, nil } // the client can optionally disable cacheing. if c.size == 0 { return c.base.Convert(ctx, req) } // generate the key used to cache the converted file. key := fmt.Sprintf(keyf, req.Repo.ID, req.Build.Event, req.Build.Action, req.Build.Ref, req.Build.After, req.Repo.Config, ) logger := logrus.WithField("repo", req.Repo.Slug). WithField("build", req.Build.Event). WithField("action", req.Build.Action). WithField("ref", req.Build.Ref). WithField("rev", req.Build.After). WithField("config", req.Repo.Config) logger.Trace("extension: conversion: check cache") // check the cache for the file and return if exists. cached, ok := c.cache.Get(key) if ok { logger.Trace("extension: conversion: cache hit") return cached.(*core.Config), nil } logger.Trace("extension: conversion: cache miss") // else convert the configuration file. config, err := c.base.Convert(ctx, req) if err != nil { return nil, err } if config == nil { return nil, nil } if config.Data == "" { return nil, nil } // if the configuration file was converted // it is temporarily cached. Note that we do // not cache if the commit sha is empty (gogs). if req.Build.After != "" { c.cache.Add(key, config) } return config, nil }