Skip to content

Commit

Permalink
Fix index file creation (#37)
Browse files Browse the repository at this point in the history
Signed-off-by: Reinhard Naegele <unguiculus@gmail.com>
  • Loading branch information
unguiculus committed Jul 23, 2019
1 parent 5132b26 commit d1fca78
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 40 deletions.
7 changes: 4 additions & 3 deletions cr/cmd/index.go
Expand Up @@ -34,22 +34,23 @@ given GitHub repository's releases.
if err != nil {
return err
}
ghc := github.NewClient(config.Owner, config.Repo, config.Token)
ghc := github.NewClient(config.Owner, config.GitRepo, config.Token)
releaser := releaser.NewReleaser(config, ghc)
_, err = releaser.UpdateIndexFile()
return err
},
}

func getRequiredIndexArgs() []string {
return []string{"owner", "repo"}
return []string{"owner", "git-repo", "charts-repo"}
}

func init() {
rootCmd.AddCommand(indexCmd)
flags := indexCmd.Flags()
flags.StringP("owner", "o", "", "GitHub username or organization")
flags.StringP("repo", "r", "", "GitHub repository")
flags.StringP("git-repo", "r", "", "GitHub repository")
flags.StringP("charts-repo", "c", "", "The URL to the charts repository")
flags.StringP("index-path", "i", ".cr-index/index.yaml", "Path to index file")
flags.StringP("package-path", "p", ".cr-release-packages", "Path to directory with chart packages")
flags.StringP("token", "t", "", "GitHub Auth Token (only needed for private repos)")
Expand Down
6 changes: 3 additions & 3 deletions cr/cmd/upload.go
Expand Up @@ -31,20 +31,20 @@ var uploadCmd = &cobra.Command{
if err != nil {
return err
}
ghc := github.NewClient(config.Owner, config.Repo, config.Token)
ghc := github.NewClient(config.Owner, config.GitRepo, config.Token)
releaser := releaser.NewReleaser(config, ghc)
return releaser.CreateReleases()
},
}

func getRequiredUploadArgs() []string {
return []string{"owner", "repo", "token"}
return []string{"owner", "git-repo", "token"}
}

