tooling/image-sync/main.go (96 lines of code) (raw):

// Copyright 2025 Microsoft Corporation // // 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 ( "encoding/json" defaultlog "log" "os" "time" "github.com/Azure/ARO-HCP/tooling/image-sync/internal" "github.com/spf13/cobra" "github.com/spf13/viper" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) var ( syncCmd = &cobra.Command{ Use: "image-sync", Short: "image-sync", Long: "image-sync", RunE: func(cmd *cobra.Command, args []string) error { return internal.DoSync(newSyncConfig()) }, } cfgFile string logLevel string ) func main() { syncCmd.Flags().StringVarP(&cfgFile, "cfgFile", "c", "", "Configuration File") syncCmd.Flags().StringVarP(&logLevel, "logLevel", "l", "", "Loglevel (info, debug, error, warn, fatal, panic)") cobra.OnInitialize(configureLogging) cobra.OnInitialize(initConfig) err := syncCmd.Execute() if err != nil { os.Exit(1) } } func initConfig() { if cfgFile != "" { viper.SetConfigFile(cfgFile) if err := viper.ReadInConfig(); err != nil { Log().Warnw("Error reading config file, using environment variables only", "error", err) } } } // newSyncConfig creates a new SyncConfig from the configuration file func newSyncConfig() *internal.SyncConfig { var sc *internal.SyncConfig v := viper.GetViper() v.SetDefault("numberoftags", 10) v.SetDefault("requesttimeout", 10) v.SetDefault("addlatest", false) // bind environment variables // we can't use vipers native viper.AutomaticEnv() because it only works // when also a config file is used envVars := map[string]string{ "NumberOfTags": "NUMBER_OF_TAGS", "RequestTimeout": "REQUEST_TIMEOUT", "AddLatest": "ADD_LATEST", "Repositories": "REPOSITORIES", "AcrTargetRegistry": "ACR_TARGET_REGISTRY", "TenantId": "TENANT_ID", "ManagedIdentityClientID": "MANAGED_IDENTITY_CLIENT_ID", } for key, env := range envVars { if err := v.BindEnv(key, env); err != nil { Log().Fatalw("Error while binding environment variable %s: %s", key, err.Error()) } } if err := v.Unmarshal(&sc); err != nil { Log().Fatalw("Error while unmarshalling configuration %s", err.Error()) } if secretEnv := os.Getenv("SECRETS"); secretEnv != "" { type listOfSecrets struct { Secrets []internal.Secrets } var s listOfSecrets err := json.Unmarshal([]byte(secretEnv), &s) if err != nil { Log().Fatal("Error unmarshalling configuration") } sc.Secrets = append(sc.Secrets, s.Secrets...) } Log().Debugw("Using configuration", "config", sc) return sc } func configureLogging() { loggerConfig := zap.NewDevelopmentConfig() loglevel, err := zap.ParseAtomicLevel(logLevel) if err != nil { defaultlog.Fatal(err) } loggerConfig.Level = loglevel loggerConfig.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(time.RFC3339) logger, err := loggerConfig.Build() zap.ReplaceGlobals(logger) if err != nil { defaultlog.Fatal(err) } } func Log() *zap.SugaredLogger { return zap.L().Sugar() }