/
gonfig.go
161 lines (138 loc) · 4.83 KB
/
gonfig.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package gonfig
import (
"crypto/tls"
"fmt"
"github.com/cloudfoundry-community/go-cfenv"
"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
"net/http"
"os"
"reflect"
"time"
)
// URL represents the particles for building the URL to access the configuration
// from the Pivotal Cloud Foundry configuration server.
type URL struct {
URI string
App string
Profile string
Label string
}
// Credentials are used in order to access the PCF Config Server with oauth2.
type Credentials struct {
AccessTokenURI string
ClientID string
ClientSecret string
URL URL
}
// Configuration from config server
type Config map[string]interface{}
// GetConfigurationFromServer requests the current configuration from the Configuration Server
// using the given Credentials.
func (c *Credentials) GetConfigurationFromServer() (map[string]interface{}, error) {
var client *http.Client
var url string
if os.Getenv("gonfig_testing") == "1" {
client = &http.Client{Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}}
url = c.URL.URI
} else {
conf := &clientcredentials.Config{
ClientID: c.ClientID,
ClientSecret: c.ClientSecret,
TokenURL: c.AccessTokenURI,
}
client = oauth2.NewClient(oauth2.NoContext, conf.TokenSource(oauth2.NoContext))
url = fmt.Sprintf("%s/%s/%s/%s", c.URL.URI, c.URL.App, c.URL.Profile, c.URL.Label)
}
return makeRequest(client, url)
}
// GetServiceCredentialsFromEnv returns oauth2 and other parameters (app name,
// space name) out of the PCF environment variables using the given label.
func GetServiceCredentialsFromEnv(service string) (*Credentials, error) {
env, err := cfenv.Current()
if err != nil {
return nil, fmt.Errorf("Error during getting CF configuration out of environment: %s", err.Error())
}
services, err := env.Services.WithLabel(service)
if err != nil {
return nil, err
}
var credentials Credentials
credentials.AccessTokenURI, _ = services[0].CredentialString("access_token_uri")
if credentials.AccessTokenURI == "" {
return nil, fmt.Errorf("access_token_uri not found in credentials")
}
credentials.ClientID, _ = services[0].CredentialString("client_id")
if credentials.ClientID == "" {
return nil, fmt.Errorf("client_id not found in credentials")
}
credentials.ClientSecret, _ = services[0].CredentialString("client_secret")
if credentials.ClientSecret == "" {
return nil, fmt.Errorf("client_secret not found in credentials")
}
uri, _ := services[0].CredentialString("uri")
if uri == "" {
return nil, fmt.Errorf("uri not found in credentials")
}
credentials.URL = URL{
URI: uri,
App: env.Name,
Profile: env.SpaceName,
Label: "master",
}
return &credentials, nil
}
// GetConfigServerCredentialsFromEnv returns oauth2 and other parameters (app name,
// space name) out of the PCF environment variables. They are required for accessing
// the PCF Configuration Server.
func GetConfigServerCredentialsFromEnv() (*Credentials, error) {
return GetServiceCredentialsFromEnv("p-config-server")
}
// FetchConfig returns the configuration given by the PCF Config Server which is bound
// as service to the app.
func FetchConfig() (Config, error) {
return FetchConfigByLabel("master")
}
// FetchConfigByLabel returns the configuration from the PCF Config Server for a specifc
// label. The default label is "master" which is used by FetchConfig(). The label represents
// for a git configuration typically a branch name.
func FetchConfigByLabel(label string) (Config, error) {
credentials, err := GetConfigServerCredentialsFromEnv()
if err != nil {
return nil, err
}
credentials.URL.Label = label
return credentials.GetConfigurationFromServer()
}
// ConfigChange checks periodically (checkInterval) if the configuration of the application
// changed. As soon as there is a difference a new Config object is send into the created
// Config output channel.
//
// Note that it is not guaranteed to have all updates to the config in the output channel.
// If for example multiple changes of the configuration occur within one checkInterval then
// only the latest one will be send out.
func ConfigChange(checkInterval time.Duration) (<-chan Config, error) {
return ConfigChangeByLabel(checkInterval, "master")
}
// ConfigChangeByLabel is the same as ConfigChance with the difference that a label of
// the configuration can be given (otherwise it would be the master branch configuration).
func ConfigChangeByLabel(checkInterval time.Duration, label string) (<-chan Config, error) {
var lastConfig Config
ticker := time.NewTicker(checkInterval).C
out := make(chan Config)
go func() {
for range ticker {
newConfig, err := FetchConfigByLabel(label)
if err != nil {
break
}
if reflect.DeepEqual(lastConfig, newConfig) == false {
lastConfig = newConfig
out <- newConfig
}
}
}()
return out, nil
}