grafana.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package grafana
  13. import (
  14. "context"
  15. "encoding/json"
  16. "fmt"
  17. "net/url"
  18. "strings"
  19. "github.com/google/uuid"
  20. grafanaclient "github.com/grafana/grafana-openapi-client-go/client"
  21. grafanasa "github.com/grafana/grafana-openapi-client-go/client/service_accounts"
  22. "github.com/grafana/grafana-openapi-client-go/models"
  23. apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  24. "k8s.io/utils/ptr"
  25. "sigs.k8s.io/controller-runtime/pkg/client"
  26. genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
  27. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  28. "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
  29. )
  30. type Grafana struct{}
  31. func (w *Grafana) Generate(ctx context.Context, jsonSpec *apiextensions.JSON, kclient client.Client, ns string) (map[string][]byte, genv1alpha1.GeneratorProviderState, error) {
  32. gen, err := parseSpec(jsonSpec.Raw)
  33. if err != nil {
  34. return nil, nil, err
  35. }
  36. cl, err := newClient(ctx, gen, kclient, ns)
  37. if err != nil {
  38. return nil, nil, err
  39. }
  40. state, err := createOrGetServiceAccount(cl, gen)
  41. if err != nil {
  42. return nil, nil, err
  43. }
  44. // create new token
  45. res, err := cl.ServiceAccounts.CreateToken(&grafanasa.CreateTokenParams{
  46. ServiceAccountID: *state.ServiceAccount.ServiceAccountID,
  47. Body: &models.AddServiceAccountTokenCommand{
  48. Name: uuid.New().String(),
  49. },
  50. }, nil)
  51. if err != nil {
  52. return nil, nil, err
  53. }
  54. state.ServiceAccount.ServiceAccountTokenID = ptr.To(res.Payload.ID)
  55. return tokenResponse(state, res.Payload.Key)
  56. }
  57. func (w *Grafana) Cleanup(ctx context.Context, jsonSpec *apiextensions.JSON, previousStatus genv1alpha1.GeneratorProviderState, kclient client.Client, ns string) error {
  58. if previousStatus == nil {
  59. return fmt.Errorf("missing previous status")
  60. }
  61. status, err := parseStatus(previousStatus.Raw)
  62. if err != nil {
  63. return err
  64. }
  65. gen, err := parseSpec(jsonSpec.Raw)
  66. if err != nil {
  67. return err
  68. }
  69. cl, err := newClient(ctx, gen, kclient, ns)
  70. if err != nil {
  71. return err
  72. }
  73. _, err = cl.ServiceAccounts.DeleteToken(*status.ServiceAccount.ServiceAccountTokenID, *status.ServiceAccount.ServiceAccountID)
  74. if err != nil && !strings.Contains(err.Error(), "service account token not found") {
  75. return err
  76. }
  77. return nil
  78. }
  79. func newClient(ctx context.Context, gen *genv1alpha1.Grafana, kclient client.Client, ns string) (*grafanaclient.GrafanaHTTPAPI, error) {
  80. parsedURL, err := url.Parse(gen.Spec.URL)
  81. if err != nil {
  82. return nil, err
  83. }
  84. cfg := &grafanaclient.TransportConfig{
  85. Host: parsedURL.Host,
  86. BasePath: parsedURL.JoinPath("/api").Path,
  87. Schemes: []string{parsedURL.Scheme},
  88. }
  89. if err := setGrafanaClientCredentials(ctx, gen, kclient, ns, cfg); err != nil {
  90. return nil, err
  91. }
  92. return grafanaclient.NewHTTPClientWithConfig(nil, cfg), nil
  93. }
  94. func setGrafanaClientCredentials(ctx context.Context, gen *genv1alpha1.Grafana, kclient client.Client, ns string, cfg *grafanaclient.TransportConfig) error {
  95. // First try to use service account auth
  96. if gen.Spec.Auth.Token != nil {
  97. serviceAccountAPIKey, err := resolvers.SecretKeyRef(ctx, kclient, resolvers.EmptyStoreKind, ns, &esmeta.SecretKeySelector{
  98. Namespace: &ns,
  99. Name: gen.Spec.Auth.Token.Name,
  100. Key: gen.Spec.Auth.Token.Key,
  101. })
  102. if err != nil {
  103. return err
  104. }
  105. cfg.APIKey = serviceAccountAPIKey
  106. return nil
  107. }
  108. // Next try to use basic auth
  109. if gen.Spec.Auth.Basic != nil {
  110. basicAuthPassword, err := resolvers.SecretKeyRef(ctx, kclient, resolvers.EmptyStoreKind, ns, &esmeta.SecretKeySelector{
  111. Namespace: &ns,
  112. Name: gen.Spec.Auth.Basic.Password.Name,
  113. Key: gen.Spec.Auth.Basic.Password.Key,
  114. })
  115. if err != nil {
  116. return err
  117. }
  118. cfg.BasicAuth = url.UserPassword(gen.Spec.Auth.Basic.Username, basicAuthPassword)
  119. return nil
  120. }
  121. // No auth found, fail
  122. return fmt.Errorf("no auth configuration found")
  123. }
  124. func createOrGetServiceAccount(cl *grafanaclient.GrafanaHTTPAPI, gen *genv1alpha1.Grafana) (*genv1alpha1.GrafanaServiceAccountTokenState, error) {
  125. saList, err := cl.ServiceAccounts.SearchOrgServiceAccountsWithPaging(&grafanasa.SearchOrgServiceAccountsWithPagingParams{
  126. Query: ptr.To(gen.Spec.ServiceAccount.Name),
  127. })
  128. if err != nil {
  129. return nil, err
  130. }
  131. for _, sa := range saList.Payload.ServiceAccounts {
  132. if sa.Name == gen.Spec.ServiceAccount.Name {
  133. return &genv1alpha1.GrafanaServiceAccountTokenState{
  134. ServiceAccount: genv1alpha1.GrafanaStateServiceAccount{
  135. ServiceAccountID: &sa.ID,
  136. ServiceAccountLogin: &sa.Login,
  137. },
  138. }, nil
  139. }
  140. }
  141. res, err := cl.ServiceAccounts.CreateServiceAccount(&grafanasa.CreateServiceAccountParams{
  142. Body: &models.CreateServiceAccountForm{
  143. Name: gen.Spec.ServiceAccount.Name,
  144. },
  145. }, nil)
  146. if err != nil {
  147. return nil, err
  148. }
  149. return &genv1alpha1.GrafanaServiceAccountTokenState{
  150. ServiceAccount: genv1alpha1.GrafanaStateServiceAccount{
  151. ServiceAccountID: ptr.To(res.Payload.ID),
  152. ServiceAccountLogin: &res.Payload.Login,
  153. },
  154. }, nil
  155. }
  156. func tokenResponse(state *genv1alpha1.GrafanaServiceAccountTokenState, token string) (map[string][]byte, genv1alpha1.GeneratorProviderState, error) {
  157. newStateJSON, err := json.Marshal(state)
  158. if err != nil {
  159. return nil, nil, err
  160. }
  161. return map[string][]byte{
  162. "login": []byte(*state.ServiceAccount.ServiceAccountLogin),
  163. "token": []byte(token),
  164. }, &apiextensions.JSON{Raw: newStateJSON}, nil
  165. }
  166. func parseSpec(data []byte) (*genv1alpha1.Grafana, error) {
  167. var spec genv1alpha1.Grafana
  168. err := json.Unmarshal(data, &spec)
  169. return &spec, err
  170. }
  171. func parseStatus(data []byte) (*genv1alpha1.GrafanaServiceAccountTokenState, error) {
  172. var state genv1alpha1.GrafanaServiceAccountTokenState
  173. err := json.Unmarshal(data, &state)
  174. if err != nil {
  175. return nil, err
  176. }
  177. return &state, err
  178. }
  179. func init() {
  180. genv1alpha1.Register(genv1alpha1.GrafanaKind, &Grafana{})
  181. }