diff options
author | walkafwalka <2865898-walkafwalka@users.noreply.gitlab.com> | 2019-03-08 00:51:43 +0300 |
---|---|---|
committer | walkafwalka <2865898-walkafwalka@users.noreply.gitlab.com> | 2019-03-08 02:25:48 +0300 |
commit | 460797dec3dc143943390e86a09d6e6b45a465d8 (patch) | |
tree | a5495703f3bfd1152929c84853322cd9a6812f15 | |
parent | 31cf53df4d766ef58d6d5be60d5fa2e380406fc8 (diff) |
Add support for ingress hostnames
30 files changed, 234 insertions, 154 deletions
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index f74cd71de04..13e8617c515 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -89,53 +89,26 @@ export default { ingressInstalled() { return this.applications.ingress.status === APPLICATION_STATUS.INSTALLED; }, - ingressExternalIp() { - return this.applications.ingress.externalIp; + ingressExternalEndpoint() { + return this.applications.ingress.externalIp || this.applications.ingress.externalHostname; }, certManagerInstalled() { return this.applications.cert_manager.status === APPLICATION_STATUS.INSTALLED; }, ingressDescription() { - const extraCostParagraph = sprintf( - _.escape( - s__( - `ClusterIntegration|%{boldNotice} This will add some extra resources - like a load balancer, which may incur additional costs depending on - the hosting provider your Kubernetes cluster is installed on. If you are using - Google Kubernetes Engine, you can %{pricingLink}.`, - ), - ), - { - boldNotice: `<strong>${_.escape(s__('ClusterIntegration|Note:'))}</strong>`, - pricingLink: `<a href="https://cloud.google.com/compute/pricing#lb" target="_blank" rel="noopener noreferrer"> - ${_.escape(s__('ClusterIntegration|check the pricing here'))}</a>`, - }, - false, - ); - - const externalIpParagraph = sprintf( + return sprintf( _.escape( s__( - `ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS - at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}`, + `ClusterIntegration|Installing Ingress may incur additional costs. Learn more about %{pricingLink}.`, ), ), { - ingressHelpLink: `<a href="${this.ingressHelpPath}"> - ${_.escape(s__('ClusterIntegration|More information'))} - </a>`, + pricingLink: `<strong><a href="https://cloud.google.com/compute/pricing#lb" + target="_blank" rel="noopener noreferrer"> + ${_.escape(s__('ClusterIntegration|pricing'))}</a></strong>`, }, false, ); - - return ` - <p> - ${extraCostParagraph} - </p> - <p class="settings-message append-bottom-0"> - ${externalIpParagraph} - </p> - `; }, certManagerDescription() { return sprintf( @@ -196,11 +169,26 @@ export default { knativeUpgradeFailed() { return this.knative.status === APPLICATION_STATUS.UPDATE_ERRORED; }, - knativeExternalIp() { - return this.knative.externalIp; + knativeExternalEndpoint() { + return this.knative.externalIp || this.knative.externalHostname; + }, + knativeDescription() { + return sprintf( + _.escape( + s__( + `ClusterIntegration|Installing Knative may incur additional costs. Learn more about %{pricingLink}.`, + ), + ), + { + pricingLink: `<strong><a href="https://cloud.google.com/compute/pricing#lb" + target="_blank" rel="noopener noreferrer"> + ${_.escape(s__('ClusterIntegration|pricing'))}</a></strong>`, + }, + false, + ); }, canUpdateKnativeEndpoint() { - return this.knativeExternalIp && !this.knativeUpgradeFailed && !this.knativeUpgrading; + return this.knativeExternalEndpoint && !this.knativeUpgradeFailed && !this.knativeUpgrading; }, knativeHostname: { get() { @@ -289,31 +277,31 @@ export default { <template v-if="ingressInstalled"> <div class="form-group"> - <label for="ingress-ip-address"> - {{ s__('ClusterIntegration|Ingress IP Address') }} + <label for="ingress-endpoint"> + {{ s__('ClusterIntegration|Ingress Endpoint') }} </label> - <div v-if="ingressExternalIp" class="input-group"> + <div v-if="ingressExternalEndpoint" class="input-group"> <input - id="ingress-ip-address" - :value="ingressExternalIp" + id="ingress-endpoint" + :value="ingressExternalEndpoint" type="text" - class="form-control js-ip-address" + class="form-control js-endpoint" readonly /> <span class="input-group-append"> <clipboard-button - :text="ingressExternalIp" - :title="s__('ClusterIntegration|Copy Ingress IP Address to clipboard')" + :text="ingressExternalEndpoint" + :title="s__('ClusterIntegration|Copy Ingress Endpoint to clipboard')" class="input-group-text js-clipboard-btn" /> </span> </div> - <input v-else type="text" class="form-control js-ip-address" readonly value="?" /> + <input v-else type="text" class="form-control js-endpoint" readonly value="?" /> <p class="form-text text-muted"> {{ s__(`ClusterIntegration|Point a wildcard DNS to this - generated IP address in order to access - your application after it has been deployed.`) + generated endpoint in order to access + your application after it has been deployed.`) }} <a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer"> {{ __('More information') }} @@ -321,19 +309,21 @@ export default { </p> </div> - <p v-if="!ingressExternalIp" class="settings-message js-no-ip-message"> + <p v-if="!ingressExternalEndpoint" class="settings-message js-no-endpoint-message"> {{ - s__(`ClusterIntegration|The IP address is in - the process of being assigned. Please check your Kubernetes - cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) + s__(`ClusterIntegration|The endpoint is in + the process of being assigned. Please check your Kubernetes + cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) }} - <a :href="ingressHelpPath" target="_blank" rel="noopener noreferrer"> + <a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer"> {{ __('More information') }} </a> </p> </template> - <div v-html="ingressDescription"></div> + <template v-if="!ingressInstalled"> + <div class="bs-callout bs-callout-info" v-html="ingressDescription"></div> + </template> </div> </application-row> <application-row @@ -443,7 +433,7 @@ export default { }} </p> - <template v-if="ingressExternalIp"> + <template v-if="ingressExternalEndpoint"> <div class="form-group"> <label for="jupyter-hostname"> {{ s__('ClusterIntegration|Jupyter Hostname') }} @@ -468,7 +458,7 @@ export default { <p v-if="ingressInstalled" class="form-text text-muted"> {{ s__(`ClusterIntegration|Replace this with your own hostname if you want. - If you do so, point hostname to Ingress IP Address from above.`) + If you do so, point hostname to Ingress IP Address from above.`) }} <a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer"> {{ __('More information') }} @@ -493,10 +483,10 @@ export default { > <div slot="description"> <span v-if="!rbac"> - <p v-if="!rbac" class="bs-callout bs-callout-info append-bottom-0"> + <p v-if="!rbac" class="rbac-notice bs-callout bs-callout-info append-bottom-0"> {{ s__(`ClusterIntegration|You must have an RBAC-enabled cluster - to install Knative.`) + to install Knative.`) }} <a :href="helpPath" target="_blank" rel="noopener noreferrer"> {{ __('More information') }} @@ -534,31 +524,31 @@ export default { </template> <template v-if="knativeInstalled"> <div class="form-group col-sm-12 col-md-6 pl-md-0 mb-0 mt-3 mt-md-0"> - <label for="knative-ip-address"> + <label for="knative-endpoint"> <strong> {{ s__('ClusterIntegration|Knative Endpoint:') }} </strong> </label> - <div v-if="knativeExternalIp" class="input-group"> + <div v-if="knativeExternalEndpoint" class="input-group"> <input - id="knative-ip-address" - :value="knativeExternalIp" + id="knative-endpoint" + :value="knativeExternalEndpoint" type="text" - class="form-control js-knative-ip-address" + class="form-control js-knative-endpoint" readonly /> <span class="input-group-append"> <clipboard-button - :text="knativeExternalIp" + :text="knativeExternalEndpoint" :title="s__('ClusterIntegration|Copy Knative Endpoint to clipboard')" - class="input-group-text js-knative-ip-clipboard-btn" + class="input-group-text js-knative-endpoint-clipboard-btn" /> </span> </div> <input v-else type="text" - class="form-control js-knative-ip-address" + class="form-control js-knative-endpoint" readonly value="?" /> @@ -576,13 +566,13 @@ export default { </p> <p - v-if="!knativeExternalIp" - class="settings-message js-no-knative-ip-message mt-2 mr-3 mb-0 ml-3 " + v-if="!knativeExternalEndpoint" + class="settings-message js-no-knative-endpoint-message mt-2 mr-3 mb-0 ml-3" > {{ - s__(`ClusterIntegration|The IP address is in - the process of being assigned. Please check your Kubernetes - cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) + s__(`ClusterIntegration|The endpoint is in + the process of being assigned. Please check your Kubernetes + cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) }} </p> diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js index 3f03a8512fc..92993337f02 100644 --- a/app/assets/javascripts/clusters/stores/clusters_store.js +++ b/app/assets/javascripts/clusters/stores/clusters_store.js @@ -25,6 +25,7 @@ export default class ClusterStore { requestStatus: null, requestReason: null, externalIp: null, + externalHostname: null, }, cert_manager: { title: s__('ClusterIntegration|Cert-Manager'), @@ -68,6 +69,7 @@ export default class ClusterStore { hostname: null, isEditingHostName: false, externalIp: null, + externalHostname: null, }, }, }; @@ -120,6 +122,7 @@ export default class ClusterStore { if (appId === INGRESS) { this.state.applications.ingress.externalIp = serverAppEntry.external_ip; + this.state.applications.ingress.externalHostname = serverAppEntry.external_hostname; } else if (appId === CERT_MANAGER) { this.state.applications.cert_manager.email = this.state.applications.cert_manager.email || serverAppEntry.email; @@ -136,6 +139,8 @@ export default class ClusterStore { } this.state.applications.knative.externalIp = serverAppEntry.external_ip || this.state.applications.knative.externalIp; + this.state.applications.knative.externalHostname = + serverAppEntry.external_hostname || this.state.applications.knative.externalHostname; } else if (appId === RUNNER) { this.state.applications.runner.version = version; this.state.applications.runner.upgradeAvailable = upgradeAvailable; diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb index 7c15aaa4825..567f1a2267f 100644 --- a/app/models/clusters/applications/ingress.rb +++ b/app/models/clusters/applications/ingress.rb @@ -48,6 +48,7 @@ module Clusters def schedule_status_update return unless installed? return if external_ip + return if external_hostname ClusterWaitForIngressIpAddressWorker.perform_async(name, id) end diff --git a/app/models/clusters/applications/jupyter.rb b/app/models/clusters/applications/jupyter.rb index 80205775b6a..7efcc175f9f 100644 --- a/app/models/clusters/applications/jupyter.rb +++ b/app/models/clusters/applications/jupyter.rb @@ -18,8 +18,10 @@ module Clusters def set_initial_status return unless not_installable? + return unless cluster&.application_ingress_available? - if cluster&.application_ingress_available? && cluster.application_ingress.external_ip + ingress = cluster.application_ingress + if ingress.external_ip || ingress.external_hostname self.status = 'installable' end end diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb index 8d79b041b64..347c3c8c37f 100644 --- a/app/models/clusters/applications/knative.rb +++ b/app/models/clusters/applications/knative.rb @@ -66,6 +66,7 @@ module Clusters def schedule_status_update return unless installed? return if external_ip + return if external_hostname ClusterWaitForIngressIpAddressWorker.perform_async(name, id) end diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index be3e6a05e1e..5156c7d7514 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -67,6 +67,7 @@ module Clusters delegate :available?, to: :application_prometheus, prefix: true, allow_nil: true delegate :available?, to: :application_knative, prefix: true, allow_nil: true delegate :external_ip, to: :application_ingress, prefix: true, allow_nil: true + delegate :external_hostname, to: :application_ingress, prefix: true, allow_nil: true alias_attribute :base_domain, :domain diff --git a/app/serializers/cluster_application_entity.rb b/app/serializers/cluster_application_entity.rb index 02df1480828..a4a2c015c4e 100644 --- a/app/serializers/cluster_application_entity.rb +++ b/app/serializers/cluster_application_entity.rb @@ -6,6 +6,7 @@ class ClusterApplicationEntity < Grape::Entity expose :status_reason expose :version expose :external_ip, if: -> (e, _) { e.respond_to?(:external_ip) } + expose :external_hostname, if: -> (e, _) { e.respond_to?(:external_hostname) } expose :hostname, if: -> (e, _) { e.respond_to?(:hostname) } expose :email, if: -> (e, _) { e.respond_to?(:email) } expose :update_available?, as: :update_available, if: -> (e, _) { e.respond_to?(:update_available?) } diff --git a/app/services/clusters/applications/check_ingress_ip_address_service.rb b/app/services/clusters/applications/check_ingress_ip_address_service.rb index 0ec06e776a7..e254a0358a0 100644 --- a/app/services/clusters/applications/check_ingress_ip_address_service.rb +++ b/app/services/clusters/applications/check_ingress_ip_address_service.rb @@ -11,9 +11,13 @@ module Clusters def execute return if app.external_ip + return if app.external_hostname return unless try_obtain_lease - app.update!(external_ip: ingress_ip) if ingress_ip + app.external_ip = ingress_ip if ingress_ip + app.external_hostname = ingress_hostname if ingress_hostname + + app.save! if app.changed? end private @@ -25,12 +29,16 @@ module Clusters end def ingress_ip - service.status.loadBalancer.ingress&.first&.ip + ingress_service&.ip + end + + def ingress_hostname + ingress_service&.hostname end - def service + def ingress_service strong_memoize(:ingress_service) do - app.ingress_service + app.ingress_service.status.loadBalancer.ingress&.first end end end diff --git a/app/views/clusters/clusters/_form.html.haml b/app/views/clusters/clusters/_form.html.haml index 9fb91a39387..c08b41e2f23 100644 --- a/app/views/clusters/clusters/_form.html.haml +++ b/app/views/clusters/clusters/_form.html.haml @@ -37,7 +37,7 @@ = s_('ClusterIntegration|Alternatively') %code #{@cluster.application_ingress_external_ip}.nip.io = s_('ClusterIntegration| can be used instead of a custom domain.') - - custom_domain_url = help_page_path('user/project/clusters/index', anchor: 'pointing-your-dns-at-the-cluster-ip') + - custom_domain_url = help_page_path('user/project/clusters/index', anchor: 'pointing-your-dns-at-the-external-endpoint') - custom_domain_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: custom_domain_url } = s_('ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}.').html_safe % { custom_domain_start: custom_domain_start, custom_domain_end: '</a>'.html_safe } diff --git a/app/views/clusters/clusters/show.html.haml b/app/views/clusters/clusters/show.html.haml index 7d381c6d4a6..884fa323093 100644 --- a/app/views/clusters/clusters/show.html.haml +++ b/app/views/clusters/clusters/show.html.haml @@ -22,8 +22,8 @@ cluster_status: @cluster.status_name, cluster_status_reason: @cluster.status_reason, help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'), - ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-ip-address'), - ingress_dns_help_path: help_page_path('topics/autodevops/quick_start_guide.md', anchor: 'point-dns-at-cluster-ip'), + ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-endpoint'), + ingress_dns_help_path: help_page_path('user/project/clusters/index.md', anchor: 'manually-determining-the-external-endpoint'), manage_prometheus_path: manage_prometheus_path } } .js-cluster-application-notice diff --git a/changelogs/unreleased/ingress-hostnames.yml b/changelogs/unreleased/ingress-hostnames.yml new file mode 100644 index 00000000000..66721113769 --- /dev/null +++ b/changelogs/unreleased/ingress-hostnames.yml @@ -0,0 +1,5 @@ +--- +title: Added support for ingress hostnames +merge_request: 25181 +author: walkafwalka +type: added diff --git a/db/migrate/20190301182457_add_external_hostname_to_ingress_and_knative.rb b/db/migrate/20190301182457_add_external_hostname_to_ingress_and_knative.rb new file mode 100644 index 00000000000..2c3a54b12a9 --- /dev/null +++ b/db/migrate/20190301182457_add_external_hostname_to_ingress_and_knative.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class AddExternalHostnameToIngressAndKnative < ActiveRecord::Migration[5.0] + DOWNTIME = false + + def change + add_column :clusters_applications_ingress, :external_hostname, :string + add_column :clusters_applications_knative, :external_hostname, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index c782524c391..59a76e21a5f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20190301081611) do +ActiveRecord::Schema.define(version: 20190301182457) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -708,6 +708,7 @@ ActiveRecord::Schema.define(version: 20190301081611) do t.string "cluster_ip" t.text "status_reason" t.string "external_ip" + t.string "external_hostname" t.index ["cluster_id"], name: "index_clusters_applications_ingress_on_cluster_id", unique: true, using: :btree end @@ -733,6 +734,7 @@ ActiveRecord::Schema.define(version: 20190301081611) do t.string "hostname" t.text "status_reason" t.string "external_ip" + t.string "external_hostname" t.index ["cluster_id"], name: "index_clusters_applications_knative_on_cluster_id", unique: true, using: :btree end diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index 3819dc308ec..ef85b2f6837 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -387,27 +387,27 @@ Upgrades will reset values back to the values built into the `runner` chart plus the values set by [`values.yaml`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/vendor/runner/values.yaml) -## Getting the external IP address +## Getting the external endpoint NOTE: **Note:** With the following procedure, a load balancer must be installed in your cluster -to obtain the external IP address. You can use either +to obtain the endpoint. You can use either [Ingress](#installing-applications), or Knative's own load balancer ([Istio](https://istio.io)) if using [Knative](#installing-applications). -In order to publish your web application, you first need to find the external IP -address associated to your load balancer. +In order to publish your web application, you first need to find the endpoint which will be either an IP +address or a hostname associated with your load balancer. -### Let GitLab fetch the IP address +### Let GitLab fetch the external endpoint > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17052) in GitLab 10.6. If you [installed Ingress or Knative](#installing-applications), -you should see the Ingress IP address on this same page within a few minutes. -If you don't see this, GitLab might not be able to determine the IP address of +you should see the Ingress Endpoint on this same page within a few minutes. +If you don't see this, GitLab might not be able to determine the external endpoint of your ingress application in which case you should manually determine it. -### Manually determining the IP address +### Manually determining the external endpoint If the cluster is on GKE, click the **Google Kubernetes Engine** link in the **Advanced settings**, or go directly to the @@ -417,7 +417,7 @@ the `gcloud` command in a local terminal or using the **Cloud Shell**. If the cluster is not on GKE, follow the specific instructions for your Kubernetes provider to configure `kubectl` with the right credentials. -The output of the following examples will show the external IP address of your +The output of the following examples will show the external endpoint of your cluster. This information can then be used to set up DNS entries and forwarding rules that allow external access to your deployed applications. @@ -425,19 +425,19 @@ If you installed the Ingress [via the **Applications**](#installing-applications run the following command: ```bash -kubectl get svc --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip} ' +kubectl get service --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}' ``` -For Istio/Knative, the command will be different: +Some Kubernetes clusters return a hostname instead, like [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run: ```bash -kubectl get svc --namespace=istio-system knative-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} ' +kubectl get service --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' ``` -Some Kubernetes clusters return a hostname instead, like [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run: +For Istio/Knative, the command will be different: ```bash -kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}". +kubectl get svc --namespace=istio-system knative-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} ' ``` Otherwise, you can list the IP addresses of all load balancers: @@ -456,13 +456,12 @@ reserved IP. Read how to [promote an ephemeral external IP address in GKE](https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address#promote_ephemeral_ip). -### Pointing your DNS at the cluster IP +### Pointing your DNS at the external endpoint -Once you've set up the static IP, you should associate it to a [wildcard DNS -record](https://en.wikipedia.org/wiki/Wildcard_DNS_record), in order to be able -to reach your apps. This heavily depends on your domain provider, but in case -you aren't sure, just create an A record with a wildcard host like -`*.example.com.`. +Once you've set up the external endpoint, you should associate it with a [wildcard DNS +record](https://en.wikipedia.org/wiki/Wildcard_DNS_record) such as `*.example.com.` +in order to be able to reach your apps. If your external endpoint is an IP address, +use an A record. If your external endpoint is a hostname, use a CNAME record. ## Multiple Kubernetes clusters **[PREMIUM]** diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md index 856ae03f4bc..e6804666e22 100644 --- a/doc/user/project/clusters/serverless/index.md +++ b/doc/user/project/clusters/serverless/index.md @@ -37,9 +37,9 @@ To run Knative on Gitlab, you will need: applications or functions onto your cluster. You can install the GitLab Runner onto the existing Kubernetes cluster. See [Installing Applications](../index.md#installing-applications) for more information. 1. **Domain Name:** Knative will provide its own load balancer using Istio. It will provide an - external IP address for all the applications served by Knative. You will be prompted to enter a + external IP address or hostname for all the applications served by Knative. You will be prompted to enter a wildcard domain where your applications will be served. Configure your DNS server to use the - external IP address for that domain. + external IP address or hostname for that domain. 1. **`.gitlab-ci.yml`:** GitLab uses [Kaniko](https://github.com/GoogleContainerTools/kaniko) to build the application and the [TriggerMesh CLI](https://github.com/triggermesh/tm) to simplify the deployment of knative services and functions. @@ -62,18 +62,8 @@ The minimum recommended cluster size to run Knative is 3-nodes, 6 vCPUs, and 22. ![install-knative](img/install-knative.png) -1. After the Knative installation has finished, you can wait for the IP address to be displayed in the - **Knative IP Address** field (takes up to 5 minutes) or retrieve the Istio Ingress IP address by running the following command: - - ```bash - kubectl get svc --namespace=istio-system knative-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} ' - ``` - - Output: - - ```bash - 35.161.143.124 my-machine-name:~ my-user$ - ``` +1. After the Knative installation has finished, you can wait for the IP address or hostname to be displayed in the + **Knative Endpoint** field or [retrieve the Istio Ingress Endpoint manually](../#manually-determining-the-external-endpoint). NOTE: **Note:** Running `kubectl` commands on your cluster requires setting up access to the cluster first. @@ -82,8 +72,8 @@ The minimum recommended cluster size to run Knative is 3-nodes, 6 vCPUs, and 22. 1. The ingress is now available at this address and will route incoming requests to the proper service based on the DNS name in the request. To support this, a wildcard DNS A record should be created for the desired domain name. For example, - if your Knative base domain is `example.com` then you need to create an A record with domain `*.example.com` - pointing the ip address of the ingress. + if your Knative base domain is `knative.info` then you need to create an A record or CNAME record with domain `*.knative.info` + pointing the ip address or hostname of the ingress. ![dns entry](img/dns-entry.png) diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress.md b/doc/user/project/integrations/prometheus_library/nginx_ingress.md index b7601f26802..de7fc93f0a4 100644 --- a/doc/user/project/integrations/prometheus_library/nginx_ingress.md +++ b/doc/user/project/integrations/prometheus_library/nginx_ingress.md @@ -30,7 +30,7 @@ For other deployments, there is [some configuration](#manually-setting-up-nginx- ### About managed NGINX Ingress deployments -NGINX Ingress is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress). NGINX Ingress will be [externally reachable via the Load Balancer's IP](../../clusters/index.md#getting-the-external-ip-address). +NGINX Ingress is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress). NGINX Ingress will be [externally reachable via the Load Balancer's Endpoint](../../clusters/index.md#getting-the-external-endpoint). NGINX is configured for Prometheus monitoring, by setting: diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md b/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md index 081eb8732ad..31ac53c0d14 100644 --- a/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md +++ b/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md @@ -30,7 +30,7 @@ For other deployments, there is [some configuration](#manually-setting-up-nginx- ### About managed NGINX Ingress deployments -NGINX Ingress is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress). NGINX Ingress will be [externally reachable via the Load Balancer's IP](../../clusters/index.md#getting-the-external-ip-address). +NGINX Ingress is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress). NGINX Ingress will be [externally reachable via the Load Balancer's Endpoint](../../clusters/index.md#getting-the-external-endpoint). NGINX is configured for Prometheus monitoring, by setting: diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 05d8ac1a8d0..abca0256726 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1614,9 +1614,6 @@ msgstr "" msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster" msgstr "" -msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}." -msgstr "" - msgid "ClusterIntegration|%{title} upgraded successfully." msgstr "" @@ -1638,9 +1635,6 @@ msgstr "" msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration" msgstr "" -msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}" -msgstr "" - msgid "ClusterIntegration|Alternatively" msgstr "" @@ -1695,7 +1689,7 @@ msgstr "" msgid "ClusterIntegration|Copy CA Certificate" msgstr "" -msgid "ClusterIntegration|Copy Ingress IP Address to clipboard" +msgid "ClusterIntegration|Copy Ingress Endpoint to clipboard" msgstr "" msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard" @@ -1782,7 +1776,7 @@ msgstr "" msgid "ClusterIntegration|Ingress" msgstr "" -msgid "ClusterIntegration|Ingress IP Address" +msgid "ClusterIntegration|Ingress Endpoint" msgstr "" msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint." @@ -1797,6 +1791,12 @@ msgstr "" msgid "ClusterIntegration|Installing" msgstr "" +msgid "ClusterIntegration|Installing Ingress may incur additional costs. Learn more about %{pricingLink}." +msgstr "" + +msgid "ClusterIntegration|Installing Knative may incur additional costs. Learn more about %{pricingLink}." +msgstr "" + msgid "ClusterIntegration|Integrate Kubernetes cluster automation" msgstr "" @@ -1878,9 +1878,6 @@ msgstr "" msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}" msgstr "" -msgid "ClusterIntegration|More information" -msgstr "" - msgid "ClusterIntegration|No machine types matched your search" msgstr "" @@ -1893,9 +1890,6 @@ msgstr "" msgid "ClusterIntegration|No zones matched your search" msgstr "" -msgid "ClusterIntegration|Note:" -msgstr "" - msgid "ClusterIntegration|Number of nodes" msgstr "" @@ -1905,7 +1899,7 @@ msgstr "" msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:" msgstr "" -msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed." +msgid "ClusterIntegration|Point a wildcard DNS to this generated endpoint in order to access your application after it has been deployed." msgstr "" msgid "ClusterIntegration|Project cluster" @@ -1998,7 +1992,7 @@ msgstr "" msgid "ClusterIntegration|Specifying a domain will allow you to use Auto Review Apps and Auto Deploy stages for %{auto_devops_start}Auto DevOps%{auto_devops_end}. The domain should have a wildcard DNS configured matching the domain." msgstr "" -msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time." +msgid "ClusterIntegration|The endpoint is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time." msgstr "" msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below" @@ -2058,9 +2052,6 @@ msgstr "" msgid "ClusterIntegration|access to Google Kubernetes Engine" msgstr "" -msgid "ClusterIntegration|check the pricing here" -msgstr "" - msgid "ClusterIntegration|documentation" msgstr "" @@ -2070,6 +2061,9 @@ msgstr "" msgid "ClusterIntegration|meets the requirements" msgstr "" +msgid "ClusterIntegration|pricing" +msgstr "" + msgid "ClusterIntegration|properly configured" msgstr "" diff --git a/qa/qa/page/project/operations/kubernetes/show.rb b/qa/qa/page/project/operations/kubernetes/show.rb index d4e1679b6bf..50a25718e96 100644 --- a/qa/qa/page/project/operations/kubernetes/show.rb +++ b/qa/qa/page/project/operations/kubernetes/show.rb @@ -11,7 +11,7 @@ module QA end view 'app/assets/javascripts/clusters/components/applications.vue' do - element :ingress_ip_address, 'id="ingress-ip-address"' # rubocop:disable QA/ElementWithPattern + element :ingress_ip_address, 'id="ingress-endpoint"' # rubocop:disable QA/ElementWithPattern end view 'app/views/clusters/clusters/_form.html.haml' do @@ -35,7 +35,7 @@ module QA def ingress_ip # We need to wait longer since it can take some time before the # ip address is assigned for the ingress controller - page.find('#ingress-ip-address', wait: 1200).value + page.find('#ingress-endpoint', wait: 1200).value end def set_domain(domain) diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb index 713e25cdcb2..4981bf794d9 100644 --- a/spec/features/projects/clusters/applications_spec.rb +++ b/spec/features/projects/clusters/applications_spec.rb @@ -82,7 +82,7 @@ describe 'Clusters Applications', :js do it 'should show info block and not be installable' do page.within('.js-cluster-application-row-knative') do - expect(page).to have_css('.bs-callout-info') + expect(page).to have_css('.rbac-notice') expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true') end end @@ -93,7 +93,7 @@ describe 'Clusters Applications', :js do it 'should not show callout block and be installable' do page.within('.js-cluster-application-row-knative') do - expect(page).not_to have_css('.bs-callout-info') + expect(page).not_to have_css('.rbac-notice') expect(page).to have_css('.js-cluster-application-install-button:not([disabled])') end end @@ -226,14 +226,14 @@ describe 'Clusters Applications', :js do expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installed') expect(page).to have_css('.js-cluster-application-install-button[disabled]') - expect(page).to have_selector('.js-no-ip-message') - expect(page.find('.js-ip-address').value).to eq('?') + expect(page).to have_selector('.js-no-endpoint-message') + expect(page.find('.js-endpoint').value).to eq('?') # We receive the external IP address and display Clusters::Cluster.last.application_ingress.update!(external_ip: '192.168.1.100') - expect(page).not_to have_selector('.js-no-ip-message') - expect(page.find('.js-ip-address').value).to eq('192.168.1.100') + expect(page).not_to have_selector('.js-no-endpoint-message') + expect(page.find('.js-endpoint').value).to eq('192.168.1.100') end expect(page).to have_content('Ingress was successfully installed on your Kubernetes cluster') diff --git a/spec/fixtures/api/schemas/cluster_status.json b/spec/fixtures/api/schemas/cluster_status.json index 5ebc09a96dc..9da07a0b253 100644 --- a/spec/fixtures/api/schemas/cluster_status.json +++ b/spec/fixtures/api/schemas/cluster_status.json @@ -33,6 +33,7 @@ "version": { "type": "string" }, "status_reason": { "type": ["string", "null"] }, "external_ip": { "type": ["string", "null"] }, + "external_hostname": { "type": ["string", "null"] }, "hostname": { "type": ["string", "null"] }, "email": { "type": ["string", "null"] }, "update_available": { "type": ["boolean", "null"] } diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js index 8daf0282184..e2466bf326c 100644 --- a/spec/javascripts/clusters/components/applications_spec.js +++ b/spec/javascripts/clusters/components/applications_spec.js @@ -106,7 +106,7 @@ describe('Applications', () => { }, }); - expect(vm.$el.querySelector('.js-ip-address').value).toEqual('0.0.0.0'); + expect(vm.$el.querySelector('.js-endpoint').value).toEqual('0.0.0.0'); expect( vm.$el.querySelector('.js-clipboard-btn').getAttribute('data-clipboard-text'), @@ -114,6 +114,32 @@ describe('Applications', () => { }); }); + describe('with hostname', () => { + it('renders hostname with a clipboard button', () => { + vm = mountComponent(Applications, { + applications: { + ingress: { + title: 'Ingress', + status: 'installed', + externalHostname: 'localhost.localdomain', + }, + helm: { title: 'Helm Tiller' }, + cert_manager: { title: 'Cert-Manager' }, + runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, + jupyter: { title: 'JupyterHub', hostname: '' }, + knative: { title: 'Knative', hostname: '' }, + }, + }); + + expect(vm.$el.querySelector('.js-endpoint').value).toEqual('localhost.localdomain'); + + expect( + vm.$el.querySelector('.js-clipboard-btn').getAttribute('data-clipboard-text'), + ).toEqual('localhost.localdomain'); + }); + }); + describe('without ip address', () => { it('renders an input text with a question mark and an alert text', () => { vm = mountComponent(Applications, { @@ -126,9 +152,9 @@ describe('Applications', () => { }, }); - expect(vm.$el.querySelector('.js-ip-address').value).toEqual('?'); + expect(vm.$el.querySelector('.js-endpoint').value).toEqual('?'); - expect(vm.$el.querySelector('.js-no-ip-message')).not.toBe(null); + expect(vm.$el.querySelector('.js-no-endpoint-message')).not.toBe(null); }); }); }); @@ -140,7 +166,7 @@ describe('Applications', () => { }); expect(vm.$el.textContent).not.toContain('Ingress IP Address'); - expect(vm.$el.querySelector('.js-ip-address')).toBe(null); + expect(vm.$el.querySelector('.js-endpoint')).toBe(null); }); }); @@ -268,11 +294,11 @@ describe('Applications', () => { it('renders ip address with a clipboard button', () => { vm = mountComponent(Applications, props); - expect(vm.$el.querySelector('.js-knative-ip-address').value).toEqual('1.1.1.1'); + expect(vm.$el.querySelector('.js-knative-endpoint').value).toEqual('1.1.1.1'); expect( vm.$el - .querySelector('.js-knative-ip-clipboard-btn') + .querySelector('.js-knative-endpoint-clipboard-btn') .getAttribute('data-clipboard-text'), ).toEqual('1.1.1.1'); }); @@ -316,9 +342,9 @@ describe('Applications', () => { }, }); - expect(vm.$el.querySelector('.js-knative-ip-address').value).toEqual('?'); + expect(vm.$el.querySelector('.js-knative-endpoint').value).toEqual('?'); - expect(vm.$el.querySelector('.js-no-knative-ip-message')).not.toBe(null); + expect(vm.$el.querySelector('.js-no-knative-endpoint-message')).not.toBe(null); }); }); }); diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js index 3ace19c6401..b4d1bb710e0 100644 --- a/spec/javascripts/clusters/services/mock_data.js +++ b/spec/javascripts/clusters/services/mock_data.js @@ -17,6 +17,7 @@ const CLUSTERS_MOCK_DATA = { status: APPLICATION_STATUS.ERROR, status_reason: 'Cannot connect', external_ip: null, + external_hostname: null, }, { name: 'runner', @@ -62,6 +63,7 @@ const CLUSTERS_MOCK_DATA = { status: APPLICATION_STATUS.INSTALLED, status_reason: 'Cannot connect', external_ip: '1.1.1.1', + external_hostname: null, }, { name: 'runner', diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js index 09bcdf91d91..161722ec571 100644 --- a/spec/javascripts/clusters/stores/clusters_store_spec.js +++ b/spec/javascripts/clusters/stores/clusters_store_spec.js @@ -78,6 +78,7 @@ describe('Clusters Store', () => { requestStatus: null, requestReason: null, externalIp: null, + externalHostname: null, }, runner: { title: 'GitLab Runner', @@ -113,6 +114,7 @@ describe('Clusters Store', () => { hostname: null, isEditingHostName: false, externalIp: null, + externalHostname: null, }, cert_manager: { title: 'Cert-Manager', diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb index d5fd42509a3..a40fa988287 100644 --- a/spec/models/clusters/applications/ingress_spec.rb +++ b/spec/models/clusters/applications/ingress_spec.rb @@ -56,6 +56,14 @@ describe Clusters::Applications::Ingress do expect(ClusterWaitForIngressIpAddressWorker).not_to have_received(:perform_in) end end + + context 'when there is already an external_hostname' do + let(:application) { create(:clusters_applications_ingress, :installed, external_hostname: 'localhost.localdomain') } + + it 'does not schedule a ClusterWaitForIngressIpAddressWorker' do + expect(ClusterWaitForIngressIpAddressWorker).not_to have_received(:perform_in) + end + end end describe '#install_command' do diff --git a/spec/models/clusters/applications/jupyter_spec.rb b/spec/models/clusters/applications/jupyter_spec.rb index 6e58f3ad699..2967c4076c6 100644 --- a/spec/models/clusters/applications/jupyter_spec.rb +++ b/spec/models/clusters/applications/jupyter_spec.rb @@ -26,6 +26,13 @@ describe Clusters::Applications::Jupyter do it { expect(jupyter).to be_installable } end + + context 'when ingress is installed and external_hostname is assigned' do + let(:ingress) { create(:clusters_applications_ingress, :installed, external_hostname: 'localhost.localdomain') } + let(:jupyter) { create(:clusters_applications_jupyter, cluster: ingress.cluster) } + + it { expect(jupyter).to be_installable } + end end describe '#install_command' do diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb index 4884a5927fb..bf425a2617c 100644 --- a/spec/models/clusters/applications/knative_spec.rb +++ b/spec/models/clusters/applications/knative_spec.rb @@ -64,6 +64,14 @@ describe Clusters::Applications::Knative do expect(ClusterWaitForIngressIpAddressWorker).not_to have_received(:perform_in) end end + + context 'when there is already an external_hostname' do + let(:application) { create(:clusters_applications_knative, :installed, external_hostname: 'localhost.localdomain') } + + it 'does not schedule a ClusterWaitForIngressIpAddressWorker' do + expect(ClusterWaitForIngressIpAddressWorker).not_to have_received(:perform_in) + end + end end shared_examples 'a command' do diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index 3feed4e9718..acbcdc7d170 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -31,6 +31,7 @@ describe Clusters::Cluster do it { is_expected.to delegate_method(:available?).to(:application_prometheus).with_prefix } it { is_expected.to delegate_method(:available?).to(:application_knative).with_prefix } it { is_expected.to delegate_method(:external_ip).to(:application_ingress).with_prefix } + it { is_expected.to delegate_method(:external_hostname).to(:application_ingress).with_prefix } it { is_expected.to respond_to :project } diff --git a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb index f3036fbcb0e..80fc48d1b07 100644 --- a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb +++ b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb @@ -6,9 +6,17 @@ describe Clusters::Applications::CheckIngressIpAddressService do let(:application) { create(:clusters_applications_ingress, :installed) } let(:service) { described_class.new(application) } let(:kubeclient) { double(::Kubeclient::Client, get_service: kube_service) } - let(:ingress) { [{ ip: '111.222.111.222' }] } let(:lease_key) { "check_ingress_ip_address_service:#{application.id}" } + let(:ingress) do + [ + { + ip: '111.222.111.222', + hostname: 'localhost.localdomain' + } + ] + end + let(:kube_service) do ::Kubeclient::Resource.new( { diff --git a/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb b/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb index 14638a574a5..02de47a96dd 100644 --- a/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb +++ b/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb @@ -12,6 +12,14 @@ shared_examples 'check ingress ip executions' do |app_name| end end + context 'when the ingress external hostname is available' do + it 'updates the external_hostname for the app' do + subject + + expect(application.external_hostname).to eq('localhost.localdomain') + end + end + context 'when the ingress ip address is not available' do let(:ingress) { nil } |