2
0

domain.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. package domain
  2. import (
  3. "context"
  4. "fmt"
  5. "net"
  6. "strings"
  7. "github.com/porter-dev/porter/internal/encryption"
  8. "github.com/porter-dev/porter/internal/integrations/dns"
  9. "github.com/porter-dev/porter/internal/models"
  10. v1 "k8s.io/api/core/v1"
  11. "k8s.io/client-go/kubernetes"
  12. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  13. )
  14. // GetNGINXIngressServiceIP retrieves the external address of the nginx-ingress service
  15. func GetNGINXIngressServiceIP(clientset kubernetes.Interface) (string, bool, error) {
  16. // first check ingress-nginx namespace
  17. svcList, err := clientset.CoreV1().Services("ingress-nginx").List(context.TODO(), metav1.ListOptions{
  18. LabelSelector: "app.kubernetes.io/managed-by=Helm",
  19. })
  20. if err != nil {
  21. return "", false, err
  22. }
  23. var nginxSvc *v1.Service
  24. exists := false
  25. for _, svc := range svcList.Items {
  26. // check that helm chart annotation is correct exists
  27. if svc.Spec.Type == v1.ServiceTypeLoadBalancer {
  28. nginxSvc = &svc // nolint:gosec // quick fix to duplicate logic
  29. exists = true
  30. break
  31. }
  32. }
  33. if exists {
  34. if ipArr := nginxSvc.Status.LoadBalancer.Ingress; len(ipArr) > 0 {
  35. // first default to ip, then check hostname
  36. if ipArr[0].IP != "" {
  37. return ipArr[0].IP, true, nil
  38. } else if ipArr[0].Hostname != "" {
  39. return ipArr[0].Hostname, true, nil
  40. }
  41. }
  42. }
  43. // fall back to all namespaces
  44. nginxSvc = nil
  45. exists = false
  46. svcList, err = clientset.CoreV1().Services("").List(context.TODO(), metav1.ListOptions{
  47. LabelSelector: "app.kubernetes.io/managed-by=Helm",
  48. })
  49. if err != nil {
  50. return "", false, err
  51. }
  52. for _, svc := range svcList.Items {
  53. // check that helm chart annotation is correct exists
  54. if chartAnn, found := svc.ObjectMeta.Labels["helm.sh/chart"]; found {
  55. if (strings.Contains(chartAnn, "ingress-nginx") || strings.Contains(chartAnn, "nginx-ingress")) && svc.Spec.Type == v1.ServiceTypeLoadBalancer {
  56. nginxSvc = &svc
  57. exists = true
  58. break
  59. }
  60. }
  61. }
  62. if !exists {
  63. // look for alternate services/names (just Azure for now)
  64. svcList, err = clientset.CoreV1().Services("").List(context.TODO(), metav1.ListOptions{
  65. LabelSelector: "app=addon-http-application-routing-nginx-ingress",
  66. })
  67. if err != nil {
  68. return "", false, err
  69. }
  70. for _, svc := range svcList.Items {
  71. // check that the service is type load balancer
  72. if svc.Spec.Type == v1.ServiceTypeLoadBalancer {
  73. nginxSvc = &svc
  74. exists = true
  75. break
  76. }
  77. }
  78. if !exists {
  79. return "", false, nil
  80. }
  81. }
  82. if ipArr := nginxSvc.Status.LoadBalancer.Ingress; len(ipArr) > 0 {
  83. // first default to ip, then check hostname
  84. if ipArr[0].IP != "" {
  85. return ipArr[0].IP, true, nil
  86. } else if ipArr[0].Hostname != "" {
  87. return ipArr[0].Hostname, true, nil
  88. }
  89. }
  90. return "", false, nil
  91. }
  92. // DNSRecord wraps the gorm DNSRecord model
  93. type DNSRecord models.DNSRecord
  94. type CreateDNSRecordConfig struct {
  95. ReleaseName string
  96. RootDomain string
  97. Endpoint string
  98. }
  99. // NewDNSRecordForEndpoint generates a random subdomain and returns a DNSRecord
  100. // model
  101. func (c *CreateDNSRecordConfig) NewDNSRecordForEndpoint() *models.DNSRecord {
  102. suffix, _ := encryption.GenerateRandomBytes(8)
  103. subdomain := fmt.Sprintf("%s-%s", c.ReleaseName, suffix)
  104. return &models.DNSRecord{
  105. SubdomainPrefix: subdomain,
  106. RootDomain: c.RootDomain,
  107. Endpoint: c.Endpoint,
  108. Hostname: fmt.Sprintf("%s.%s", subdomain, c.RootDomain),
  109. }
  110. }
  111. // CreateDomain creates a new record for the vanity domain
  112. func (e *DNSRecord) CreateDomain(dnsClient *dns.Client) error {
  113. isIPv4 := net.ParseIP(e.Endpoint) != nil
  114. dnsType := dns.RecordType_CNAME
  115. if isIPv4 {
  116. dnsType = dns.RecordType_A
  117. }
  118. return dnsClient.CreateRecord(dns.Record{
  119. Type: dnsType,
  120. Value: e.Endpoint,
  121. Name: e.SubdomainPrefix,
  122. RootDomain: e.RootDomain,
  123. })
  124. }