agent.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. package kubernetes
  2. import (
  3. "bufio"
  4. "bytes"
  5. "context"
  6. "fmt"
  7. "io"
  8. "strings"
  9. "github.com/porter-dev/porter/internal/kubernetes/provisioner"
  10. "github.com/porter-dev/porter/internal/kubernetes/provisioner/aws"
  11. "github.com/porter-dev/porter/internal/kubernetes/provisioner/aws/ecr"
  12. "github.com/porter-dev/porter/internal/kubernetes/provisioner/aws/eks"
  13. "github.com/porter-dev/porter/internal/kubernetes/provisioner/do"
  14. "github.com/porter-dev/porter/internal/kubernetes/provisioner/do/docr"
  15. "github.com/porter-dev/porter/internal/kubernetes/provisioner/do/doks"
  16. "github.com/porter-dev/porter/internal/kubernetes/provisioner/gcp"
  17. "github.com/porter-dev/porter/internal/kubernetes/provisioner/gcp/gke"
  18. "github.com/porter-dev/porter/internal/models"
  19. "github.com/porter-dev/porter/internal/models/integrations"
  20. "github.com/porter-dev/porter/internal/oauth"
  21. "github.com/porter-dev/porter/internal/registry"
  22. "github.com/porter-dev/porter/internal/repository"
  23. "golang.org/x/oauth2"
  24. "github.com/gorilla/websocket"
  25. "github.com/porter-dev/porter/internal/helm/grapher"
  26. appsv1 "k8s.io/api/apps/v1"
  27. batchv1 "k8s.io/api/batch/v1"
  28. batchv1beta1 "k8s.io/api/batch/v1beta1"
  29. v1 "k8s.io/api/core/v1"
  30. v1beta1 "k8s.io/api/extensions/v1beta1"
  31. "k8s.io/apimachinery/pkg/api/errors"
  32. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  33. "k8s.io/cli-runtime/pkg/genericclioptions"
  34. "k8s.io/client-go/informers"
  35. "k8s.io/client-go/kubernetes"
  36. "k8s.io/client-go/tools/cache"
  37. "github.com/porter-dev/porter/internal/config"
  38. )
  39. // Agent is a Kubernetes agent for performing operations that interact with the
  40. // api server
  41. type Agent struct {
  42. RESTClientGetter genericclioptions.RESTClientGetter
  43. Clientset kubernetes.Interface
  44. }
  45. type Message struct {
  46. EventType string `json:"event_type"`
  47. Object interface{}
  48. Kind string
  49. }
  50. type ListOptions struct {
  51. FieldSelector string
  52. }
  53. // ListNamespaces simply lists namespaces
  54. func (a *Agent) ListNamespaces() (*v1.NamespaceList, error) {
  55. return a.Clientset.CoreV1().Namespaces().List(
  56. context.TODO(),
  57. metav1.ListOptions{},
  58. )
  59. }
  60. // ListJobsByLabel lists jobs in a namespace matching a label
  61. type Label struct {
  62. Key string
  63. Val string
  64. }
  65. func (a *Agent) ListJobsByLabel(namespace string, labels ...Label) ([]batchv1.Job, error) {
  66. selectors := make([]string, 0)
  67. for _, label := range labels {
  68. selectors = append(selectors, fmt.Sprintf("%s=%s", label.Key, label.Val))
  69. }
  70. resp, err := a.Clientset.BatchV1().Jobs(namespace).List(
  71. context.TODO(),
  72. metav1.ListOptions{
  73. LabelSelector: strings.Join(selectors, ","),
  74. },
  75. )
  76. if err != nil {
  77. return nil, err
  78. }
  79. return resp.Items, nil
  80. }
  81. // GetJobPods lists all pods belonging to a job in a namespace
  82. func (a *Agent) GetJobPods(namespace, jobName string) ([]v1.Pod, error) {
  83. resp, err := a.Clientset.CoreV1().Pods(namespace).List(
  84. context.TODO(),
  85. metav1.ListOptions{
  86. LabelSelector: fmt.Sprintf("%s=%s", "job-name", jobName),
  87. },
  88. )
  89. if err != nil {
  90. return nil, err
  91. }
  92. return resp.Items, nil
  93. }
  94. // GetIngress gets ingress given the name and namespace
  95. func (a *Agent) GetIngress(namespace string, name string) (*v1beta1.Ingress, error) {
  96. return a.Clientset.ExtensionsV1beta1().Ingresses(namespace).Get(
  97. context.TODO(),
  98. name,
  99. metav1.GetOptions{},
  100. )
  101. }
  102. // GetDeployment gets the deployment given the name and namespace
  103. func (a *Agent) GetDeployment(c grapher.Object) (*appsv1.Deployment, error) {
  104. return a.Clientset.AppsV1().Deployments(c.Namespace).Get(
  105. context.TODO(),
  106. c.Name,
  107. metav1.GetOptions{},
  108. )
  109. }
  110. // GetStatefulSet gets the statefulset given the name and namespace
  111. func (a *Agent) GetStatefulSet(c grapher.Object) (*appsv1.StatefulSet, error) {
  112. return a.Clientset.AppsV1().StatefulSets(c.Namespace).Get(
  113. context.TODO(),
  114. c.Name,
  115. metav1.GetOptions{},
  116. )
  117. }
  118. // GetReplicaSet gets the replicaset given the name and namespace
  119. func (a *Agent) GetReplicaSet(c grapher.Object) (*appsv1.ReplicaSet, error) {
  120. return a.Clientset.AppsV1().ReplicaSets(c.Namespace).Get(
  121. context.TODO(),
  122. c.Name,
  123. metav1.GetOptions{},
  124. )
  125. }
  126. // GetDaemonSet gets the daemonset by name and namespace
  127. func (a *Agent) GetDaemonSet(c grapher.Object) (*appsv1.DaemonSet, error) {
  128. return a.Clientset.AppsV1().DaemonSets(c.Namespace).Get(
  129. context.TODO(),
  130. c.Name,
  131. metav1.GetOptions{},
  132. )
  133. }
  134. // GetJob gets the job by name and namespace
  135. func (a *Agent) GetJob(c grapher.Object) (*batchv1.Job, error) {
  136. return a.Clientset.BatchV1().Jobs(c.Namespace).Get(
  137. context.TODO(),
  138. c.Name,
  139. metav1.GetOptions{},
  140. )
  141. }
  142. // GetCronJob gets the CronJob by name and namespace
  143. func (a *Agent) GetCronJob(c grapher.Object) (*batchv1beta1.CronJob, error) {
  144. return a.Clientset.BatchV1beta1().CronJobs(c.Namespace).Get(
  145. context.TODO(),
  146. c.Name,
  147. metav1.GetOptions{},
  148. )
  149. }
  150. // GetPodsByLabel retrieves pods with matching labels
  151. func (a *Agent) GetPodsByLabel(selector string) (*v1.PodList, error) {
  152. // Search in all namespaces for matching pods
  153. return a.Clientset.CoreV1().Pods("").List(
  154. context.TODO(),
  155. metav1.ListOptions{
  156. LabelSelector: selector,
  157. },
  158. )
  159. }
  160. // DeletePod deletes a pod by name and namespace
  161. func (a *Agent) DeletePod(namespace string, name string) error {
  162. return a.Clientset.CoreV1().Pods(namespace).Delete(
  163. context.TODO(),
  164. name,
  165. metav1.DeleteOptions{},
  166. )
  167. }
  168. // GetPodLogs streams real-time logs from a given pod.
  169. func (a *Agent) GetPodLogs(namespace string, name string, conn *websocket.Conn) error {
  170. tails := int64(400)
  171. // follow logs
  172. podLogOpts := v1.PodLogOptions{
  173. Follow: true,
  174. TailLines: &tails,
  175. }
  176. req := a.Clientset.CoreV1().Pods(namespace).GetLogs(name, &podLogOpts)
  177. podLogs, err := req.Stream(context.TODO())
  178. if err != nil {
  179. return fmt.Errorf("Cannot open log stream for pod %s", name)
  180. }
  181. defer podLogs.Close()
  182. r := bufio.NewReader(podLogs)
  183. errorchan := make(chan error)
  184. go func() {
  185. // listens for websocket closing handshake
  186. for {
  187. if _, _, err := conn.ReadMessage(); err != nil {
  188. defer conn.Close()
  189. errorchan <- nil
  190. return
  191. }
  192. }
  193. }()
  194. go func() {
  195. for {
  196. select {
  197. case <-errorchan:
  198. defer close(errorchan)
  199. return
  200. default:
  201. }
  202. bytes, err := r.ReadBytes('\n')
  203. if writeErr := conn.WriteMessage(websocket.TextMessage, bytes); writeErr != nil {
  204. errorchan <- writeErr
  205. return
  206. }
  207. if err != nil {
  208. if err != io.EOF {
  209. errorchan <- err
  210. return
  211. }
  212. errorchan <- nil
  213. return
  214. }
  215. }
  216. }()
  217. for {
  218. select {
  219. case err = <-errorchan:
  220. return err
  221. }
  222. }
  223. }
  224. // StreamControllerStatus streams controller status. Supports Deployment, StatefulSet, ReplicaSet, and DaemonSet
  225. // TODO: Support Jobs
  226. func (a *Agent) StreamControllerStatus(conn *websocket.Conn, kind string) error {
  227. factory := informers.NewSharedInformerFactory(
  228. a.Clientset,
  229. 0,
  230. )
  231. var informer cache.SharedInformer
  232. // Spins up an informer depending on kind. Convert to lowercase for robustness
  233. switch strings.ToLower(kind) {
  234. case "deployment":
  235. informer = factory.Apps().V1().Deployments().Informer()
  236. case "statefulset":
  237. informer = factory.Apps().V1().StatefulSets().Informer()
  238. case "replicaset":
  239. informer = factory.Apps().V1().ReplicaSets().Informer()
  240. case "daemonset":
  241. informer = factory.Apps().V1().DaemonSets().Informer()
  242. case "job":
  243. informer = factory.Batch().V1().Jobs().Informer()
  244. }
  245. stopper := make(chan struct{})
  246. errorchan := make(chan error)
  247. defer close(errorchan)
  248. informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
  249. UpdateFunc: func(oldObj, newObj interface{}) {
  250. msg := Message{
  251. EventType: "UPDATE",
  252. Object: newObj,
  253. Kind: strings.ToLower(kind),
  254. }
  255. if writeErr := conn.WriteJSON(msg); writeErr != nil {
  256. errorchan <- writeErr
  257. return
  258. }
  259. },
  260. AddFunc: func(obj interface{}) {
  261. msg := Message{
  262. EventType: "ADD",
  263. Object: obj,
  264. Kind: strings.ToLower(kind),
  265. }
  266. if writeErr := conn.WriteJSON(msg); writeErr != nil {
  267. errorchan <- writeErr
  268. return
  269. }
  270. },
  271. DeleteFunc: func(obj interface{}) {
  272. msg := Message{
  273. EventType: "DELETE",
  274. Object: obj,
  275. Kind: strings.ToLower(kind),
  276. }
  277. if writeErr := conn.WriteJSON(msg); writeErr != nil {
  278. errorchan <- writeErr
  279. return
  280. }
  281. },
  282. })
  283. go func() {
  284. // listens for websocket closing handshake
  285. for {
  286. if _, _, err := conn.ReadMessage(); err != nil {
  287. defer conn.Close()
  288. defer close(stopper)
  289. errorchan <- nil
  290. return
  291. }
  292. }
  293. }()
  294. go informer.Run(stopper)
  295. for {
  296. select {
  297. case err := <-errorchan:
  298. return err
  299. }
  300. }
  301. }
  302. // ProvisionECR spawns a new provisioning pod that creates an ECR instance
  303. func (a *Agent) ProvisionECR(
  304. projectID uint,
  305. awsConf *integrations.AWSIntegration,
  306. ecrName string,
  307. repo repository.Repository,
  308. infra *models.Infra,
  309. operation provisioner.ProvisionerOperation,
  310. pgConf *config.DBConf,
  311. redisConf *config.RedisConf,
  312. provImageTag string,
  313. ) (*batchv1.Job, error) {
  314. id := infra.GetUniqueName()
  315. prov := &provisioner.Conf{
  316. ID: id,
  317. Name: fmt.Sprintf("prov-%s-%s", id, string(operation)),
  318. Kind: provisioner.ECR,
  319. Operation: operation,
  320. Redis: redisConf,
  321. Postgres: pgConf,
  322. ProvisionerImageTag: provImageTag,
  323. LastApplied: infra.LastApplied,
  324. AWS: &aws.Conf{
  325. AWSRegion: awsConf.AWSRegion,
  326. AWSAccessKeyID: string(awsConf.AWSAccessKeyID),
  327. AWSSecretAccessKey: string(awsConf.AWSSecretAccessKey),
  328. },
  329. ECR: &ecr.Conf{
  330. ECRName: ecrName,
  331. },
  332. }
  333. return a.provision(prov, infra, repo)
  334. }
  335. // ProvisionEKS spawns a new provisioning pod that creates an EKS instance
  336. func (a *Agent) ProvisionEKS(
  337. projectID uint,
  338. awsConf *integrations.AWSIntegration,
  339. eksName, machineType string,
  340. repo repository.Repository,
  341. infra *models.Infra,
  342. operation provisioner.ProvisionerOperation,
  343. pgConf *config.DBConf,
  344. redisConf *config.RedisConf,
  345. provImageTag string,
  346. ) (*batchv1.Job, error) {
  347. id := infra.GetUniqueName()
  348. prov := &provisioner.Conf{
  349. ID: id,
  350. Name: fmt.Sprintf("prov-%s-%s", id, string(operation)),
  351. Kind: provisioner.EKS,
  352. Operation: operation,
  353. Redis: redisConf,
  354. Postgres: pgConf,
  355. ProvisionerImageTag: provImageTag,
  356. LastApplied: infra.LastApplied,
  357. AWS: &aws.Conf{
  358. AWSRegion: awsConf.AWSRegion,
  359. AWSAccessKeyID: string(awsConf.AWSAccessKeyID),
  360. AWSSecretAccessKey: string(awsConf.AWSSecretAccessKey),
  361. },
  362. EKS: &eks.Conf{
  363. ClusterName: eksName,
  364. MachineType: machineType,
  365. },
  366. }
  367. return a.provision(prov, infra, repo)
  368. }
  369. // ProvisionGCR spawns a new provisioning pod that creates a GCR instance
  370. func (a *Agent) ProvisionGCR(
  371. projectID uint,
  372. gcpConf *integrations.GCPIntegration,
  373. repo repository.Repository,
  374. infra *models.Infra,
  375. operation provisioner.ProvisionerOperation,
  376. pgConf *config.DBConf,
  377. redisConf *config.RedisConf,
  378. provImageTag string,
  379. ) (*batchv1.Job, error) {
  380. id := infra.GetUniqueName()
  381. prov := &provisioner.Conf{
  382. ID: id,
  383. Name: fmt.Sprintf("prov-%s-%s", id, string(operation)),
  384. Kind: provisioner.GCR,
  385. Operation: operation,
  386. Redis: redisConf,
  387. Postgres: pgConf,
  388. ProvisionerImageTag: provImageTag,
  389. LastApplied: infra.LastApplied,
  390. GCP: &gcp.Conf{
  391. GCPRegion: gcpConf.GCPRegion,
  392. GCPProjectID: gcpConf.GCPProjectID,
  393. GCPKeyData: string(gcpConf.GCPKeyData),
  394. },
  395. }
  396. return a.provision(prov, infra, repo)
  397. }
  398. // ProvisionGKE spawns a new provisioning pod that creates a GKE instance
  399. func (a *Agent) ProvisionGKE(
  400. projectID uint,
  401. gcpConf *integrations.GCPIntegration,
  402. gkeName string,
  403. repo repository.Repository,
  404. infra *models.Infra,
  405. operation provisioner.ProvisionerOperation,
  406. pgConf *config.DBConf,
  407. redisConf *config.RedisConf,
  408. provImageTag string,
  409. ) (*batchv1.Job, error) {
  410. id := infra.GetUniqueName()
  411. prov := &provisioner.Conf{
  412. ID: id,
  413. Name: fmt.Sprintf("prov-%s-%s", id, string(operation)),
  414. Kind: provisioner.GKE,
  415. Operation: operation,
  416. Redis: redisConf,
  417. Postgres: pgConf,
  418. ProvisionerImageTag: provImageTag,
  419. LastApplied: infra.LastApplied,
  420. GCP: &gcp.Conf{
  421. GCPRegion: gcpConf.GCPRegion,
  422. GCPProjectID: gcpConf.GCPProjectID,
  423. GCPKeyData: string(gcpConf.GCPKeyData),
  424. },
  425. GKE: &gke.Conf{
  426. ClusterName: gkeName,
  427. },
  428. }
  429. return a.provision(prov, infra, repo)
  430. }
  431. // ProvisionDOCR spawns a new provisioning pod that creates a DOCR instance
  432. func (a *Agent) ProvisionDOCR(
  433. projectID uint,
  434. doConf *integrations.OAuthIntegration,
  435. doAuth *oauth2.Config,
  436. repo repository.Repository,
  437. docrName, docrSubscriptionTier string,
  438. infra *models.Infra,
  439. operation provisioner.ProvisionerOperation,
  440. pgConf *config.DBConf,
  441. redisConf *config.RedisConf,
  442. provImageTag string,
  443. ) (*batchv1.Job, error) {
  444. // get the token
  445. oauthInt, err := repo.OAuthIntegration.ReadOAuthIntegration(
  446. infra.DOIntegrationID,
  447. )
  448. if err != nil {
  449. return nil, err
  450. }
  451. tok, _, err := oauth.GetAccessToken(oauthInt, doAuth, repo)
  452. if err != nil {
  453. return nil, err
  454. }
  455. id := infra.GetUniqueName()
  456. prov := &provisioner.Conf{
  457. ID: id,
  458. Name: fmt.Sprintf("prov-%s-%s", id, string(operation)),
  459. Kind: provisioner.DOCR,
  460. Operation: operation,
  461. Redis: redisConf,
  462. Postgres: pgConf,
  463. ProvisionerImageTag: provImageTag,
  464. LastApplied: infra.LastApplied,
  465. DO: &do.Conf{
  466. DOToken: tok,
  467. },
  468. DOCR: &docr.Conf{
  469. DOCRName: docrName,
  470. DOCRSubscriptionTier: docrSubscriptionTier,
  471. },
  472. }
  473. return a.provision(prov, infra, repo)
  474. }
  475. // ProvisionDOKS spawns a new provisioning pod that creates a DOKS instance
  476. func (a *Agent) ProvisionDOKS(
  477. projectID uint,
  478. doConf *integrations.OAuthIntegration,
  479. doAuth *oauth2.Config,
  480. repo repository.Repository,
  481. doRegion, doksClusterName string,
  482. infra *models.Infra,
  483. operation provisioner.ProvisionerOperation,
  484. pgConf *config.DBConf,
  485. redisConf *config.RedisConf,
  486. provImageTag string,
  487. ) (*batchv1.Job, error) {
  488. // get the token
  489. oauthInt, err := repo.OAuthIntegration.ReadOAuthIntegration(
  490. infra.DOIntegrationID,
  491. )
  492. if err != nil {
  493. return nil, err
  494. }
  495. tok, _, err := oauth.GetAccessToken(oauthInt, doAuth, repo)
  496. if err != nil {
  497. return nil, err
  498. }
  499. id := infra.GetUniqueName()
  500. prov := &provisioner.Conf{
  501. ID: id,
  502. Name: fmt.Sprintf("prov-%s-%s", id, string(operation)),
  503. Kind: provisioner.DOKS,
  504. Operation: operation,
  505. Redis: redisConf,
  506. Postgres: pgConf,
  507. LastApplied: infra.LastApplied,
  508. ProvisionerImageTag: provImageTag,
  509. DO: &do.Conf{
  510. DOToken: tok,
  511. },
  512. DOKS: &doks.Conf{
  513. DORegion: doRegion,
  514. DOKSClusterName: doksClusterName,
  515. },
  516. }
  517. return a.provision(prov, infra, repo)
  518. }
  519. // ProvisionTest spawns a new provisioning pod that tests provisioning
  520. func (a *Agent) ProvisionTest(
  521. projectID uint,
  522. infra *models.Infra,
  523. repo repository.Repository,
  524. operation provisioner.ProvisionerOperation,
  525. pgConf *config.DBConf,
  526. redisConf *config.RedisConf,
  527. provImageTag string,
  528. ) (*batchv1.Job, error) {
  529. id := infra.GetUniqueName()
  530. prov := &provisioner.Conf{
  531. ID: id,
  532. Name: fmt.Sprintf("prov-%s-%s", id, string(operation)),
  533. Operation: operation,
  534. Kind: provisioner.Test,
  535. Redis: redisConf,
  536. Postgres: pgConf,
  537. ProvisionerImageTag: provImageTag,
  538. }
  539. return a.provision(prov, infra, repo)
  540. }
  541. func (a *Agent) provision(
  542. prov *provisioner.Conf,
  543. infra *models.Infra,
  544. repo repository.Repository,
  545. ) (*batchv1.Job, error) {
  546. prov.Namespace = "default"
  547. job, err := prov.GetProvisionerJobTemplate()
  548. if err != nil {
  549. return nil, err
  550. }
  551. job, err = a.Clientset.BatchV1().Jobs(prov.Namespace).Create(
  552. context.TODO(),
  553. job,
  554. metav1.CreateOptions{},
  555. )
  556. if err != nil {
  557. return nil, err
  558. }
  559. infra.LastApplied = prov.LastApplied
  560. infra, err = repo.Infra.UpdateInfra(infra)
  561. if err != nil {
  562. return nil, err
  563. }
  564. return job, nil
  565. }
  566. // CreateImagePullSecrets will create the required image pull secrets and
  567. // return a map from the registry name to the name of the secret.
  568. func (a *Agent) CreateImagePullSecrets(
  569. repo repository.Repository,
  570. namespace string,
  571. linkedRegs map[string]*models.Registry,
  572. doAuth *oauth2.Config,
  573. ) (map[string]string, error) {
  574. res := make(map[string]string)
  575. for key, val := range linkedRegs {
  576. _reg := registry.Registry(*val)
  577. data, err := _reg.GetDockerConfigJSON(repo, doAuth)
  578. if err != nil {
  579. return nil, err
  580. }
  581. secretName := fmt.Sprintf("porter-%s-%d", val.Externalize().Service, val.ID)
  582. secret, err := a.Clientset.CoreV1().Secrets(namespace).Get(
  583. context.TODO(),
  584. secretName,
  585. metav1.GetOptions{},
  586. )
  587. // if not found, create the secret
  588. if err != nil && errors.IsNotFound(err) {
  589. _, err = a.Clientset.CoreV1().Secrets(namespace).Create(
  590. context.TODO(),
  591. &v1.Secret{
  592. ObjectMeta: metav1.ObjectMeta{
  593. Name: secretName,
  594. },
  595. Data: map[string][]byte{
  596. string(v1.DockerConfigJsonKey): data,
  597. },
  598. Type: v1.SecretTypeDockerConfigJson,
  599. },
  600. metav1.CreateOptions{},
  601. )
  602. if err != nil {
  603. return nil, err
  604. }
  605. // add secret name to the map
  606. res[key] = secretName
  607. continue
  608. } else if err != nil {
  609. return nil, err
  610. }
  611. // otherwise, check that the secret contains the correct data: if
  612. // if doesn't, update it
  613. if !bytes.Equal(secret.Data[v1.DockerConfigJsonKey], data) {
  614. _, err := a.Clientset.CoreV1().Secrets(namespace).Update(
  615. context.TODO(),
  616. &v1.Secret{
  617. ObjectMeta: metav1.ObjectMeta{
  618. Name: secretName,
  619. },
  620. Data: map[string][]byte{
  621. string(v1.DockerConfigJsonKey): data,
  622. },
  623. Type: v1.SecretTypeDockerConfigJson,
  624. },
  625. metav1.UpdateOptions{},
  626. )
  627. if err != nil {
  628. return nil, err
  629. }
  630. }
  631. // add secret name to the map
  632. res[key] = secretName
  633. }
  634. return res, nil
  635. }