Add metahelper for reading/writing metadata in Prow (#773)

* Add metahelper for writing metadata in Prow

* Update prow package importing path
This commit is contained in:
chaodaiG 2019-10-22 10:29:27 -07:00 committed by Knative Prow Robot
parent ea367c1342
commit f0ffda4667
3 changed files with 358 additions and 0 deletions

View File

@ -0,0 +1,109 @@
/*
Copyright 2019 The Knative Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License 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 client supports various needs for running tests
package client
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"path"
"knative.dev/pkg/test/prow"
)
const (
filename = "metadata.json"
)
// client holds metadata as a string:string map, as well as path for storing
// metadata
type client struct {
MetaData map[string]string
Path string
}
// NewClient creates a client, takes custom directory for storing `metadata.json`.
// It reads existing `metadata.json` file if it exists, otherwise creates it.
// Errors out if there is any file i/o problem other than file not exist error.
func NewClient(dir string) (*client, error) {
c := &client{
MetaData: make(map[string]string),
}
if dir == "" {
log.Println("Getting artifacts dir from prow")
dir = prow.GetLocalArtifactsDir()
}
c.Path = path.Join(dir, filename)
if _, err := os.Stat(dir); os.IsNotExist(err) {
if err = os.MkdirAll(dir, 0777); err != nil {
return nil, fmt.Errorf("Failed to create directory: %v", err)
}
}
return c, nil
}
// sync is shared by Get and Set, invoked at the very beginning of each, makes
// sure the file exists, and loads the content of file into c.MetaData
func (c *client) sync() error {
_, err := os.Stat(c.Path)
if os.IsNotExist(err) {
body, _ := json.Marshal(&c.MetaData)
err = ioutil.WriteFile(c.Path, body, 0777)
} else {
var body []byte
body, err = ioutil.ReadFile(c.Path)
if err == nil {
err = json.Unmarshal(body, &c.MetaData)
}
}
return err
}
// Set sets key:val pair, and overrides if it exists
func (c *client) Set(key, val string) error {
err := c.sync()
if err != nil {
return err
}
if oldVal, ok := c.MetaData[key]; ok {
log.Printf("Overriding meta %q:%q with new value %q", key, oldVal, val)
}
c.MetaData[key] = val
body, _ := json.Marshal(c.MetaData)
return ioutil.WriteFile(c.Path, body, 0777)
}
// Get gets val for key
func (c *client) Get(key string) (string, error) {
if _, err := os.Stat(c.Path); err != nil && os.IsNotExist(err) {
return "", fmt.Errorf("file %q doesn't exist", c.Path)
}
var res string
err := c.sync()
if err == nil {
if val, ok := c.MetaData[key]; ok {
res = val
} else {
err = fmt.Errorf("key %q doesn't exist", key)
}
}
return res, err
}

View File

@ -0,0 +1,183 @@
/*
Copyright 2019 The Knative Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License 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 client supports various needs for running tests
package client
import (
"fmt"
"io/ioutil"
"log"
"os"
"reflect"
"testing"
)
const (
fakeArtifactDir = "fakeArtifactDir"
mockArtifactEnv = "mockArtifactDir"
)
func TestNewClient(t *testing.T) {
datas := []struct {
customDir string
expPath string
expErr bool
}{
{ // default dir
"", "mockArtifactDir/metadata.json", false,
}, { // custom dir
"a", "a/metadata.json", false,
},
}
for _, data := range datas {
dir := data.customDir
if data.customDir == "" { // use env var
oriArtifactDir := os.Getenv("ARTIFACTS")
defer os.Setenv("ARTIFACTS", oriArtifactDir)
os.Setenv("ARTIFACTS", mockArtifactEnv)
dir = mockArtifactEnv
}
os.RemoveAll(dir)
defer os.RemoveAll(dir)
c, err := NewClient(data.customDir)
errMsg := fmt.Sprintf("Testing new client with dir: %q", data.customDir)
if (err == nil && data.expErr) || (err != nil && !data.expErr) {
log.Fatalf("%s\ngot: '%v', want: '%v'", errMsg, err, data.expErr)
}
if c.Path != data.expPath {
log.Fatalf("%s\ngot: %q, want: %q", errMsg, c.Path, data.expPath)
}
if _, err := os.Stat(dir); os.IsNotExist(err) {
log.Fatalf("%s\nDirectory %q wasn't created", errMsg, dir)
}
}
}
func TestSync(t *testing.T) {
datas := []struct {
fileExist bool
content string
expMetadata map[string]string
expErr bool
}{
{ // file not exist
false, "", make(map[string]string), false,
}, { // file exist but empty
true, "", make(map[string]string), true,
}, { // file exist, invalid
true, "{", make(map[string]string), true,
}, { // file exist valid
true, "{}", make(map[string]string), false,
},
}
for _, data := range datas {
c, _ := NewClient(fakeArtifactDir)
os.Remove(c.Path)
if data.fileExist {
defer os.Remove(c.Path)
ioutil.WriteFile(c.Path, []byte(data.content), 0644)
}
err := c.sync()
errMsg := fmt.Sprintf("Testing syncing with file exist: %v, content: %q", data.fileExist, data.content)
if (err == nil && data.expErr) || (err != nil && !data.expErr) {
log.Fatalf("%s\ngot: '%v', want: '%v'", errMsg, err, data.expErr)
}
if !reflect.DeepEqual(c.MetaData, data.expMetadata) {
log.Fatalf("%s\ngot: '%v', want: '%v'", errMsg, c.MetaData, data.expMetadata)
}
}
}
func TestSet(t *testing.T) {
datas := []struct {
metadata map[string]string
content string
setKey string
setVal string
expMetadata map[string]string
expErr bool
}{
{ // sync failed
make(map[string]string), "", "", "", make(map[string]string), true,
}, { // set normal key
make(map[string]string), "{}", "a", "b", map[string]string{"a": "b"}, false,
}, { // override
make(map[string]string), "{\"a\":\"b\"}", "a", "c", map[string]string{"a": "c"}, false,
}, { // ignore old client val
map[string]string{"a": "b"}, "{}", "c", "d", map[string]string{"c": "d"}, false,
},
}
for _, data := range datas {
c, _ := NewClient(fakeArtifactDir)
defer os.Remove(c.Path)
ioutil.WriteFile(c.Path, []byte(data.content), 0644)
err := c.Set(data.setKey, data.setVal)
errMsg := fmt.Sprintf("Testing set %q:%q, with metadata: %v, content: %q",
data.setKey, data.setVal, data.metadata, data.content)
if (err == nil && data.expErr) || (err != nil && !data.expErr) {
log.Fatalf("%s\ngot: '%v', want: '%v'", errMsg, err, data.expErr)
}
if !reflect.DeepEqual(c.MetaData, data.expMetadata) {
log.Fatalf("%s\ngot: '%v', want: '%v'", errMsg, c.MetaData, data.expMetadata)
}
}
}
func TestGet(t *testing.T) {
datas := []struct {
metadata map[string]string
content string
getKey string
expMetadata map[string]string
expVal string
expErr bool
}{
{ // sync failed
make(map[string]string), "", "", make(map[string]string), "", true,
}, { // get normal key
make(map[string]string), "{\"a\":\"b\"}", "a", map[string]string{"a": "b"}, "b", false,
}, { // key not exist
make(map[string]string), "{\"a\":\"b\"}", "c", map[string]string{"a": "b"}, "", true,
}, { // ignore old client val
map[string]string{"a": "c"}, "{\"a\":\"b\"}", "a", map[string]string{"a": "b"}, "b", false,
},
}
for _, data := range datas {
c, _ := NewClient(fakeArtifactDir)
defer os.Remove(c.Path)
ioutil.WriteFile(c.Path, []byte(data.content), 0644)
val, err := c.Get(data.getKey)
errMsg := fmt.Sprintf("Testing get %q, with metadata: %v, content: %q",
data.getKey, data.metadata, data.content)
if (err == nil && data.expErr) || (err != nil && !data.expErr) {
log.Fatalf("%s\ngot: '%v', want: '%v'", errMsg, err, data.expErr)
}
if !reflect.DeepEqual(c.MetaData, data.expMetadata) {
log.Fatalf("%s\ngot: '%v', want: '%v'", errMsg, c.MetaData, data.expMetadata)
}
if val != data.expVal {
log.Fatalf("%s\ngot: %q, want: %q", errMsg, val, data.expVal)
}
}
}

View File

@ -0,0 +1,66 @@
/*
Copyright 2019 The Knative Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License 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 main
import (
"flag"
"fmt"
"log"
"knative.dev/pkg/testutils/metahelper/client"
)
var (
getKeyOpt string
saveKeyOpt string
valOpt string
)
func main() {
flag.StringVar(&getKeyOpt, "get", "", "get val for a key")
flag.StringVar(&saveKeyOpt, "set", "", "save val for a key, must have --val supplied")
flag.StringVar(&valOpt, "val", "", "val to be modified, only useful when --save is passed")
flag.Parse()
// Create with default path of metahelper/client, so that the path is
// consistent with all other consumers of metahelper/client that run within
// the same context of this tool
c, err := client.NewClient("")
if err != nil {
log.Fatal(err)
}
var res string
switch {
case getKeyOpt != "" && saveKeyOpt != "":
log.Fatal("--get and --save can't be used at the same time")
case getKeyOpt != "":
gotVal, err := c.Get(getKeyOpt)
if err != nil {
log.Fatalf("Failed getting value for %q from %q: '%v'", getKeyOpt, c.Path, err)
}
res = gotVal
case saveKeyOpt != "":
if valOpt == "" {
log.Fatal("--val must be supplied when using --save")
}
log.Printf("Writing files to %s", c.Path)
if err := c.Set(saveKeyOpt, valOpt); err != nil {
log.Fatalf("Failed saving %q:%q to %q: '%v'", saveKeyOpt, valOpt, c.Path, err)
}
}
fmt.Print(res)
}