Skip to content
This repository was archived by the owner on Jun 14, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions cmd/pj-rehearse/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
pjapi "k8s.io/test-infra/prow/apis/prowjobs/v1"
prowgithub "k8s.io/test-infra/prow/github"
prowplugins "k8s.io/test-infra/prow/plugins"
pjdwapi "k8s.io/test-infra/prow/pod-utils/downwardapi"

"k8s.io/client-go/rest"
Expand Down Expand Up @@ -109,6 +110,14 @@ func gracefulExit(suppressFailures bool, message string) int {
return 1
}

func loadPluginConfig(releaseRepoPath string) (ret prowplugins.ConfigUpdater, err error) {
agent := prowplugins.ConfigAgent{}
if err = agent.Load(filepath.Join(releaseRepoPath, config.PluginConfigInRepoPath)); err == nil {
ret = agent.Config().ConfigUpdater
}
return
}

func rehearseMain() int {
o := gatherOptions()
err := validateOptions(o)
Expand Down Expand Up @@ -162,6 +171,11 @@ func rehearseMain() int {
}

prConfig := config.GetAllConfigs(o.releaseRepoPath, logger)
pluginConfig, err := loadPluginConfig(o.releaseRepoPath)
if err != nil {
logger.WithError(err).Error("could not load plugin configuration from tested revision of release repo")
return gracefulExit(o.noFail, misconfigurationOutput)
}
masterConfig, err := config.GetAllConfigsFromSHA(o.releaseRepoPath, jobSpec.Refs.BaseSHA, logger)
if err != nil {
logger.WithError(err).Error("could not load configuration from base revision of release repo")
Expand Down Expand Up @@ -214,7 +228,7 @@ func rehearseMain() int {
return gracefulExit(o.noFail, misconfigurationOutput)
}

cmManager := config.NewTemplateCMManager(cmClient, prNumber, logger)
cmManager := config.NewTemplateCMManager(cmClient, pluginConfig, prNumber, o.releaseRepoPath, logger)
defer func() {
if err := cmManager.CleanupCMTemplates(); err != nil {
logger.WithError(err).Error("failed to clean up temporary template CM")
Expand All @@ -224,7 +238,7 @@ func rehearseMain() int {
logger.WithError(err).Error("couldn't create template configMap")
return gracefulExit(o.noFail, failedSetupOutput)
}
if err := cmManager.CreateClusterProfiles(filepath.Join(o.releaseRepoPath, config.ClusterProfilesPath), changedClusterProfiles); err != nil {
if err := cmManager.CreateClusterProfiles(changedClusterProfiles); err != nil {
logger.WithError(err).Error("couldn't create cluster profile ConfigMaps")
return gracefulExit(o.noFail, failedSetupOutput)
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
const (
// ConfigInRepoPath is the prow config path from release repo
ConfigInRepoPath = "cluster/ci/config/prow/config.yaml"
// PluginConfigInRepoPath is the prow plugin config path from release repo
PluginConfigInRepoPath = "cluster/ci/config/prow/plugins.yaml"
// JobConfigInRepoPath is the prowjobs path from release repo
JobConfigInRepoPath = "ci-operator/jobs"
// CiopConfigInRepoPath is the ci-operator config path from release repo
Expand Down
100 changes: 63 additions & 37 deletions pkg/config/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ import (
kutilerrors "k8s.io/apimachinery/pkg/util/errors"

corev1 "k8s.io/client-go/kubernetes/typed/core/v1"

prowgithub "k8s.io/test-infra/prow/github"
_ "k8s.io/test-infra/prow/hook"
prowplugins "k8s.io/test-infra/prow/plugins"
"k8s.io/test-infra/prow/plugins/updateconfig"
)

// CiTemplates is a map of all the changed templates
Expand Down Expand Up @@ -58,17 +63,27 @@ func getTemplates(templatePath string) (CiTemplates, error) {

// TemplateCMManager holds the details needed for the configmap controller
type TemplateCMManager struct {
cmclient corev1.ConfigMapInterface
prNumber int
logger *logrus.Entry
cmclient corev1.ConfigMapInterface
configUpdaterCfg prowplugins.ConfigUpdater
prNumber int
releaseRepoPath string
logger *logrus.Entry
}

// NewTemplateCMManager creates a new TemplateCMManager
func NewTemplateCMManager(cmclient corev1.ConfigMapInterface, prNumber int, logger *logrus.Entry) *TemplateCMManager {
func NewTemplateCMManager(
cmclient corev1.ConfigMapInterface,
configUpdaterCfg prowplugins.ConfigUpdater,
prNumber int,
releaseRepoPath string,
logger *logrus.Entry,
) *TemplateCMManager {
return &TemplateCMManager{
cmclient: cmclient,
prNumber: prNumber,
logger: logger,
cmclient: cmclient,
configUpdaterCfg: configUpdaterCfg,
prNumber: prNumber,
releaseRepoPath: releaseRepoPath,
logger: logger,
}
}

Expand Down Expand Up @@ -103,43 +118,54 @@ func (c *TemplateCMManager) CreateCMTemplates(templates CiTemplates) error {
return kutilerrors.NewAggregate(errors)
}

func (c *TemplateCMManager) CreateClusterProfiles(dir string, profiles []ClusterProfile) error {
var errs []error
for _, p := range profiles {
cm, err := genClusterProfileCM(dir, p)
if err != nil {
errs = append(errs, err)
continue
}
c.logger.WithFields(logrus.Fields{"cluster-profile": cm.ObjectMeta.Name}).Info("creating rehearsal cluster profile ConfigMap")
if err := c.createCM(cm); err != nil {
errs = append(errs, err)
}
}
return kutilerrors.NewAggregate(errs)
type osFileGetter struct {
root string
}

func genClusterProfileCM(dir string, profile ClusterProfile) (*v1.ConfigMap, error) {
ret := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Name: profile.CMName()},
Data: map[string]string{},
}
profilePath := filepath.Join(dir, profile.Name)
err := filepath.Walk(profilePath, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() {
func (g osFileGetter) GetFile(filename string) ([]byte, error) {
return ioutil.ReadFile(filepath.Join(g.root, filename))
}

func (c *TemplateCMManager) CreateClusterProfiles(profiles []ClusterProfile) error {
changes := []prowgithub.PullRequestChange{}
for _, profile := range profiles {
err := filepath.Walk(filepath.Join(c.releaseRepoPath, ClusterProfilesPath, profile.Name), func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() {
return err
}
// Failure is impossible per filepath.Walk's API.
if path, err = filepath.Rel(c.releaseRepoPath, path); err == nil {
changes = append(changes, prowgithub.PullRequestChange{
Filename: path,
Status: string(prowgithub.PullRequestFileModified),
})
}
return err
}
b, err := ioutil.ReadFile(path)
})
if err != nil {
return err
}
ret.Data[filepath.Base(path)] = string(b)
return nil
})
if err != nil {
return nil, err
}
return ret, nil
var errs []error
for cm, data := range updateconfig.FilterChanges(c.configUpdaterCfg, changes, c.logger) {
profile := strings.TrimPrefix(cm.Name, "cluster-profile-")
for _, p := range profiles {
if p.Name == profile {
profile = p.CMName()
break
}
}
err := c.createCM(&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Name: profile},
Data: map[string]string{},
})
if err != nil && !kerrors.IsAlreadyExists(err) {
errs = append(errs, err)
} else if err := updateconfig.Update(osFileGetter{root: c.releaseRepoPath}, c.cmclient, profile, cm.Namespace, data, c.logger); err != nil {
errs = append(errs, err)
}
}
return kutilerrors.NewAggregate(errs)
}

// CleanupCMTemplates deletes all the configMaps that have been created for the changed templates.
Expand Down
114 changes: 53 additions & 61 deletions pkg/config/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"path/filepath"
"reflect"
"sort"
"strconv"
"testing"

Expand All @@ -19,6 +20,8 @@ import (
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/client-go/kubernetes/fake"
coretesting "k8s.io/client-go/testing"

prowplugins "k8s.io/test-infra/prow/plugins"
)

const templatesPath = "../../test/pj-rehearse-integration/master/ci-operator/templates"
Expand Down Expand Up @@ -63,7 +66,7 @@ func TestCreateCleanupCMTemplates(t *testing.T) {
return true, nil, nil
})
client := cs.CoreV1().ConfigMaps(ns)
cmManager := NewTemplateCMManager(client, 1234, logrus.NewEntry(logrus.New()))
cmManager := NewTemplateCMManager(client, prowplugins.ConfigUpdater{}, 1234, "not_used", logrus.NewEntry(logrus.New()))
if err := cmManager.CreateCMTemplates(ciTemplates); err != nil {
t.Fatalf("CreateCMTemplates() returned error: %v", err)
}
Expand Down Expand Up @@ -101,45 +104,6 @@ func getBaseCiTemplates(t *testing.T) CiTemplates {
return CiTemplates{"test-template.yaml": contents}
}

func TestGenClusterProfileCM(t *testing.T) {
dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
profile := ClusterProfile{
Name: "test-profile",
TreeHash: "abcdef0123456789abcdef0123456789abcdef01",
}
profilePath := filepath.Join(dir, profile.Name)
if err := os.Mkdir(profilePath, 0775); err != nil {
t.Fatal(err)
}
files := []string{"vars.yaml", "vars-origin.yaml"}
for _, f := range files {
if err := ioutil.WriteFile(filepath.Join(profilePath, f), []byte(f+" content"), 0664); err != nil {
t.Fatal(err)
}
}
cm, err := genClusterProfileCM(dir, profile)
if err != nil {
t.Fatal(err)
}
name := "rehearse-cluster-profile-test-profile-abcde"
if n := cm.ObjectMeta.Name; n != name {
t.Errorf("unexpected name: want %q, got %q", name, n)
}
for _, f := range files {
e, d := f+" content", cm.Data[f]
if d != e {
t.Errorf("unexpected value for key %q: want %q, got %q", f, e, d)
}
}
if t.Failed() {
t.Logf("full CM content: %s", cm.Data)
}
}

func TestCreateClusterProfiles(t *testing.T) {
dir, err := ioutil.TempDir("", "")
if err != nil {
Expand All @@ -152,40 +116,68 @@ func TestCreateClusterProfiles(t *testing.T) {
{Name: "unchanged", TreeHash: "8012ff51a005eaa8ed8f4c08ccdce580f462fff6"},
}
for _, p := range profiles {
if err := os.Mkdir(filepath.Join(dir, p.Name), 0775); err != nil {
path := filepath.Join(dir, ClusterProfilesPath, p.Name)
if err := os.MkdirAll(path, 0775); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(filepath.Join(path, "file"), []byte(p.Name+" content"), 0664); err != nil {
t.Fatal(err)
}
}
profiles = profiles[:2]
ns := "test"
pr := 1234
configUpdaterCfg := prowplugins.ConfigUpdater{
Maps: map[string]prowplugins.ConfigMapSpec{
filepath.Join(ClusterProfilesPath, "profile0", "file"): {
Name: "cluster-profile-profile0",
Namespaces: []string{ns},
},
filepath.Join(ClusterProfilesPath, "profile1", "file"): {
Name: "cluster-profile-profile1",
Namespaces: []string{ns},
},
filepath.Join(ClusterProfilesPath, "unchanged", "file"): {
Name: "cluster-profile-unchanged",
Namespaces: []string{ns},
},
},
}
cs := fake.NewSimpleClientset()
client := cs.CoreV1().ConfigMaps(ns)
m := NewTemplateCMManager(client, pr, logrus.NewEntry(logrus.New()))
if err := m.CreateClusterProfiles(dir, profiles); err != nil {
m := NewTemplateCMManager(client, configUpdaterCfg, pr, dir, logrus.NewEntry(logrus.New()))
if err := m.CreateClusterProfiles(profiles); err != nil {
t.Fatal(err)
}
cms, err := client.List(metav1.ListOptions{})
sort.Slice(cms.Items, func(i, j int) bool {
return cms.Items[i].Name < cms.Items[j].Name
})
if err != nil {
t.Fatal(err)
}
var names []string
for _, p := range cms.Items {
names = append(names, p.Name)
}
expected := []string{
"rehearse-cluster-profile-profile0-e92d4",
"rehearse-cluster-profile-profile1-a8c99",
}
if !reflect.DeepEqual(expected, names) {
t.Fatal(diff.ObjectDiff(expected, names))
}
for _, cm := range cms.Items {
if cm.Labels[createByRehearse] != "true" {
t.Fatalf("%q doesn't have label %s=true", cm.Name, createByRehearse)
}
if cm.Labels[rehearseLabelPull] != strconv.Itoa(pr) {
t.Fatalf("%q doesn't have label %s=%d", cm.Name, rehearseLabelPull, pr)
}
expected := []v1.ConfigMap{{
ObjectMeta: metav1.ObjectMeta{
Name: "rehearse-cluster-profile-profile0-e92d4",
Namespace: ns,
Labels: map[string]string{
createByRehearse: "true",
rehearseLabelPull: strconv.Itoa(pr),
},
},
Data: map[string]string{"file": "profile0 content"},
}, {
ObjectMeta: metav1.ObjectMeta{
Name: "rehearse-cluster-profile-profile1-a8c99",
Namespace: ns,
Labels: map[string]string{
createByRehearse: "true",
rehearseLabelPull: strconv.Itoa(pr),
},
},
Data: map[string]string{"file": "profile1 content"},
}}
if !equality.Semantic.DeepEqual(expected, cms.Items) {
t.Fatal(diff.ObjectDiff(expected, cms.Items))
}
}
11 changes: 10 additions & 1 deletion pkg/rehearse/jobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,16 @@ func NewCMClient(clusterConfig *rest.Config, namespace string, dry bool) (corecl
if err != nil {
return true, nil, fmt.Errorf("failed to convert ConfigMap to YAML: %v", err)
}
fmt.Printf("%s", y)
fmt.Print(string(y))
return false, nil, nil
})
c.PrependReactor("update", "configmaps", func(action coretesting.Action) (bool, runtime.Object, error) {
cm := action.(coretesting.UpdateAction).GetObject().(*v1.ConfigMap)
y, err := yaml.Marshal([]*v1.ConfigMap{cm})
if err != nil {
return true, nil, fmt.Errorf("failed to convert ConfigMap to YAML: %v", err)
}
fmt.Print(string(y))
return false, nil, nil
})
return c.CoreV1().ConfigMaps(namespace), nil
Expand Down
7 changes: 7 additions & 0 deletions test/pj-rehearse-integration/expected.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@
ci.openshift.org/rehearse-pull: "1234"
created-by-pj-rehearse: "true"
name: rehearse-hnq8xb9r-test-template
- metadata:
creationTimestamp: null
labels:
ci.openshift.org/rehearse-pull: "1234"
created-by-pj-rehearse: "true"
name: rehearse-cluster-profile-test-profile-47224
- data:
vars-origin.yaml: |
vars-origin.yaml
Expand All @@ -141,6 +147,7 @@
ci.openshift.org/rehearse-pull: "1234"
created-by-pj-rehearse: "true"
name: rehearse-cluster-profile-test-profile-47224
namespace: test-namespace
- apiVersion: prow.k8s.io/v1
kind: ProwJob
metadata:
Expand Down
Loading