func init() {
rootCmd.AddCommand(uploadCmd)
uploadCmd.Flags().StringP("owner", "o", "", "GitHub username or organization")
uploadCmd.Flags().StringP("repo", "r", "", "GitHub repository")
uploadCmd.Flags().StringP("git-repo", "r", "", "GitHub repository")
uploadCmd.Flags().StringP("package-path", "p", ".cr-release-packages", "Path to directory with chart packages")
uploadCmd.Flags().StringP("token", "t", "", "GitHub Auth Token")
}
27 changes: 23 additions & 4 deletions pkg/config/config.go
Expand Up @@ -17,7 +17,7 @@ package config
import (
"fmt"
"github.com/mitchellh/go-homedir"
"path"
"path/filepath"
"reflect"
"strings"

Expand All @@ -31,14 +31,15 @@ var (
homeDir, _ = homedir.Dir()
configSearchLocations = []string{
".",
path.Join(homeDir, ".cr"),
filepath.Join(homeDir, ".cr"),
"/etc/cr",
}
)

type Options struct {
Owner string `mapstructure:"owner"`
Repo string `mapstructure:"repo"`
GitRepo string `mapstructure:"git-repo"`
ChartsRepo string `mapstructure:"charts-repo"`
IndexPath string `mapstructure:"index-path"`
PackagePath string `mapstructure:"package-path"`
Token string `mapstructure:"token"`
Expand Down Expand Up @@ -86,7 +87,8 @@ func LoadConfiguration(cfgFile string, cmd *cobra.Command, requiredFlags []strin

elem := reflect.ValueOf(opts).Elem()
for _, requiredFlag := range requiredFlags {
f := elem.FieldByName(strings.Title(requiredFlag))
fieldName := kebabCaseToTitleCamelCase(requiredFlag)
f := elem.FieldByName(fieldName)
value := fmt.Sprintf("%v", f.Interface())
if value == "" {
return nil, errors.Errorf("'--%s' is required", requiredFlag)
Expand All @@ -95,3 +97,20 @@ func LoadConfiguration(cfgFile string, cmd *cobra.Command, requiredFlags []strin

return opts, nil
}

func kebabCaseToTitleCamelCase(input string) (result string) {
nextToUpper := true
for _, runeValue := range input {
if nextToUpper {
result += strings.ToUpper(string(runeValue))
nextToUpper = false
} else {
if runeValue == '-' {
nextToUpper = true
} else {
result += string(runeValue)
}
}
}
return
}
Empty file.
58 changes: 45 additions & 13 deletions pkg/releaser/releaser.go
Expand Up @@ -17,10 +17,11 @@ package releaser
import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"strings"

Expand All @@ -40,35 +41,66 @@ type GitHub interface {
GetRelease(ctx context.Context, tag string) (*github.Release, error)
}

type HttpClient interface {
Get(url string) (*http.Response, error)
}

type DefaultHttpClient struct {
}

func (c *DefaultHttpClient) Get(url string) (resp *http.Response, err error) {
return http.Get(url)
}

type Releaser struct {
config *config.Options
github GitHub
config *config.Options
github GitHub
httpClient HttpClient
}

func NewReleaser(config *config.Options, github GitHub) *Releaser {
return &Releaser{
config: config,
github: github,
config: config,
github: github,
httpClient: &DefaultHttpClient{},
}
}

//UpdateIndexFile index.yaml file for a give git repo
// UpdateIndexFile index.yaml file for a give git repo
func (r *Releaser) UpdateIndexFile() (bool, error) {
// if path doesn't end with index.yaml we can try and fix it
if path.Base(r.config.IndexPath) != "index.yaml" {
if filepath.Base(r.config.IndexPath) != "index.yaml" {
// if path is a directory then add index.yaml
if stat, err := os.Stat(r.config.IndexPath); err == nil && stat.IsDir() {
r.config.IndexPath = path.Join(r.config.IndexPath, "index.yaml")
r.config.IndexPath = filepath.Join(r.config.IndexPath, "index.yaml")
// otherwise error out
} else {
fmt.Printf("path (%s) should be a directory or a file called index.yaml\n", r.config.IndexPath)
os.Exit(1)
}
}

var indexFile = &repo.IndexFile{}
var indexFile *repo.IndexFile

resp, err := r.httpClient.Get(fmt.Sprintf("%s/index.yaml", r.config.ChartsRepo))
if err != nil {
return false, err
}

defer resp.Body.Close()

if resp.StatusCode == http.StatusOK {
out, err := os.Create(r.config.IndexPath)
if err != nil {
return false, err
}
defer out.Close()

_, err = io.Copy(out, resp.Body)
if err != nil {
return false, err
}

if _, err := os.Stat(r.config.IndexPath); err == nil {
fmt.Printf("====> Using existing index at %s\n", r.config.IndexPath)
indexFile, err = repo.LoadIndexFile(r.config.IndexPath)
if err != nil {
Expand All @@ -95,7 +127,7 @@ func (r *Releaser) UpdateIndexFile() (bool, error) {

for _, asset := range release.Assets {
downloadUrl, _ := url.Parse(asset.URL)
name := path.Base(downloadUrl.Path)
name := filepath.Base(downloadUrl.Path)
baseName := strings.TrimSuffix(name, filepath.Ext(name))
tagParts := r.splitPackageNameAndVersion(baseName)
packageName, packageVersion := tagParts[0], tagParts[1]
Expand Down Expand Up @@ -127,7 +159,7 @@ func (r *Releaser) splitPackageNameAndVersion(pkg string) []string {
}

func (r *Releaser) addToIndexFile(indexFile *repo.IndexFile, url string) error {
arch := path.Join(r.config.PackagePath, path.Base(url))
arch := filepath.Join(r.config.PackagePath, filepath.Base(url))

// extract chart metadata
fmt.Printf("====> Extracting chart metadata from %s\n", arch)
Expand All @@ -149,7 +181,7 @@ func (r *Releaser) addToIndexFile(indexFile *repo.IndexFile, url string) error {
s = s[:len(s)-1]

// Add to index
indexFile.Add(c.Metadata, path.Base(arch), strings.Join(s, "/"), hash)
indexFile.Add(c.Metadata, filepath.Base(arch), strings.Join(s, "/"), hash)
return nil
}

Expand Down
58 changes: 41 additions & 17 deletions pkg/releaser/releaser_test.go
Expand Up @@ -15,9 +15,11 @@
package releaser

import (
"bufio"
"context"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"testing"
Expand All @@ -37,6 +39,20 @@ type FakeGitHub struct {
tag string
}

type MockClient struct {
statusCode int
}

func (m *MockClient) Get(url string) (*http.Response, error) {
if m.statusCode == http.StatusOK {
file, _ := os.Open("testdata/repo/index.yaml")
reader := bufio.NewReader(file)
return &http.Response{StatusCode: http.StatusOK, Body: ioutil.NopCloser(reader)}, nil
} else {
return &http.Response{StatusCode: http.StatusNotFound, Body: ioutil.NopCloser(nil)}, nil
}
}

func (f *FakeGitHub) CreateRelease(ctx context.Context, input *github.Release) error {
f.Called(ctx, input)
f.release = input
Expand All @@ -61,44 +77,52 @@ func TestReleaser_UpdateIndexFile(t *testing.T) {
indexDir, _ := ioutil.TempDir(".", "index")
defer os.RemoveAll(indexDir)

fakeGitHub := new(FakeGitHub)

tests := []struct {
name string
indexPath string
exists bool
name string
exists bool
releaser *Releaser
}{
{
"index-file-exists",
"testdata/index/index.yaml",
true,
&Releaser{
config: &config.Options{
IndexPath: "testdata/index/index.yaml",
PackagePath: "testdata/release-packages",
},
github: fakeGitHub,
httpClient: &MockClient{http.StatusOK},
},
},
{
"index-file-does-not-exist",
filepath.Join(indexDir, "index.yaml"),
false,
&Releaser{
config: &config.Options{
IndexPath: filepath.Join(indexDir, "index.yaml"),
PackagePath: "testdata/release-packages",
},
github: fakeGitHub,
httpClient: &MockClient{http.StatusNotFound},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeGitHub := new(FakeGitHub)
r := &Releaser{
config: &config.Options{
IndexPath: tt.indexPath,
PackagePath: "testdata/release-packages",
},
github: fakeGitHub,
}
var sha256 string
if tt.exists {
sha256, _ = provenance.DigestFile(tt.indexPath)
sha256, _ = provenance.DigestFile(tt.releaser.config.IndexPath)
}
update, err := r.UpdateIndexFile()
update, err := tt.releaser.UpdateIndexFile()
assert.NoError(t, err)
assert.Equal(t, update, !tt.exists)
if tt.exists {
newSha256, _ := provenance.DigestFile(tt.indexPath)
newSha256, _ := provenance.DigestFile(tt.releaser.config.IndexPath)
assert.Equal(t, sha256, newSha256)
} else {
_, err := os.Stat(tt.indexPath)
_, err := os.Stat(tt.releaser.config.IndexPath)
assert.NoError(t, err)
}
})
Expand Down
13 changes: 13 additions & 0 deletions pkg/releaser/testdata/repo/index.yaml
@@ -0,0 +1,13 @@
apiVersion: v1
entries:
test-chart:
- apiVersion: v1
appVersion: "1.0"
created: "2019-03-29T22:50:44.754424+01:00"
description: A Helm chart for Kubernetes
digest: b61c67a17ac0215b45db5d4a60677d06993c772b1412c2dc32885ef7f49e4264
name: test-chart
urls:
- test-chart-0.1.0.tgz
version: 0.1.0
generated: "2019-03-29T22:50:44.751503+01:00"

0 comments on commit d1fca78

Please sign in to comment.