@ -76,12 +76,12 @@ type ChartDownloader struct {
// Returns a string path to the location where the file was downloaded and a verification
// Returns a string path to the location where the file was downloaded and a verification
// (if provenance was verified), or an error if something bad happened.
// (if provenance was verified), or an error if something bad happened.
func ( c * ChartDownloader ) DownloadTo ( ref , version , dest string ) ( string , * provenance . Verification , error ) {
func ( c * ChartDownloader ) DownloadTo ( ref , version , dest string ) ( string , * provenance . Verification , error ) {
// resolve URL
u , client , err := c . ResolveChartVersion ( ref , version )
u , err := c . ResolveChartVersion ( ref , version )
if err != nil {
if err != nil {
return "" , nil , err
return "" , nil , err
}
}
data , err := download ( u . String ( ) )
data , err := download ( u . String ( ) , client )
if err != nil {
if err != nil {
return "" , nil , err
return "" , nil , err
}
}
@ -95,8 +95,7 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
// If provenance is requested, verify it.
// If provenance is requested, verify it.
ver := & provenance . Verification { }
ver := & provenance . Verification { }
if c . Verify > VerifyNever {
if c . Verify > VerifyNever {
body , err := download ( u . String ( ) + ".prov" , client )
body , err := download ( u . String ( ) + ".prov" )
if err != nil {
if err != nil {
if c . Verify == VerifyAlways {
if c . Verify == VerifyAlways {
return destfile , ver , fmt . Errorf ( "Failed to fetch provenance %q" , u . String ( ) + ".prov" )
return destfile , ver , fmt . Errorf ( "Failed to fetch provenance %q" , u . String ( ) + ".prov" )
@ -132,63 +131,75 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
// * If version is non-empty, this will return the URL for that version
// * If version is non-empty, this will return the URL for that version
// * If version is empty, this will return the URL for the latest version
// * If version is empty, this will return the URL for the latest version
// * If no version can be found, an error is returned
// * If no version can be found, an error is returned
func ( c * ChartDownloader ) ResolveChartVersion ( ref , version string ) ( * url . URL , error ) {
func ( c * ChartDownloader ) ResolveChartVersion ( ref , version string ) ( * url . URL , * http . Client , error ) {
// See if it's already a full URL.
u , err := url . Parse ( ref )
// FIXME: Why do we use url.ParseRequestURI instead of url.Parse?
if err != nil {
u , err := url . ParseRequestURI ( ref )
return nil , nil , fmt . Errorf ( "invalid chart URL format: %s" , ref )
if err == nil {
// If it has a scheme and host and path, it's a full URL
if u . IsAbs ( ) && len ( u . Host ) > 0 && len ( u . Path ) > 0 {
return u , nil
}
return u , fmt . Errorf ( "invalid chart url format: %s" , ref )
}
}
r , err := repo . LoadRepositor ies File( c . HelmHome . RepositoryFile ( ) )
rf , err := repo . LoadRepositoryFile ( c . HelmHome . RepositoryFile ( ) )
if err != nil {
if err != nil {
return u , err
return nil , nil , err
}
}
var (
chartName string
rc * repo . ChartRepositoryConfig
)
if u . IsAbs ( ) && len ( u . Host ) > 0 && len ( u . Path ) > 0 {
// If it has a scheme and host and path, it's a full URL
p := strings . SplitN ( strings . TrimLeft ( u . Path , "/" ) , "-" , 2 )
if len ( p ) < 2 {
return nil , nil , fmt . Errorf ( "Seems that chart path is not in form of repo_url/path_to_chart, got: %s" , u )
}
chartName = p [ 0 ]
u . Path = ""
rc , err = pickChartRepositoryConfigByURL ( u . String ( ) , rf . Repositories )
if err != nil {
return nil , nil , err
}
} else {
// See if it's of the form: repo/path_to_chart
// See if it's of the form: repo/path_to_chart
p := strings . SplitN ( ref , "/" , 2 )
p := strings . SplitN ( u. Path , "/" , 2 )
if len ( p ) < 2 {
if len ( p ) < 2 {
return u , fmt . Errorf ( "invalid chart url format: %s" , ref )
return nil , nil , fmt . Errorf ( "Non-absolute URLs should be in form of repo_name/path_to_chart, got: %s" , u )
}
}
repoName := p [ 0 ]
repoName := p [ 0 ]
chartName := p [ 1 ]
chartName = p [ 1 ]
rf , err := findRepoEntry ( repoName , r . Repositories )
rc , err = pickChartRepositoryConfigByName ( repoName , rf . Repositories )
if err != nil {
if err != nil {
return u , err
return nil , nil , err
}
}
}
if rf . URL == "" {
return u , fmt . Errorf ( "no URL found for repository %q" , repoName )
r , err := repo . NewChartRepository ( rc )
if err != nil {
return nil , nil , err
}
}
// Next, we need to load the index, and actually look up the chart.
// Next, we need to load the index, and actually look up the chart.
i , err := repo . NewChartRepositoryIndexFromFile ( c . HelmHome . CacheIndex ( r epo Name) )
i , err := repo . NewChartRepositoryIndexFromFile ( c . HelmHome . CacheIndex ( r . Config . Name) )
if err != nil {
if err != nil {
return u , fmt . Errorf ( "no cached repo found. (try 'helm repo update'). %s" , err )
return nil , nil , fmt . Errorf ( "no cached repo found. (try 'helm repo update'). %s" , err )
}
}
cv , err := i . Get ( chartName , version )
cv , err := i . Get ( chartName , version )
if err != nil {
if err != nil {
return u , fmt . Errorf ( "chart %q not found in %s index. (try 'helm repo update'). %s" , chartName , r epo Name, err )
return nil , nil , fmt . Errorf ( "chart %q not found in %s index. (try 'helm repo update'). %s" , chartName , r . Config . Name, err )
}
}
if len ( cv . URLs ) == 0 {
if len ( cv . URLs ) == 0 {
return u , fmt . Errorf ( "chart %q has no downloadable URLs" , ref )
return nil , nil , fmt . Errorf ( "chart %q has no downloadable URLs" , ref )
}
}
return url . Parse ( cv . URLs [ 0 ] )
}
func findRepoEntry ( name string , repos [ ] * repo . ChartRepositoryConfig ) ( * repo . ChartRepositoryConfig , error ) {
// TODO: Seems that picking first URL is not fully correct
for _ , re := range repos {
u , err = url . Parse ( cv . URLs [ 0 ] )
if re . Name == name {
if err != nil {
return re , nil
return nil , nil , fmt . Errorf ( "invalid chart URL format: %s" , ref )
}
}
}
return nil , fmt . Errorf ( "no repo named %q" , name )
return u , r . Client , nil
}
}
// VerifyChart takes a path to a chart archive and a keyring, and verifies the chart.
// VerifyChart takes a path to a chart archive and a keyring, and verifies the chart.
@ -217,11 +228,11 @@ func VerifyChart(path string, keyring string) (*provenance.Verification, error)
return sig . Verify ( path , provfile )
return sig . Verify ( path , provfile )
}
}
// download performs a simple HTTP Get and returns the body.
// download performs a HTTP Get using specified clien t and returns the body.
func download ( href string ) ( * bytes . Buffer , error ) {
func download ( href string , client * http . Client ) ( * bytes . Buffer , error ) {
buf := bytes . NewBuffer ( nil )
buf := bytes . NewBuffer ( nil )
resp , err := http . Get ( href )
resp , err := client . Get ( href )
if err != nil {
if err != nil {
return buf , err
return buf , err
}
}
@ -241,3 +252,24 @@ func download(href string) (*bytes.Buffer, error) {
func isTar ( filename string ) bool {
func isTar ( filename string ) bool {
return strings . ToLower ( filepath . Ext ( filename ) ) == ".tgz"
return strings . ToLower ( filepath . Ext ( filename ) ) == ".tgz"
}
}
func pickChartRepositoryConfigByName ( name string , cfgs [ ] * repo . ChartRepositoryConfig ) ( * repo . ChartRepositoryConfig , error ) {
for _ , rc := range cfgs {
if rc . Name == name {
if rc . URL == "" {
return nil , fmt . Errorf ( "no URL found for repository %s" , name )
}
return rc , nil
}
}
return nil , fmt . Errorf ( "repo %s not found" , name )
}
func pickChartRepositoryConfigByURL ( u string , cfgs [ ] * repo . ChartRepositoryConfig ) ( * repo . ChartRepositoryConfig , error ) {
for _ , rc := range cfgs {
if rc . URL == u {
return rc , nil
}
}
return nil , fmt . Errorf ( "repo with URL %s not found" , u )
}