grafana.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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. Role: gen.Spec.ServiceAccount.Role,
  145. },
  146. }, nil)
  147. if err != nil {
  148. return nil, err
  149. }
  150. return &genv1alpha1.GrafanaServiceAccountTokenState{
  151. ServiceAccount: genv1alpha1.GrafanaStateServiceAccount{
  152. ServiceAccountID: ptr.To(res.Payload.ID),
  153. ServiceAccountLogin: &res.Payload.Login,
  154. },
  155. }, nil
  156. }
  157. func tokenResponse(state *genv1alpha1.GrafanaServiceAccountTokenState, token string) (map[string][]byte, genv1alpha1.GeneratorProviderState, error) {
  158. newStateJSON, err := json.Marshal(state)
  159. if err != nil {
  160. return nil, nil, err
  161. }
  162. return map[string][]byte{
  163. "login": []byte(*state.ServiceAccount.ServiceAccountLogin),
  164. "token": []byte(token),
  165. }, &apiextensions.JSON{Raw: newStateJSON}, nil
  166. }
  167. func parseSpec(data []byte) (*genv1alpha1.Grafana, error) {
  168. var spec genv1alpha1.Grafana
  169. err := json.Unmarshal(data, &spec)
  170. return &spec, err
  171. }
  172. func parseStatus(data []byte) (*genv1alpha1.GrafanaServiceAccountTokenState, error) {
  173. var state genv1alpha1.GrafanaServiceAccountTokenState
  174. err := json.Unmarshal(data, &state)
  175. if err != nil {
  176. return nil, err
  177. }
  178. return &state, err
  179. }
  180. func init() {
  181. genv1alpha1.Register(genv1alpha1.GrafanaKind, &Grafana{})
  182. }