github.com/danielqsj/helm@v2.0.0-alpha.4.0.20160908204436-976e0ba5199b+incompatible/pkg/repo/repo.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors All rights reserved.
     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  package repo // import "k8s.io/helm/pkg/repo"
    18  
    19  import (
    20  	"crypto/sha1"
    21  	"errors"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"net/url"
    25  	"os"
    26  	"path/filepath"
    27  	"strings"
    28  	"time"
    29  
    30  	"gopkg.in/yaml.v2"
    31  
    32  	"k8s.io/helm/pkg/chartutil"
    33  )
    34  
    35  // ChartRepository represents a chart repository
    36  type ChartRepository struct {
    37  	RootPath   string
    38  	URL        string // URL of repository
    39  	ChartPaths []string
    40  	IndexFile  *IndexFile
    41  }
    42  
    43  // RepoFile represents the repositories.yaml file in $HELM_HOME
    44  type RepoFile struct {
    45  	Repositories map[string]string
    46  }
    47  
    48  // LoadRepositoriesFile takes a file at the given path and returns a RepoFile object
    49  func LoadRepositoriesFile(path string) (*RepoFile, error) {
    50  	b, err := ioutil.ReadFile(path)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	var r RepoFile
    56  	err = yaml.Unmarshal(b, &r)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	return &r, nil
    62  }
    63  
    64  // UnmarshalYAML unmarshals the repo file
    65  func (rf *RepoFile) UnmarshalYAML(unmarshal func(interface{}) error) error {
    66  	var repos map[string]string
    67  	if err := unmarshal(&repos); err != nil {
    68  		if _, ok := err.(*yaml.TypeError); !ok {
    69  			return err
    70  		}
    71  	}
    72  	rf.Repositories = repos
    73  	return nil
    74  }
    75  
    76  // LoadChartRepository takes in a path to a local chart repository
    77  //      which contains packaged charts and an index.yaml file
    78  //
    79  // This function evaluates the contents of the directory and
    80  // returns a ChartRepository
    81  func LoadChartRepository(dir, url string) (*ChartRepository, error) {
    82  	dirInfo, err := os.Stat(dir)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	if !dirInfo.IsDir() {
    88  		return nil, errors.New(dir + "is not a directory")
    89  	}
    90  
    91  	r := &ChartRepository{RootPath: dir, URL: url}
    92  
    93  	filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
    94  		if !f.IsDir() {
    95  			if strings.Contains(f.Name(), "index.yaml") {
    96  				i, err := LoadIndexFile(path)
    97  				if err != nil {
    98  					return nil
    99  				}
   100  				r.IndexFile = i
   101  			} else if strings.HasSuffix(f.Name(), ".tgz") {
   102  				r.ChartPaths = append(r.ChartPaths, path)
   103  			}
   104  		}
   105  		return nil
   106  	})
   107  	return r, nil
   108  }
   109  
   110  func (r *ChartRepository) saveIndexFile() error {
   111  	index, err := yaml.Marshal(&r.IndexFile.Entries)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	return ioutil.WriteFile(filepath.Join(r.RootPath, indexPath), index, 0644)
   117  }
   118  
   119  // Index generates an index for the chart repository and writes an index.yaml file
   120  func (r *ChartRepository) Index() error {
   121  	if r.IndexFile == nil {
   122  		r.IndexFile = &IndexFile{Entries: make(map[string]*ChartRef)}
   123  	}
   124  
   125  	existCharts := map[string]bool{}
   126  
   127  	for _, path := range r.ChartPaths {
   128  		ch, err := chartutil.Load(path)
   129  		if err != nil {
   130  			return err
   131  		}
   132  
   133  		chartfile := ch.Metadata
   134  		hash, err := generateChecksum(path)
   135  		if err != nil {
   136  			return err
   137  		}
   138  
   139  		key := chartfile.Name + "-" + chartfile.Version
   140  		if r.IndexFile.Entries == nil {
   141  			r.IndexFile.Entries = make(map[string]*ChartRef)
   142  		}
   143  
   144  		ref, ok := r.IndexFile.Entries[key]
   145  		var created string
   146  		if ok && ref.Created != "" {
   147  			created = ref.Created
   148  		} else {
   149  			created = time.Now().UTC().String()
   150  		}
   151  
   152  		url, _ := url.Parse(r.URL)
   153  		url.Path = filepath.Join(url.Path, key+".tgz")
   154  
   155  		entry := &ChartRef{Chartfile: chartfile, Name: chartfile.Name, URL: url.String(), Created: created, Checksum: hash, Removed: false}
   156  
   157  		r.IndexFile.Entries[key] = entry
   158  
   159  		// chart is existing
   160  		existCharts[key] = true
   161  	}
   162  
   163  	// update deleted charts with Removed = true
   164  	for k := range r.IndexFile.Entries {
   165  		if _, ok := existCharts[k]; !ok {
   166  			r.IndexFile.Entries[k].Removed = true
   167  		}
   168  	}
   169  
   170  	return r.saveIndexFile()
   171  }
   172  
   173  func generateChecksum(path string) (string, error) {
   174  	f, err := os.Open(path)
   175  	if err != nil {
   176  		return "", err
   177  	}
   178  
   179  	b, err := ioutil.ReadAll(f)
   180  	if err != nil {
   181  		return "", err
   182  	}
   183  
   184  	result := sha1.Sum(b)
   185  
   186  	return fmt.Sprintf("%x", result), nil
   187  }