cmd/config_merger/main.go (101 lines of code) (raw):
/*
Copyright 2021 The Kubernetes 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 (
"context"
"flag"
"io/ioutil"
"net/http"
"time"
"github.com/GoogleCloudPlatform/testgrid/pkg/merger"
"github.com/GoogleCloudPlatform/testgrid/util/gcs"
"github.com/GoogleCloudPlatform/testgrid/util/metrics/prometheus"
"github.com/sirupsen/logrus"
)
const componentName = "config-merger"
type options struct {
listPath string
listURL string
creds string
confirm bool
wait time.Duration
skipValidate bool
}
func (o *options) validate(log logrus.FieldLogger) {
if o.listPath == "" && o.listURL == "" {
log.Fatal("List of configurations to merge required (--config-list or --config-url)")
}
if !o.confirm {
log.Info("--confirm=false (DRY-RUN): will not write to gcs")
}
if o.skipValidate {
log.Info("--allow-invalid-configs: result may not validate either")
}
}
func gatherOptions() options {
var o options
flag.StringVar(&o.listPath, "config-list", "", "List of configurations to merge (at file)")
flag.StringVar(&o.listURL, "config-url", "", "List of configurations to merge (at web URL)")
flag.StringVar(&o.creds, "gcp-service-account", "", "/path/to/gcp/creds (use local creds if empty)")
flag.BoolVar(&o.confirm, "confirm", false, "Upload data if set")
flag.DurationVar(&o.wait, "wait", 0, "Ensure at least this much time ahs passed since the last loop. (Run only once if zero)")
flag.BoolVar(&o.skipValidate, "allow-invalid-configs", false, "Allows merging of configs that don't validate. Usually skips invalid configs")
flag.Parse()
return o
}
func main() {
log := logrus.WithField("component", componentName)
opt := gatherOptions()
opt.validate(log)
var file []byte
if opt.listPath != "" {
var err error
file, err = ioutil.ReadFile(opt.listPath)
if err != nil {
log.WithField("--config-list", opt.listPath).WithError(err).Fatalf("Can't find --config-list")
}
}
if opt.listURL != "" {
resp, err := http.Get(opt.listURL)
if err != nil {
log.WithField("--config-url", opt.listURL).WithError(err).Fatalf("Can't GET --config-url")
}
defer resp.Body.Close()
file, err = ioutil.ReadAll(resp.Body)
if err != nil {
log.WithField("--config-url", opt.listURL).WithError(err).Fatalf("Can't read contents at --config-url")
}
}
list, err := merger.ParseAndCheck(file)
if err != nil {
log.WithError(err).Fatal("Can't parse YAML merge config")
}
log.WithField("merge-list", list).Debug("YAML mergelist read successful")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
storageClient, err := gcs.ClientWithCreds(ctx, opt.creds)
if err != nil {
log.WithError(err).Fatalf("Can't make storage client")
}
client := gcs.NewClient(storageClient)
mets := merger.CreateMetrics(prometheus.NewFactory())
updateOnce := func(ctx context.Context) {
ctx, cancel := context.WithTimeout(ctx, 10*time.Minute)
defer cancel()
log.Info("Starting MergeAndUpdate")
_, err := merger.MergeAndUpdate(ctx, client, mets, list, opt.skipValidate, opt.confirm)
if err != nil {
log.WithError(err).Error("Update failed")
return
}
}
updateOnce(ctx)
if opt.wait == 0 {
return
}
timer := time.NewTimer(opt.wait)
defer timer.Stop()
for range timer.C {
timer.Reset(opt.wait)
updateOnce(ctx)
log.WithField("--wait", opt.wait).Info("Sleeping")
}
}