// Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file 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. package cache import ( "encoding/gob" "os" "path/filepath" "runtime" "github.com/aws/amazon-ecs-cli/ecs-cli/modules/config" "github.com/aws/amazon-ecs-cli/ecs-cli/modules/utils" ) // available for injecting mocks for testing var osOpen = os.Open var osCreate = os.Create var osMkdirAll = os.MkdirAll var getOSName = func() string { return runtime.GOOS } // rw only for the user in-line with how most programs setup their cache // directories. Also, this directory could container sensitive-ish files due to // environment variables, so keep it user-only. const cacheDirMode = 0700 const cachePrefix = "ecs-cli" // cacheDir is a helper function to return the 'cache' directory for a given // application name func cacheDir(name string) (string, error) { homedir, err := utils.GetHomeDir() if err != nil { return "", err } path := filepath.Join(homedir, ".cache", cachePrefix, name) if getOSName() == "windows" { path = filepath.Join(homedir, config.GetWindowsBaseDataPath(), "cache", name) } return path, nil } type fsCache struct { name string cacheDir string } // NewFSCache returns a new cache backed by the filesystem. The 'name' value // should be constant in order to access the same data between uses. // The cache will be namespaced under this project func NewFSCache(name string) (Cache, error) { dir, err := cacheDir(name) if err != nil { return nil, err } err = osMkdirAll(dir, cacheDirMode) if err != nil { return nil, err } return &fsCache{ name: name, cacheDir: dir, }, nil } func (cache *fsCache) Put(key string, val interface{}) (retErr error) { file, err := osCreate(filepath.Join(cache.cacheDir, key)) if err != nil { return err } defer func() { closeErr := file.Close() // Avoid masking the 'gob.Encode' error, it's earlier and probably more specific if closeErr != nil && retErr == nil { // named return error retErr = closeErr } }() valEnc := gob.NewEncoder(file) return valEnc.Encode(val) } func (cache *fsCache) Get(key string, i interface{}) error { file, err := osOpen(filepath.Join(cache.cacheDir, key)) if err != nil { return err } defer file.Close() valDec := gob.NewDecoder(file) return valDec.Decode(i) }