github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/pipelinescheduler/generator.go (about)

     1  package pipelinescheduler
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"sort"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/google/go-cmp/cmp"
    13  	"github.com/google/go-cmp/cmp/cmpopts"
    14  	"github.com/jenkins-x/jx-logging/pkg/log"
    15  	"github.com/olli-ai/jx/v2/pkg/kube"
    16  	"github.com/olli-ai/jx/v2/pkg/kube/naming"
    17  	"github.com/olli-ai/jx/v2/pkg/prow"
    18  	"github.com/jenkins-x/lighthouse/pkg/config/branchprotection"
    19  	"github.com/jenkins-x/lighthouse/pkg/config/job"
    20  	"github.com/jenkins-x/lighthouse/pkg/config/keeper"
    21  
    22  	"github.com/google/uuid"
    23  	"github.com/olli-ai/jx/v2/pkg/environments"
    24  	"github.com/olli-ai/jx/v2/pkg/gits"
    25  	"github.com/olli-ai/jx/v2/pkg/helm"
    26  	"github.com/jenkins-x/lighthouse/pkg/config"
    27  	"github.com/jenkins-x/lighthouse/pkg/plugins"
    28  	v1 "k8s.io/api/core/v1"
    29  	kubeerrors "k8s.io/apimachinery/pkg/api/errors"
    30  	"k8s.io/helm/pkg/proto/hapi/chart"
    31  
    32  	"k8s.io/client-go/kubernetes"
    33  
    34  	"github.com/ghodss/yaml"
    35  
    36  	jenkinsv1 "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io/v1"
    37  	"github.com/jenkins-x/jx-api/pkg/client/clientset/versioned"
    38  	"github.com/pkg/errors"
    39  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    40  )
    41  
    42  // GenerateProw will generate the prow config for the namespace
    43  func GenerateProw(gitOps bool, autoApplyConfigUpdater bool, jxClient versioned.Interface, namespace string, teamSchedulerName string, devEnv *jenkinsv1.Environment, loadSchedulerResourcesFunc func(versioned.Interface, string) (map[string]*jenkinsv1.Scheduler, *jenkinsv1.SourceRepositoryGroupList, *jenkinsv1.SourceRepositoryList, error)) (*config.Config,
    44  	*plugins.Configuration, error) {
    45  	if loadSchedulerResourcesFunc == nil {
    46  		loadSchedulerResourcesFunc = loadSchedulerResources
    47  	}
    48  	schedulers, sourceRepoGroups, sourceRepos, err := loadSchedulerResourcesFunc(jxClient, namespace)
    49  	if err != nil {
    50  		return nil, nil, errors.Wrapf(err, "loading scheduler resources")
    51  	}
    52  	if sourceRepos == nil || len(sourceRepos.Items) < 1 {
    53  		return nil, nil, errors.New("No source repository resources were found")
    54  	}
    55  	defaultScheduler := schedulers[teamSchedulerName]
    56  	leaves := make([]*SchedulerLeaf, 0)
    57  	for _, sourceRepo := range sourceRepos.Items {
    58  		applicableSchedulers := []*jenkinsv1.SchedulerSpec{}
    59  		// Apply config-updater to devEnv
    60  		applicableSchedulers = addConfigUpdaterToDevEnv(gitOps, autoApplyConfigUpdater, applicableSchedulers, devEnv, &sourceRepo.Spec)
    61  		// Apply repo scheduler
    62  		applicableSchedulers = addRepositoryScheduler(sourceRepo, schedulers, applicableSchedulers)
    63  		// Apply project schedulers
    64  		applicableSchedulers = addProjectSchedulers(sourceRepoGroups, sourceRepo, schedulers, applicableSchedulers)
    65  		// Apply team scheduler
    66  		applicableSchedulers = addTeamScheduler(teamSchedulerName, defaultScheduler, applicableSchedulers)
    67  		if len(applicableSchedulers) < 1 {
    68  			continue
    69  		}
    70  		merged, err := Build(applicableSchedulers)
    71  		if err != nil {
    72  			return nil, nil, errors.Wrapf(err, "building scheduler")
    73  		}
    74  		leaves = append(leaves, &SchedulerLeaf{
    75  			Repo:          sourceRepo.Spec.Repo,
    76  			Org:           sourceRepo.Spec.Org,
    77  			SchedulerSpec: merged,
    78  		})
    79  		if err != nil {
    80  			return nil, nil, errors.Wrapf(err, "building prow config")
    81  		}
    82  	}
    83  	cfg, plugs, err := BuildProwConfig(leaves)
    84  	if err != nil {
    85  		return nil, nil, errors.Wrapf(err, "building prow config")
    86  	}
    87  	if cfg != nil {
    88  		cfg.PodNamespace = namespace
    89  		cfg.LighthouseJobNamespace = namespace
    90  	}
    91  	return cfg, plugs, nil
    92  }
    93  
    94  func loadSchedulerResources(jxClient versioned.Interface, namespace string) (map[string]*jenkinsv1.Scheduler, *jenkinsv1.SourceRepositoryGroupList, *jenkinsv1.SourceRepositoryList, error) {
    95  	schedulers, err := jxClient.JenkinsV1().Schedulers(namespace).List(metav1.ListOptions{})
    96  	if err != nil {
    97  		return nil, nil, nil, errors.WithStack(err)
    98  	}
    99  	if len(schedulers.Items) == 0 {
   100  		return nil, nil, nil, errors.New("No pipeline schedulers are configured")
   101  	}
   102  	lookup := make(map[string]*jenkinsv1.Scheduler)
   103  	for _, item := range schedulers.Items {
   104  		lookup[item.Name] = item.DeepCopy()
   105  	}
   106  	// Process Schedulers linked to SourceRepositoryGroups
   107  	sourceRepoGroups, err := jxClient.JenkinsV1().SourceRepositoryGroups(namespace).List(metav1.ListOptions{})
   108  	if err != nil {
   109  		return nil, nil, nil, errors.Wrapf(err, "Error finding source repository groups")
   110  	}
   111  	// Process Schedulers linked to SourceRepositoryGroups
   112  	sourceRepos, err := jxClient.JenkinsV1().SourceRepositories(namespace).List(metav1.ListOptions{})
   113  	if err != nil {
   114  		return nil, nil, nil, errors.Wrapf(err, "Error finding source repositories")
   115  	}
   116  	return lookup, sourceRepoGroups, sourceRepos, nil
   117  }
   118  
   119  // CreateSchedulersFromProwConfig will generate Pipeline Schedulers from the prow configmaps in the specified namespace or the config and plugins files specified as an option
   120  func CreateSchedulersFromProwConfig(configFileLocation string, pluginsFileLocation string, skipVerification bool, dryRun bool, gitOps bool, jxClient versioned.Interface, kubeClient kubernetes.Interface, namespace string, teamSchedulerName string, devEnv *jenkinsv1.Environment) ([]*jenkinsv1.SourceRepositoryGroup, []*jenkinsv1.SourceRepository, map[string]*jenkinsv1.Scheduler, error) {
   121  	prowConfig, pluginConfig, err := loadExistingProwConfig(configFileLocation, pluginsFileLocation, kubeClient, namespace)
   122  	if err != nil {
   123  		return nil, nil, nil, err
   124  	}
   125  
   126  	if prowConfig.LighthouseJobNamespace == "" {
   127  		prowConfig.LighthouseJobNamespace = "jx"
   128  	}
   129  	sourceRepoGroups, sourceRepositories, sourceRepoMap, schedulers, err := BuildSchedulers(prowConfig, pluginConfig)
   130  	teamSchedulerName = "default-scheduler"
   131  	cleanupExistingProwConfig(prowConfig, pluginConfig, sourceRepoMap)
   132  
   133  	migratedConfigFunc := func(jxClient versioned.Interface, namespace string) (map[string]*jenkinsv1.Scheduler, *jenkinsv1.SourceRepositoryGroupList, *jenkinsv1.SourceRepositoryList, error) {
   134  		values := []jenkinsv1.SourceRepository{}
   135  		for _, value := range sourceRepositories {
   136  			values = append(values, *value)
   137  		}
   138  		return schedulers, nil, &jenkinsv1.SourceRepositoryList{Items: values}, nil
   139  	}
   140  	if !skipVerification {
   141  		log.Logger().Info("Verifying generated config")
   142  		migratedConfig, migratedPlugins, err := GenerateProw(gitOps, false, jxClient, namespace, teamSchedulerName, devEnv, migratedConfigFunc)
   143  		if err != nil {
   144  			return nil, nil, nil, errors.Wrap(err, "Error generating prow config")
   145  		}
   146  		if dryRun {
   147  			dumpProwConfigToFiles("migrated", migratedConfig, migratedPlugins)
   148  			dumpProwConfigToFiles("existing", prowConfig, pluginConfig)
   149  		}
   150  		sortSliceStringOpt := cmpopts.SortSlices(func(i string, j string) bool {
   151  			return i < j
   152  		})
   153  		pluginCmpOptions := cmp.Options{sortSliceStringOpt,
   154  			cmpopts.SortSlices(func(i plugins.Trigger, j plugins.Trigger) bool {
   155  				iString := strings.Join(i.Repos, ",")
   156  				jString := strings.Join(j.Repos, ",")
   157  				return iString < jString
   158  			}), cmpopts.SortSlices(func(i plugins.Approve, j plugins.Approve) bool {
   159  				iString := strings.Join(i.Repos, ",")
   160  				jString := strings.Join(j.Repos, ",")
   161  				return iString < jString
   162  			}), cmpopts.SortSlices(func(i plugins.Lgtm, j plugins.Lgtm) bool {
   163  				iString := strings.Join(i.Repos, ",")
   164  				jString := strings.Join(j.Repos, ",")
   165  				return iString < jString
   166  			}),
   167  		}
   168  
   169  		if !cmp.Equal(migratedPlugins, pluginConfig, pluginCmpOptions) {
   170  			return nil, nil, nil, errors.Errorf("Migrated Prow plugins do not match, not applying! \nDiff: \n%s", cmp.Diff(migratedPlugins, pluginConfig, pluginCmpOptions))
   171  		}
   172  		cnfgCmpOptions := cmp.Options{
   173  			cmpopts.IgnoreUnexported(job.Brancher{}),
   174  			cmpopts.IgnoreUnexported(job.RegexpChangeMatcher{}),
   175  			cmpopts.IgnoreUnexported(job.Presubmit{}),
   176  			cmpopts.IgnoreUnexported(job.Periodic{}),
   177  			sortSliceStringOpt,
   178  			cmpopts.SortSlices(func(i keeper.Query, j keeper.Query) bool {
   179  				sort.Strings(i.Repos)
   180  				sort.Strings(j.Repos)
   181  				iLabels := append(i.Labels, i.MissingLabels...)
   182  				iString := strings.Join(append(i.Repos, iLabels...), ",")
   183  				jLabels := append(j.Labels, j.MissingLabels...)
   184  				jString := strings.Join(append(j.Repos, jLabels...), ",")
   185  				return iString < jString
   186  			})}
   187  		if !cmp.Equal(migratedConfig, prowConfig, cnfgCmpOptions) {
   188  			return nil, nil, nil, errors.Errorf("Migrated Prow config do not match, not applying! \nDiff: \n%s", cmp.Diff(migratedConfig, prowConfig, cnfgCmpOptions))
   189  		}
   190  		log.Logger().Info("Generated config passed validation")
   191  	}
   192  	return sourceRepoGroups, sourceRepositories, schedulers, nil
   193  }
   194  
   195  //cleanupExistingProwConfig Removes config that we do not currently support
   196  func cleanupExistingProwConfig(prowConfig *config.Config, pluginConfig *plugins.Configuration, sourceRepoMap map[string]*jenkinsv1.SourceRepository) {
   197  	// heart plugin is not supported
   198  	pluginConfig.Heart = plugins.Heart{}
   199  	queries := prowConfig.Keeper.Queries[:0]
   200  	for _, query := range prowConfig.Keeper.Queries {
   201  		repos := query.Repos[:0]
   202  		for _, repo := range query.Repos {
   203  			// We do not support tide queries for repos with not presubmits or postsubmits, ignore the dummy environment for now
   204  			if sourceRepoMap[repo] != nil {
   205  				repos = append(repos, repo)
   206  			}
   207  		}
   208  		if len(repos) > 0 {
   209  			query.Repos = repos
   210  			queries = append(queries, query)
   211  		}
   212  	}
   213  	prowConfig.Keeper.Queries = queries
   214  
   215  	pluginConfig.ConfigUpdater = plugins.ConfigUpdater{}
   216  	triggers := make([]plugins.Trigger, 0)
   217  	for _, trigger := range pluginConfig.Triggers {
   218  		for _, repo := range trigger.Repos {
   219  			// We do not support tide queries for repos with not presubmits or postsubmits, ignore the dummy environment for now
   220  			if sourceRepoMap[repo] != nil {
   221  				triggers = append(triggers, trigger)
   222  			}
   223  		}
   224  	}
   225  	pluginConfig.Triggers = triggers
   226  
   227  	lgtms := make([]plugins.Lgtm, 0)
   228  	for _, lgtm := range pluginConfig.Lgtm {
   229  		for _, repo := range lgtm.Repos {
   230  			// We do not support tide queries for repos with not presubmits or postsubmits, ignore the dummy environment for now
   231  			if sourceRepoMap[repo] != nil {
   232  				newLgtm := lgtm
   233  				newLgtm.Repos = []string{repo}
   234  				lgtms = append(lgtms, newLgtm)
   235  			}
   236  		}
   237  	}
   238  	pluginConfig.Lgtm = lgtms
   239  
   240  	approves := make([]plugins.Approve, 0)
   241  	for _, approve := range pluginConfig.Approve {
   242  		for _, repo := range approve.Repos {
   243  			if !strings.Contains(repo, "/") {
   244  				// Expand the org in to repos for now
   245  				for existingRepo := range sourceRepoMap {
   246  					if strings.HasPrefix(existingRepo, repo+"/") {
   247  						newApprove := approve
   248  						newApprove.Repos = []string{existingRepo}
   249  						approves = append(approves, newApprove)
   250  					}
   251  				}
   252  			} else {
   253  				// We do not support tide queries for repos with not presubmits or postsubmits, ignore the dummy environment for now
   254  				if sourceRepoMap[repo] != nil {
   255  					approves = append(approves, approve)
   256  				}
   257  			}
   258  		}
   259  	}
   260  	pluginConfig.Approve = approves
   261  
   262  	// Branch org protection policy out in to repos
   263  	for org := range prowConfig.BranchProtection.Orgs {
   264  		protectionOrg := prowConfig.BranchProtection.Orgs[org]
   265  		if protectionOrg.Repos == nil {
   266  			protectionOrg.Repos = make(map[string]branchprotection.Repo)
   267  			replacedOrgPolicy := false
   268  			for existingRepo := range sourceRepoMap {
   269  				if strings.HasPrefix(existingRepo, org+"/") {
   270  					orgRepo := strings.Split(existingRepo, "/")
   271  					repoPolicy := branchprotection.Repo{
   272  						Policy: protectionOrg.Policy,
   273  					}
   274  					protectionOrg.Repos[orgRepo[1]] = repoPolicy
   275  					prowConfig.BranchProtection.Orgs[org] = protectionOrg
   276  					replacedOrgPolicy = true
   277  				}
   278  			}
   279  			if replacedOrgPolicy {
   280  				protectionOrg := prowConfig.BranchProtection.Orgs[org]
   281  				protectionOrg.Policy = branchprotection.Policy{}
   282  				prowConfig.BranchProtection.Orgs[org] = protectionOrg
   283  			}
   284  		}
   285  	}
   286  
   287  	for repo, plugins := range pluginConfig.ExternalPlugins {
   288  		if plugins == nil {
   289  			delete(pluginConfig.ExternalPlugins, repo)
   290  			continue
   291  		}
   292  		if sourceRepoMap[repo] == nil {
   293  			delete(pluginConfig.ExternalPlugins, repo)
   294  		}
   295  	}
   296  	for repo := range pluginConfig.Plugins {
   297  		if sourceRepoMap[repo] == nil {
   298  			delete(pluginConfig.Plugins, repo)
   299  		}
   300  	}
   301  }
   302  
   303  func dumpProwConfigToFiles(prefix string, prowConfig *config.Config, pluginConfig *plugins.Configuration) {
   304  	migratedConfigFile := prefix + "Config.yaml"
   305  	cnfBytes, err := yaml.Marshal(prowConfig)
   306  	if err != nil {
   307  		log.Logger().Error("marshaling prow plugins config to yaml")
   308  	}
   309  	err = ioutil.WriteFile(migratedConfigFile, cnfBytes, 0600)
   310  	if err != nil {
   311  		log.Logger().Errorf("writing %s", migratedConfigFile)
   312  	}
   313  	log.Logger().Infof("Writing migrated config to %s", migratedConfigFile)
   314  	migratedPluginsFile := prefix + "Plugins.yaml"
   315  	plugsBytes, err := yaml.Marshal(pluginConfig)
   316  	if err != nil {
   317  		log.Logger().Error("marshaling prow plugins config to yaml")
   318  	}
   319  	err = ioutil.WriteFile(migratedPluginsFile, plugsBytes, 0600)
   320  	if err != nil {
   321  		log.Logger().Errorf("writing %s", migratedPluginsFile)
   322  	}
   323  	log.Logger().Infof("Writing migrated plugins to %s", migratedPluginsFile)
   324  }
   325  
   326  // GenerateSchedulers will generate Pipeline Schedulers from the prow configmaps in the specified namespace
   327  func loadExistingProwConfig(configFileLocation string, pluginsFileLocation string, kubeClient kubernetes.Interface, namespace string) (prowConfig *config.Config, pluginConfig *plugins.Configuration, err error) {
   328  	prowOptions := prow.Options{
   329  		KubeClient:          kubeClient,
   330  		NS:                  namespace,
   331  		ConfigFileLocation:  configFileLocation,
   332  		PluginsFileLocation: pluginsFileLocation,
   333  	}
   334  	if configFileLocation != "" {
   335  		prowConfig, err = prowOptions.LoadProwConfigFromFile()
   336  		log.Logger().Infof("Loading prow config from file %s", configFileLocation)
   337  	} else {
   338  		prowConfig, err = prowOptions.LoadProwConfig()
   339  		log.Logger().Info("Loading prow config from configmap")
   340  	}
   341  	if err != nil {
   342  		return nil, nil, errors.Wrap(err, "getting prow config")
   343  	}
   344  	if pluginsFileLocation != "" {
   345  		pluginConfig, err = prowOptions.LoadProwPluginsFromFile()
   346  		log.Logger().Infof("Loading prow plugins from file %s", pluginsFileLocation)
   347  	} else {
   348  		pluginConfig, err = prowOptions.LoadPluginConfig()
   349  		log.Logger().Info("Loading prow plugins from configmap")
   350  	}
   351  	if err != nil {
   352  		return nil, nil, errors.Wrap(err, "getting prow plugins config")
   353  	}
   354  	return prowConfig, pluginConfig, nil
   355  }
   356  
   357  func addTeamScheduler(defaultSchedulerName string, defaultScheduler *jenkinsv1.Scheduler, applicableSchedulers []*jenkinsv1.SchedulerSpec) []*jenkinsv1.SchedulerSpec {
   358  	if defaultScheduler != nil {
   359  		applicableSchedulers = append([]*jenkinsv1.SchedulerSpec{&defaultScheduler.Spec}, applicableSchedulers...)
   360  	} else {
   361  		if defaultSchedulerName != "" {
   362  			log.Logger().Warnf("A team pipeline scheduler named %s was configured but could not be found", defaultSchedulerName)
   363  		}
   364  	}
   365  	return applicableSchedulers
   366  }
   367  
   368  func addRepositoryScheduler(sourceRepo jenkinsv1.SourceRepository, lookup map[string]*jenkinsv1.Scheduler, applicableSchedulers []*jenkinsv1.SchedulerSpec) []*jenkinsv1.SchedulerSpec {
   369  	if sourceRepo.Spec.Scheduler.Name != "" {
   370  		scheduler := lookup[sourceRepo.Spec.Scheduler.Name]
   371  		if scheduler != nil {
   372  			applicableSchedulers = append([]*jenkinsv1.SchedulerSpec{&scheduler.Spec}, applicableSchedulers...)
   373  		} else {
   374  			log.Logger().Warnf("A scheduler named %s is referenced by repository(%s) but could not be found", sourceRepo.Spec.Scheduler.Name, sourceRepo.Name)
   375  		}
   376  	}
   377  	return applicableSchedulers
   378  }
   379  
   380  func addProjectSchedulers(sourceRepoGroups *jenkinsv1.SourceRepositoryGroupList, sourceRepo jenkinsv1.SourceRepository, lookup map[string]*jenkinsv1.Scheduler, applicableSchedulers []*jenkinsv1.SchedulerSpec) []*jenkinsv1.SchedulerSpec {
   381  	if sourceRepoGroups != nil {
   382  		for _, sourceGroup := range sourceRepoGroups.Items {
   383  			for _, groupRepo := range sourceGroup.Spec.SourceRepositorySpec {
   384  				if groupRepo.Name == sourceRepo.Name {
   385  					if sourceGroup.Spec.Scheduler.Name != "" {
   386  						scheduler := lookup[sourceGroup.Spec.Scheduler.Name]
   387  						if scheduler != nil {
   388  							applicableSchedulers = append([]*jenkinsv1.SchedulerSpec{&scheduler.Spec}, applicableSchedulers...)
   389  						} else {
   390  							log.Logger().Warnf("A scheduler named %s is referenced by repository group(%s) but could not be found", sourceGroup.Spec.Scheduler.Name, sourceGroup.Name)
   391  						}
   392  					}
   393  				}
   394  			}
   395  		}
   396  	}
   397  	return applicableSchedulers
   398  }
   399  
   400  func addConfigUpdaterToDevEnv(gitOps bool, autoApplyConfigUpdater bool, applicableSchedulers []*jenkinsv1.SchedulerSpec, devEnv *jenkinsv1.Environment, sourceRepo *jenkinsv1.SourceRepositorySpec) []*jenkinsv1.SchedulerSpec {
   401  	if gitOps && autoApplyConfigUpdater && strings.Contains(devEnv.Spec.Source.URL, sourceRepo.Org+"/"+sourceRepo.Repo) {
   402  		maps := make(map[string]jenkinsv1.ConfigMapSpec)
   403  		maps["env/prow/config.yaml"] = jenkinsv1.ConfigMapSpec{
   404  			Name: "config",
   405  		}
   406  		maps["env/prow/plugins.yaml"] = jenkinsv1.ConfigMapSpec{
   407  			Name: "plugins",
   408  		}
   409  		environmentUpdaterSpec := &jenkinsv1.SchedulerSpec{
   410  			ConfigUpdater: &jenkinsv1.ConfigUpdater{
   411  				Map: maps,
   412  			},
   413  			Plugins: &jenkinsv1.ReplaceableSliceOfStrings{
   414  				Items: []string{"config-updater"},
   415  			},
   416  		}
   417  		applicableSchedulers = append([]*jenkinsv1.SchedulerSpec{environmentUpdaterSpec}, applicableSchedulers...)
   418  	}
   419  	return applicableSchedulers
   420  }
   421  
   422  //ApplyDirectly directly applies the prow config to the cluster
   423  func ApplyDirectly(kubeClient kubernetes.Interface, namespace string, cfg *config.Config,
   424  	plugs *plugins.Configuration) error {
   425  	cfgYaml, err := yaml.Marshal(cfg)
   426  	if err != nil {
   427  		return errors.Wrapf(err, "marshalling config to yaml")
   428  	}
   429  	cfgConfigMap := &v1.ConfigMap{
   430  		ObjectMeta: metav1.ObjectMeta{
   431  			Name:      "config",
   432  			Namespace: namespace,
   433  		},
   434  		Data: map[string]string{
   435  			"config.yaml": string(cfgYaml),
   436  		},
   437  	}
   438  	plugsYaml, err := yaml.Marshal(plugs)
   439  	if err != nil {
   440  		return errors.Wrapf(err, "marshalling plugins to yaml")
   441  	}
   442  	plugsConfigMap := &v1.ConfigMap{
   443  		ObjectMeta: metav1.ObjectMeta{
   444  			Name:      "plugins",
   445  			Namespace: namespace,
   446  		},
   447  		Data: map[string]string{
   448  			"plugins.yaml": string(plugsYaml),
   449  		},
   450  	}
   451  	_, err = kubeClient.CoreV1().ConfigMaps(namespace).Update(cfgConfigMap)
   452  	if kubeerrors.IsNotFound(err) {
   453  		_, err := kubeClient.CoreV1().ConfigMaps(namespace).Create(cfgConfigMap)
   454  		if err != nil {
   455  			return errors.Wrapf(err, "creating ConfigMap config")
   456  		}
   457  	} else if err != nil {
   458  		return errors.Wrapf(err, "updating ConfigMap config")
   459  	}
   460  	_, err = kubeClient.CoreV1().ConfigMaps(namespace).Update(plugsConfigMap)
   461  	if kubeerrors.IsNotFound(err) {
   462  		_, err := kubeClient.CoreV1().ConfigMaps(namespace).Create(plugsConfigMap)
   463  		if err != nil {
   464  			return errors.Wrapf(err, "creating ConfigMap plugins")
   465  		}
   466  	} else if err != nil {
   467  		return errors.Wrapf(err, "updating ConfigMap plugins")
   468  	}
   469  	return nil
   470  }
   471  
   472  //ApplySchedulersDirectly directly applies pipeline schedulers to the cluster
   473  func ApplySchedulersDirectly(jxClient versioned.Interface, namespace string, sourceRepositoryGroups []*jenkinsv1.SourceRepositoryGroup, sourceRepositories []*jenkinsv1.SourceRepository, schedulers map[string]*jenkinsv1.Scheduler, devEnv *jenkinsv1.Environment) error {
   474  	log.Logger().Infof("Applying scheduler configuration to namespace %s", namespace)
   475  	err := jxClient.JenkinsV1().Schedulers(namespace).DeleteCollection(&metav1.DeleteOptions{}, metav1.ListOptions{})
   476  	if err != nil {
   477  		return errors.Wrapf(err, "Error removing existing schedulers")
   478  	}
   479  	for _, scheduler := range schedulers {
   480  		_, err := jxClient.JenkinsV1().Schedulers(namespace).Update(scheduler)
   481  		if kubeerrors.IsNotFound(err) {
   482  			_, err := jxClient.JenkinsV1().Schedulers(namespace).Create(scheduler)
   483  			if err != nil {
   484  				return errors.Wrapf(err, "creating scheduler")
   485  			}
   486  		} else if err != nil {
   487  			return errors.Wrapf(err, "updating scheduler")
   488  		}
   489  		if scheduler.Name == "default-scheduler" {
   490  			devEnv.Spec.TeamSettings.DefaultScheduler.Name = scheduler.Name
   491  			devEnv.Spec.TeamSettings.DefaultScheduler.Kind = "Scheduler"
   492  			_, err = jxClient.JenkinsV1().Environments(namespace).PatchUpdate(devEnv)
   493  			if err != nil {
   494  				return errors.Wrapf(err, "patch updating env %v", devEnv)
   495  			}
   496  		}
   497  	}
   498  	for _, repo := range sourceRepositories {
   499  		sourceRepo, err := kube.GetOrCreateSourceRepository(jxClient, namespace, repo.Spec.Repo, repo.Spec.Org, repo.Spec.Provider)
   500  		if err != nil || sourceRepo == nil {
   501  			return errors.New("Getting / creating source repo")
   502  		}
   503  		sourceRepo.Spec.Scheduler.Name = repo.Spec.Scheduler.Name
   504  		sourceRepo.Spec.Scheduler.Kind = repo.Spec.Scheduler.Kind
   505  		_, err = jxClient.JenkinsV1().SourceRepositories(namespace).Update(sourceRepo)
   506  		if err != nil {
   507  			return errors.Wrapf(err, "updating source repo")
   508  		}
   509  	}
   510  	for _, repoGroup := range sourceRepositoryGroups {
   511  		_, err := jxClient.JenkinsV1().SourceRepositoryGroups(namespace).Update(repoGroup)
   512  		if kubeerrors.IsNotFound(err) {
   513  			_, err := jxClient.JenkinsV1().SourceRepositoryGroups(namespace).Create(repoGroup)
   514  			if err != nil {
   515  				return errors.Wrapf(err, "creating source repo group")
   516  			}
   517  		} else if err != nil {
   518  			return errors.Wrapf(err, "updating source repo group")
   519  		}
   520  	}
   521  
   522  	return nil
   523  }
   524  
   525  //GitOpsOptions are options for running AddToEnvironmentRepo
   526  type GitOpsOptions struct {
   527  	Gitter              gits.Gitter
   528  	Verbose             bool
   529  	Helmer              helm.Helmer
   530  	GitProvider         gits.GitProvider
   531  	DevEnv              *jenkinsv1.Environment
   532  	PullRequestCloneDir string
   533  }
   534  
   535  // AddToEnvironmentRepo adds the prow config to the gitops environment repo
   536  func (o *GitOpsOptions) AddToEnvironmentRepo(cfg *config.Config, plugs *plugins.Configuration, kubeClient kubernetes.Interface, namespace string) error {
   537  	branchNameUUID, err := uuid.NewUUID()
   538  	if err != nil {
   539  		return errors.Wrapf(err, "creating creating branch name")
   540  	}
   541  	validBranchName := "add-prow-config" + branchNameUUID.String()
   542  	details := gits.PullRequestDetails{
   543  		BranchName: validBranchName,
   544  		Title:      fmt.Sprintf("Add Prow config"),
   545  		Message:    fmt.Sprintf("Add Prow config generated on %s", time.Now()),
   546  	}
   547  
   548  	modifyChartFn := func(requirements *helm.Requirements, metadata *chart.Metadata,
   549  		existingValues map[string]interface{},
   550  		templates map[string]string, dir string, pullRequestDetails *gits.PullRequestDetails) error {
   551  		prowDir := filepath.Join(dir, "prow")
   552  		err := os.MkdirAll(prowDir, 0700)
   553  		if err != nil {
   554  			return errors.Wrapf(err, "creating prow dir in gitops repo %s", prowDir)
   555  		}
   556  		cfgBytes, err := yaml.Marshal(cfg)
   557  		if err != nil {
   558  			return errors.Wrapf(err, "marshaling prow config to yaml")
   559  		}
   560  		cfgPath := filepath.Join(prowDir, "config.yaml")
   561  		err = ioutil.WriteFile(cfgPath, cfgBytes, 0600)
   562  		if err != nil {
   563  			return errors.Wrapf(err, "writing %s", cfgPath)
   564  		}
   565  
   566  		plugsBytes, err := yaml.Marshal(plugs)
   567  		if err != nil {
   568  			return errors.Wrapf(err, "marshaling prow plugins config to yaml")
   569  		}
   570  		plugsPath := filepath.Join(prowDir, "plugins.yaml")
   571  		err = ioutil.WriteFile(plugsPath, plugsBytes, 0600)
   572  		if err != nil {
   573  			return errors.Wrapf(err, "writing %s", plugsPath)
   574  		}
   575  		return nil
   576  	}
   577  
   578  	options := environments.EnvironmentPullRequestOptions{
   579  		Gitter:        o.Gitter,
   580  		ModifyChartFn: modifyChartFn,
   581  		GitProvider:   o.GitProvider,
   582  	}
   583  
   584  	info, err := options.Create(o.DevEnv, o.PullRequestCloneDir, &details, nil, "", false)
   585  
   586  	if err != nil {
   587  		return errors.Wrapf(err, "creating pr for prow config")
   588  	}
   589  	if info != nil {
   590  		log.Logger().Infof("Added prow config via Pull Request %s", info.PullRequest.URL)
   591  	}
   592  	err = o.RegisterProwConfigUpdater(kubeClient, namespace)
   593  	if err != nil {
   594  		return errors.Wrapf(err, "Updating prow configmap")
   595  	}
   596  	return nil
   597  }
   598  
   599  // RegisterProwConfigUpdater Register the config updater in the plugin configmap
   600  func (o *GitOpsOptions) RegisterProwConfigUpdater(kubeClient kubernetes.Interface, namespace string) error {
   601  	prowOptions := prow.Options{
   602  		KubeClient: kubeClient,
   603  		NS:         namespace,
   604  	}
   605  	pluginConfig, err := prowOptions.LoadPluginConfig()
   606  	if err != nil {
   607  		return errors.Wrapf(err, "getting plugins configmap")
   608  	}
   609  	pluginConfig.ConfigUpdater.Maps = make(map[string]plugins.ConfigMapSpec)
   610  	pluginConfig.ConfigUpdater.Maps["env/prow/config.yaml"] = plugins.ConfigMapSpec{Name: prow.ConfigMapName}
   611  	pluginConfig.ConfigUpdater.Maps["env/prow/plugins.yaml"] = plugins.ConfigMapSpec{Name: prow.PluginsConfigMapName}
   612  	pluginYAML, err := yaml.Marshal(pluginConfig)
   613  	if err != nil {
   614  		return errors.Wrap(err, "marshaling the prow plugins")
   615  	}
   616  
   617  	data := make(map[string]string)
   618  	data[prow.PluginsFilename] = string(pluginYAML)
   619  	cm := &v1.ConfigMap{
   620  		Data: data,
   621  		ObjectMeta: metav1.ObjectMeta{
   622  			Name: prow.PluginsConfigMapName,
   623  		},
   624  	}
   625  	_, err = kubeClient.CoreV1().ConfigMaps(namespace).Update(cm)
   626  	if err != nil {
   627  		err = errors.Wrapf(err, "updating the config map %q", prow.PluginsConfigMapName)
   628  	}
   629  	return nil
   630  }
   631  
   632  // AddSchedulersToEnvironmentRepo adds the prow config to the gitops environment repo
   633  func (o *GitOpsOptions) AddSchedulersToEnvironmentRepo(sourceRepositoryGroups []*jenkinsv1.SourceRepositoryGroup, sourceRepositories []*jenkinsv1.SourceRepository, schedulers map[string]*jenkinsv1.Scheduler) error {
   634  	branchNameUUID, err := uuid.NewUUID()
   635  	if err != nil {
   636  		return errors.Wrapf(err, "creating creating branch name")
   637  	}
   638  	log.Logger().Info("Saving scheduler config to environment repository")
   639  	validBranchName := "add-pipeline-schedulers" + branchNameUUID.String()
   640  	details := gits.PullRequestDetails{
   641  		BranchName: validBranchName,
   642  		Title:      fmt.Sprintf("Add pipeline schedulers"),
   643  		Message:    fmt.Sprintf("Add pipeline schedulers generated on %s", time.Now()),
   644  	}
   645  
   646  	modifyChartFn := func(requirements *helm.Requirements, metadata *chart.Metadata,
   647  		existingValues map[string]interface{},
   648  		templates map[string]string, dir string, pullRequestDetails *gits.PullRequestDetails) error {
   649  		templatesDir := filepath.Join(dir, "templates")
   650  		for _, repoGroup := range sourceRepositoryGroups {
   651  			cfgBytes, err := yaml.Marshal(repoGroup)
   652  			if err != nil {
   653  				return errors.Wrapf(err, "marshaling source repo group to yaml")
   654  			}
   655  			cfgPath := filepath.Join(templatesDir, repoGroup.Name+"-repo-group.yaml")
   656  			err = ioutil.WriteFile(cfgPath, cfgBytes, 0600)
   657  			if err != nil {
   658  				return errors.Wrapf(err, "writing %s", cfgPath)
   659  			}
   660  		}
   661  
   662  		for _, repo := range sourceRepositories {
   663  			repoName := naming.ToValidName(repo.Spec.Org + "-" + repo.Spec.Repo)
   664  			if repo.Name == "" {
   665  				repo.Name = repoName
   666  			}
   667  			cfgBytes, err := yaml.Marshal(repo)
   668  			if err != nil {
   669  				return errors.Wrapf(err, "marshaling source repos to yaml")
   670  			}
   671  			cfgPath := filepath.Join(templatesDir, repo.Name+"-repo.yaml")
   672  			err = ioutil.WriteFile(cfgPath, cfgBytes, 0600)
   673  			if err != nil {
   674  				return errors.Wrapf(err, "writing %s", cfgPath)
   675  			}
   676  		}
   677  		for _, scheduler := range schedulers {
   678  			cfgBytes, err := yaml.Marshal(scheduler)
   679  			if err != nil {
   680  				return errors.Wrapf(err, "marshaling schedulers to yaml")
   681  			}
   682  			cfgPath := filepath.Join(templatesDir, scheduler.Name+"-sch.yaml")
   683  			err = ioutil.WriteFile(cfgPath, cfgBytes, 0600)
   684  			if err != nil {
   685  				return errors.Wrapf(err, "writing %s", cfgPath)
   686  			}
   687  			if scheduler.Name == "default-scheduler" {
   688  				devEnvResource := &jenkinsv1.Environment{}
   689  				devEnvFile := filepath.Join(templatesDir, "dev-env.yaml")
   690  				devEnvData, err := ioutil.ReadFile(devEnvFile)
   691  				if err != nil {
   692  					return errors.Wrapf(err, "reading dev-env file %s", devEnvFile)
   693  				}
   694  				err = yaml.Unmarshal(devEnvData, devEnvResource)
   695  				if err != nil {
   696  					return errors.Wrapf(err, "reading dev-env yaml %s", devEnvFile)
   697  				}
   698  				devEnvResource.Spec.TeamSettings.DefaultScheduler.Kind = "Scheduler"
   699  				devEnvResource.Spec.TeamSettings.DefaultScheduler.Name = scheduler.Name
   700  				devEnvBytes, err := yaml.Marshal(devEnvResource)
   701  				if err != nil {
   702  					return errors.Wrapf(err, "reading dev-env resource %s", devEnvFile)
   703  				}
   704  				err = ioutil.WriteFile(devEnvFile, devEnvBytes, 0600)
   705  				if err != nil {
   706  					return errors.Wrapf(err, "writing %s", devEnvFile)
   707  				}
   708  			}
   709  		}
   710  		return nil
   711  	}
   712  
   713  	options := environments.EnvironmentPullRequestOptions{
   714  		Gitter:        o.Gitter,
   715  		ModifyChartFn: modifyChartFn,
   716  		GitProvider:   o.GitProvider,
   717  	}
   718  
   719  	info, err := options.Create(o.DevEnv, o.PullRequestCloneDir, &details, nil, "", false)
   720  
   721  	if err != nil {
   722  		return errors.Wrapf(err, "creating pr for scheduler config")
   723  	}
   724  	if info != nil {
   725  		log.Logger().Infof("Added pipeline scheduler config via Pull Request %s\n", info.PullRequest.URL)
   726  	}
   727  	return nil
   728  }