github.com/verrazzano/verrazzano@v1.7.0/authproxy/src/config/config.go (about) 1 // Copyright (c) 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package config 5 6 import ( 7 "os" 8 "sync" 9 "sync/atomic" 10 "time" 11 12 "go.uber.org/zap" 13 ) 14 15 // these can be changed for unit testing 16 var ( 17 serviceURLFilename = "/etc/config/oidcServiceURL" 18 externalURLFilename = "/etc/config/oidcExternalURL" 19 clientIDFilename = "/etc/config/oidcClientID" 20 21 watchInterval = time.Minute 22 keepWatching atomic.Bool 23 ) 24 25 var ( 26 serviceURL string 27 externalURL string 28 clientID string 29 30 serviceURLFileModTime time.Time 31 externalURLFileModTime time.Time 32 clientIDFileModTime time.Time 33 34 mutex sync.RWMutex 35 ) 36 37 // GetServiceURL returns the in-cluster service URL of the OIDC provider 38 func GetServiceURL() string { 39 mutex.RLock() 40 defer mutex.RUnlock() 41 return serviceURL 42 } 43 44 // GetExternalURL returns the external URL of the OIDC provider 45 func GetExternalURL() string { 46 mutex.RLock() 47 defer mutex.RUnlock() 48 return externalURL 49 } 50 51 // GetClientID returns the client ID 52 func GetClientID() string { 53 mutex.RLock() 54 defer mutex.RUnlock() 55 return clientID 56 } 57 58 // loadServiceURL loads the in-cluster service URL from a file and stores the file modification time 59 func loadServiceURL() error { 60 mutex.Lock() 61 defer mutex.Unlock() 62 63 value, modTime, err := loadConfigValue(serviceURLFilename) 64 if err != nil { 65 return err 66 } 67 68 serviceURL = value 69 serviceURLFileModTime = *modTime 70 return nil 71 } 72 73 // loadExternalURL loads the external URL from a file and stores the file modification time 74 func loadExternalURL() error { 75 mutex.Lock() 76 defer mutex.Unlock() 77 78 value, modTime, err := loadConfigValue(externalURLFilename) 79 if err != nil { 80 return err 81 } 82 83 externalURL = value 84 externalURLFileModTime = *modTime 85 return nil 86 } 87 88 // loadClientID loads the client ID from a file and stores the file modification time 89 func loadClientID() error { 90 mutex.Lock() 91 defer mutex.Unlock() 92 93 value, modTime, err := loadConfigValue(clientIDFilename) 94 if err != nil { 95 return err 96 } 97 98 clientID = value 99 clientIDFileModTime = *modTime 100 return nil 101 } 102 103 // loadConfigValue loads a configuration value from a file and stores the file modification time 104 func loadConfigValue(filename string) (string, *time.Time, error) { 105 bytes, err := os.ReadFile(filename) 106 if err != nil { 107 return "", nil, err 108 } 109 110 fileInfo, err := os.Stat(filename) 111 if err != nil { 112 return "", nil, err 113 } 114 115 modTime := fileInfo.ModTime() 116 return string(bytes), &modTime, nil 117 } 118 119 // InitConfiguration loads the configuration from files and starts a goroutine to watch for configuration changes and reloads 120 // config values when changes are detected 121 func InitConfiguration(log *zap.SugaredLogger) error { 122 if envVal := os.Getenv("OVERRIDE_SVC_URL"); envVal != "" { 123 serviceURL = envVal 124 } 125 if envVal := os.Getenv("OVERRIDE_EXTERNAL_URL"); envVal != "" { 126 externalURL = envVal 127 } 128 if externalURL != "" && serviceURL != "" { 129 return nil 130 } 131 if err := loadServiceURL(); err != nil { 132 log.Errorf("Failed to load Service URL: %v", err) 133 return err 134 } 135 if err := loadExternalURL(); err != nil { 136 log.Errorf("Failed to load External URL: %v", err) 137 return err 138 } 139 if err := loadClientID(); err != nil { 140 log.Errorf("Failed to load Client ID: %v", err) 141 return err 142 } 143 144 keepWatching.Store(true) 145 go watchConfigForChanges(log) 146 return nil 147 } 148 149 // watchConfigForChanges watches the configuration files for changes and reloads as necessary. This function generally 150 // runs forever but the keepWatching atomic bool can be set to false in unit tests to stop the loop. 151 func watchConfigForChanges(log *zap.SugaredLogger) { 152 for keepWatching.Load() { 153 if err := reloadConfigWhenChanged(log); err != nil { 154 log.Warnf("Error reloading configuration: %v", err) 155 } 156 time.Sleep(watchInterval) 157 } 158 } 159 160 // reloadConfigWhenChanged compares the config file modification times and reloads config values 161 func reloadConfigWhenChanged(log *zap.SugaredLogger) error { 162 fileInfo, err := os.Stat(serviceURLFilename) 163 if err != nil { 164 return err 165 } 166 if fileInfo.ModTime().After(serviceURLFileModTime) { 167 // file has changed 168 log.Debugf("Detected change in file %s, reloading contents", serviceURLFilename) 169 if err := loadServiceURL(); err != nil { 170 return err 171 } 172 } 173 174 fileInfo, err = os.Stat(externalURLFilename) 175 if err != nil { 176 return err 177 } 178 if fileInfo.ModTime().After(externalURLFileModTime) { 179 // file has changed 180 log.Debugf("Detected change in file %s, reloading contents", externalURLFilename) 181 if err := loadExternalURL(); err != nil { 182 return err 183 } 184 } 185 186 fileInfo, err = os.Stat(clientIDFilename) 187 if err != nil { 188 return err 189 } 190 if fileInfo.ModTime().After(clientIDFileModTime) { 191 // file has changed 192 log.Debugf("Detected change in file %s, reloading contents", clientIDFilename) 193 if err := loadClientID(); err != nil { 194 return err 195 } 196 } 197 198 return nil 199 }