Skip to content
This repository has been archived by the owner on Jul 16, 2021. It is now read-only.

Commit

Permalink
Populate and serve values.schema.json if exists (#645)
Browse files Browse the repository at this point in the history
Signed-off-by: Andres Martinez Gotor <andres@bitnami.com>
  • Loading branch information
Andres Martinez Gotor authored and Adnan Abdulhussein committed Oct 3, 2019
1 parent 4d0201a commit 4043fe3
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 5 deletions.
1 change: 1 addition & 0 deletions cmd/chart-repo/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type chartFiles struct {
ID string `bson:"_id"`
Readme string
Values string
Schema string
Repo repo
Digest string
}
Expand Down
8 changes: 7 additions & 1 deletion cmd/chart-repo/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,8 @@ func fetchAndImportFiles(dbSession datastore.Session, name string, r repo, cv ch

readmeFileName := name + "/README.md"
valuesFileName := name + "/values.yaml"
filenames := []string{valuesFileName, readmeFileName}
schemaFileName := name + "/values.schema.json"
filenames := []string{valuesFileName, readmeFileName, schemaFileName}

files, err := extractFilesFromTarball(filenames, tarf)
if err != nil {
Expand All @@ -448,6 +449,11 @@ func fetchAndImportFiles(dbSession datastore.Session, name string, r repo, cv ch
} else {
log.WithFields(log.Fields{"name": name, "version": cv.Version}).Info("values.yaml not found")
}
if v, ok := files[schemaFileName]; ok {
chartFiles.Schema = v
} else {
log.WithFields(log.Fields{"name": name, "version": cv.Version}).Info("values.schema.json not found")
}

// inserts the chart files if not already indexed, or updates the existing
// entry if digest has changed
Expand Down
14 changes: 10 additions & 4 deletions cmd/chart-repo/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,12 @@ type goodTarballClient struct {
c chart
skipReadme bool
skipValues bool
skipSchema bool
}

var testChartReadme = "# readme for chart\n\nBest chart in town"
var testChartValues = "image: test"
var testChartSchema = `{"properties": {}}`

func (h *goodTarballClient) Do(req *http.Request) (*http.Response, error) {
w := httptest.NewRecorder()
Expand All @@ -139,6 +141,9 @@ func (h *goodTarballClient) Do(req *http.Request) (*http.Response, error) {
if !h.skipReadme {
files = append(files, tarballFile{h.c.Name + "/README.md", testChartReadme})
}
if !h.skipSchema {
files = append(files, tarballFile{h.c.Name + "/values.schema.json", testChartSchema})
}
createTestTarball(gzw, files)
gzw.Flush()
return w.Result(), nil
Expand All @@ -159,6 +164,7 @@ func (h *authenticatedTarballClient) Do(req *http.Request) (*http.Response, erro
files := []tarballFile{{h.c.Name + "/Chart.yaml", "should be a Chart.yaml here..."}}
files = append(files, tarballFile{h.c.Name + "/values.yaml", testChartValues})
files = append(files, tarballFile{h.c.Name + "/README.md", testChartReadme})
files = append(files, tarballFile{h.c.Name + "/values.schema.json", testChartSchema})
createTestTarball(gzw, files)
gzw.Flush()
}
Expand Down Expand Up @@ -405,11 +411,11 @@ func Test_fetchAndImportFiles(t *testing.T) {
})

t.Run("file not found", func(t *testing.T) {
netClient = &goodTarballClient{c: charts[0], skipValues: true, skipReadme: true}
netClient = &goodTarballClient{c: charts[0], skipValues: true, skipReadme: true, skipSchema: true}
m := mock.Mock{}
m.On("One", mock.Anything).Return(errors.New("return an error when checking if files already exists to force fetching"))
chartFilesID := fmt.Sprintf("%s/%s-%s", charts[0].Repo.Name, charts[0].Name, cv.Version)
m.On("UpsertId", chartFilesID, chartFiles{chartFilesID, "", "", charts[0].Repo, cv.Digest})
m.On("UpsertId", chartFilesID, chartFiles{chartFilesID, "", "", "", charts[0].Repo, cv.Digest})
dbSession := mockstore.NewMockSession(&m)
err := fetchAndImportFiles(dbSession, charts[0].Name, charts[0].Repo, cv)
assert.NoErr(t, err)
Expand All @@ -421,7 +427,7 @@ func Test_fetchAndImportFiles(t *testing.T) {
m := mock.Mock{}
m.On("One", mock.Anything).Return(errors.New("return an error when checking if files already exists to force fetching"))
chartFilesID := fmt.Sprintf("%s/%s-%s", charts[0].Repo.Name, charts[0].Name, cv.Version)
m.On("UpsertId", chartFilesID, chartFiles{chartFilesID, testChartReadme, testChartValues, charts[0].Repo, cv.Digest})
m.On("UpsertId", chartFilesID, chartFiles{chartFilesID, testChartReadme, testChartValues, testChartSchema, charts[0].Repo, cv.Digest})
dbSession := mockstore.NewMockSession(&m)
err := fetchAndImportFiles(dbSession, charts[0].Name, charts[0].Repo, cv)
assert.NoErr(t, err)
Expand All @@ -433,7 +439,7 @@ func Test_fetchAndImportFiles(t *testing.T) {
m := mock.Mock{}
m.On("One", mock.Anything).Return(errors.New("return an error when checking if files already exists to force fetching"))
chartFilesID := fmt.Sprintf("%s/%s-%s", charts[0].Repo.Name, charts[0].Name, cv.Version)
m.On("UpsertId", chartFilesID, chartFiles{chartFilesID, testChartReadme, testChartValues, charts[0].Repo, cv.Digest})
m.On("UpsertId", chartFilesID, chartFiles{chartFilesID, testChartReadme, testChartValues, testChartSchema, charts[0].Repo, cv.Digest})
dbSession := mockstore.NewMockSession(&m)
err := fetchAndImportFiles(dbSession, charts[0].Name, charts[0].Repo, cv)
assert.NoErr(t, err)
Expand Down
15 changes: 15 additions & 0 deletions cmd/chartsvc/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,21 @@ func getChartVersionValues(w http.ResponseWriter, req *http.Request, params Para
w.Write([]byte(files.Values))
}

// getChartVersionSchema returns the values.schema.json for a given chart
func getChartVersionSchema(w http.ResponseWriter, req *http.Request, params Params) {
db, closer := dbSession.DB()
defer closer()
var files models.ChartFiles
fileID := fmt.Sprintf("%s/%s-%s", params["repo"], params["chartName"], params["version"])
if err := db.C(filesCollection).FindId(fileID).One(&files); err != nil {
log.WithError(err).Errorf("could not find values.schema.json with id %s", fileID)
http.NotFound(w, req)
return
}

w.Write([]byte(files.Schema))
}

// listChartsWithFilters returns the list of repos that contains the given chart and the latest version found
func listChartsWithFilters(w http.ResponseWriter, req *http.Request, params Params) {
db, closer := dbSession.DB()
Expand Down
65 changes: 65 additions & 0 deletions cmd/chartsvc/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ var cc count

const testChartReadme = "# Quickstart\n\n```bash\nhelm install my-repo/my-chart\n```"
const testChartValues = "image:\n registry: docker.io\n repository: my-repo/my-chart\n tag: 0.1.0"
const testChartSchema = `{"properties": {"type": "object"}}`

func iconBytes() []byte {
var b bytes.Buffer
Expand Down Expand Up @@ -730,6 +731,70 @@ func Test_getChartVersionValues(t *testing.T) {
}
}

func Test_getChartVersionSchema(t *testing.T) {
tests := []struct {
name string
version string
err error
files models.ChartFiles
wantCode int
}{
{
"chart does not exist",
"0.1.0",
errors.New("return an error when checking if chart exists"),
models.ChartFiles{ID: "my-repo/my-chart"},
http.StatusNotFound,
},
{
"chart exists",
"3.2.1",
nil,
models.ChartFiles{ID: "my-repo/my-chart", Schema: testChartSchema},
http.StatusOK,
},
{
"chart does not have values.yaml",
"2.2.2",
nil,
models.ChartFiles{ID: "my-repo/my-chart"},
http.StatusOK,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var m mock.Mock
dbSession = mockstore.NewMockSession(&m)

if tt.err != nil {
m.On("One", mock.Anything).Return(tt.err)
} else {
m.On("One", &models.ChartFiles{}).Return(nil).Run(func(args mock.Arguments) {
*args.Get(0).(*models.ChartFiles) = tt.files
})
}

w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/assets/"+tt.files.ID+"/versions/"+tt.version+"/values.schema.json", nil)
parts := strings.Split(tt.files.ID, "/")
params := Params{
"repo": parts[0],
"chartName": parts[1],
"version": "0.1.0",
}

getChartVersionSchema(w, req, params)

m.AssertExpectations(t)
assert.Equal(t, tt.wantCode, w.Code, "http status code should match")
if tt.wantCode == http.StatusOK {
assert.Equal(t, string(w.Body.Bytes()), tt.files.Schema, "content of values.schema.json should match")
}
})
}
}

func Test_findLatestChart(t *testing.T) {
t.Run("returns mocked chart", func(t *testing.T) {
chart := &models.Chart{
Expand Down
1 change: 1 addition & 0 deletions cmd/chartsvc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func setupRoutes() http.Handler {
apiv1.Methods("GET").Path("/assets/{repo}/{chartName}/logo-160x160-fit.png").Handler(WithParams(getChartIcon))
apiv1.Methods("GET").Path("/assets/{repo}/{chartName}/versions/{version}/README.md").Handler(WithParams(getChartVersionReadme))
apiv1.Methods("GET").Path("/assets/{repo}/{chartName}/versions/{version}/values.yaml").Handler(WithParams(getChartVersionValues))
apiv1.Methods("GET").Path("/assets/{repo}/{chartName}/versions/{version}/values.schema.json").Handler(WithParams(getChartVersionSchema))

n := negroni.Classic()
n.UseHandler(r)
Expand Down
57 changes: 57 additions & 0 deletions cmd/chartsvc/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,3 +465,60 @@ func Test_GetChartValues(t *testing.T) {
})
}
}

// tests the GET /{apiVersion}/assets/{repo}/{chartName}/versions/{version}/values/schema.json endpoint
func Test_GetChartSchema(t *testing.T) {
ts := httptest.NewServer(setupRoutes())
defer ts.Close()

tests := []struct {
name string
version string
err error
files models.ChartFiles
wantCode int
}{
{
"chart does not exist",
"0.1.0",
errors.New("return an error when checking if chart exists"),
models.ChartFiles{ID: "my-repo/my-chart"},
http.StatusNotFound,
},
{
"chart exists",
"3.2.1",
nil,
models.ChartFiles{ID: "my-repo/my-chart", Schema: testChartSchema},
http.StatusOK,
},
{
"chart does not have values.schema.json",
"2.2.2",
nil,
models.ChartFiles{ID: "my-repo/my-chart"},
http.StatusOK,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var m mock.Mock
dbSession = mockstore.NewMockSession(&m)
if tt.err != nil {
m.On("One", mock.Anything).Return(tt.err)
} else {
m.On("One", &models.ChartFiles{}).Return(nil).Run(func(args mock.Arguments) {
*args.Get(0).(*models.ChartFiles) = tt.files
})
}

res, err := http.Get(ts.URL + pathPrefix + "/assets/" + tt.files.ID + "/versions/" + tt.version + "/values.schema.json")
assert.NoError(t, err)
defer res.Body.Close()

m.AssertExpectations(t)
assert.Equal(t, res.StatusCode, tt.wantCode, "http status code should match")
})
}
}
2 changes: 2 additions & 0 deletions cmd/chartsvc/models/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ type ChartVersion struct {
URLs []string `json:"urls"`
Readme string `json:"readme" bson:"-"`
Values string `json:"values" bson:"-"`
Schema string `json:"schema" bson:"-"`
}

// ChartFiles holds the README and values for a given chart version
type ChartFiles struct {
ID string `bson:"_id"`
Readme string
Values string
Schema string
}

0 comments on commit 4043fe3

Please sign in to comment.