GitOps for k8s
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

images.go 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package kubernetes
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/go-kit/kit/log"
  6. "github.com/pkg/errors"
  7. "github.com/ryanuber/go-glob"
  8. apiv1 "k8s.io/api/core/v1"
  9. apierrors "k8s.io/apimachinery/pkg/api/errors"
  10. meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  11. "github.com/fluxcd/flux/image"
  12. "github.com/fluxcd/flux/registry"
  13. "github.com/fluxcd/flux/resource"
  14. )
  15. func mergeCredentials(log func(...interface{}) error,
  16. includeImage func(imageName string) bool,
  17. client ExtendedClient,
  18. namespace string, podTemplate apiv1.PodTemplateSpec,
  19. imageCreds registry.ImageCreds,
  20. seenCreds map[string]registry.Credentials) {
  21. var images []image.Name
  22. for _, container := range podTemplate.Spec.InitContainers {
  23. r, err := image.ParseRef(container.Image)
  24. if err != nil {
  25. log("err", err.Error())
  26. continue
  27. }
  28. if includeImage(r.CanonicalName().Name.String()) {
  29. images = append(images, r.Name)
  30. }
  31. }
  32. for _, container := range podTemplate.Spec.Containers {
  33. r, err := image.ParseRef(container.Image)
  34. if err != nil {
  35. log("err", err.Error())
  36. continue
  37. }
  38. if includeImage(r.CanonicalName().Name.String()) {
  39. images = append(images, r.Name)
  40. }
  41. }
  42. if len(images) < 1 {
  43. return
  44. }
  45. creds := registry.NoCredentials()
  46. var imagePullSecrets []string
  47. saName := podTemplate.Spec.ServiceAccountName
  48. if saName == "" {
  49. saName = "default"
  50. }
  51. sa, err := client.CoreV1().ServiceAccounts(namespace).Get(saName, meta_v1.GetOptions{})
  52. if err == nil {
  53. for _, ips := range sa.ImagePullSecrets {
  54. imagePullSecrets = append(imagePullSecrets, ips.Name)
  55. }
  56. }
  57. for _, imagePullSecret := range podTemplate.Spec.ImagePullSecrets {
  58. imagePullSecrets = append(imagePullSecrets, imagePullSecret.Name)
  59. }
  60. for _, name := range imagePullSecrets {
  61. if seen, ok := seenCreds[name]; ok {
  62. creds.Merge(seen)
  63. continue
  64. }
  65. secret, err := client.CoreV1().Secrets(namespace).Get(name, meta_v1.GetOptions{})
  66. if err != nil {
  67. log("err", errors.Wrapf(err, "getting secret %q from namespace %q", name, namespace))
  68. seenCreds[name] = registry.NoCredentials()
  69. continue
  70. }
  71. var decoded []byte
  72. var ok bool
  73. // These differ in format; but, ParseCredentials will
  74. // handle either.
  75. switch apiv1.SecretType(secret.Type) {
  76. case apiv1.SecretTypeDockercfg:
  77. decoded, ok = secret.Data[apiv1.DockerConfigKey]
  78. case apiv1.SecretTypeDockerConfigJson:
  79. decoded, ok = secret.Data[apiv1.DockerConfigJsonKey]
  80. default:
  81. log("skip", "unknown type", "secret", namespace+"/"+secret.Name, "type", secret.Type)
  82. seenCreds[name] = registry.NoCredentials()
  83. continue
  84. }
  85. if !ok {
  86. log("err", errors.Wrapf(err, "retrieving pod secret %q", secret.Name))
  87. seenCreds[name] = registry.NoCredentials()
  88. continue
  89. }
  90. // Parse secret
  91. crd, err := registry.ParseCredentials(fmt.Sprintf("%s:secret/%s", namespace, name), decoded)
  92. if err != nil {
  93. log("err", err.Error())
  94. seenCreds[name] = registry.NoCredentials()
  95. continue
  96. }
  97. seenCreds[name] = crd
  98. // Merge into the credentials for this PodSpec
  99. creds.Merge(crd)
  100. }
  101. // Now create the service and attach the credentials
  102. for _, image := range images {
  103. imageCreds[image] = creds
  104. }
  105. }
  106. // ImagesToFetch is a k8s specific method to get a list of images to update along with their credentials
  107. func (c *Cluster) ImagesToFetch() registry.ImageCreds {
  108. allImageCreds := make(registry.ImageCreds)
  109. ctx := context.Background()
  110. namespaces, err := c.getAllowedAndExistingNamespaces(ctx)
  111. if err != nil {
  112. c.logger.Log("err", errors.Wrap(err, "getting namespaces"))
  113. return allImageCreds
  114. }
  115. for _, ns := range namespaces {
  116. seenCreds := make(map[string]registry.Credentials)
  117. for kind, resourceKind := range resourceKinds {
  118. workloads, err := resourceKind.getWorkloads(ctx, c, ns.Name)
  119. if err != nil {
  120. if apierrors.IsNotFound(err) || apierrors.IsForbidden(err) {
  121. // Skip unsupported or forbidden resource kinds
  122. continue
  123. }
  124. c.logger.Log("err", errors.Wrapf(err, "getting kind %s for namespace %s", kind, ns.Name))
  125. }
  126. imageCreds := make(registry.ImageCreds)
  127. for _, workload := range workloads {
  128. logger := log.With(c.logger, "resource", resource.MakeID(ns.Name, kind, workload.GetName()))
  129. mergeCredentials(logger.Log, c.includeImage, c.client, ns.Name, workload.podTemplate, imageCreds, seenCreds)
  130. }
  131. // Merge creds
  132. for imageID, creds := range imageCreds {
  133. existingCreds, ok := allImageCreds[imageID]
  134. if ok {
  135. mergedCreds := registry.NoCredentials()
  136. mergedCreds.Merge(existingCreds)
  137. mergedCreds.Merge(creds)
  138. allImageCreds[imageID] = mergedCreds
  139. } else {
  140. allImageCreds[imageID] = creds
  141. }
  142. }
  143. }
  144. }
  145. return allImageCreds
  146. }
  147. func (c *Cluster) includeImage(imageName string) bool {
  148. for _, exp := range c.imageExcludeList {
  149. if glob.Glob(exp, imageName) {
  150. return false
  151. }
  152. }
  153. return true
  154. }