diff --git a/api/v1beta1/ip_types.go b/api/v1beta1/ip_types.go index 79bc4a0e..28224994 100644 --- a/api/v1beta1/ip_types.go +++ b/api/v1beta1/ip_types.go @@ -42,9 +42,9 @@ type IPSpecIPSources struct { // ResponseJSONPath defines the JSON path to the value to be used as IP // +optional ResponseJSONPath string `json:"responseJSONPath,omitempty"` - // ResponseTextRegex defines the regular expression to be used to extract the IP from the response + // ResponseRegex defines the regular expression to be used to extract the IP from the response // +optional - ResponseTextRegex string `json:"responseTextRegex,omitempty"` + ResponseRegex string `json:"responseRegex,omitempty"` } // IPSpec defines the desired state of IP diff --git a/config/crd/bases/cf.containeroo.ch_ips.yaml b/config/crd/bases/cf.containeroo.ch_ips.yaml index 7df0f3a7..83d34c2e 100644 --- a/config/crd/bases/cf.containeroo.ch_ips.yaml +++ b/config/crd/bases/cf.containeroo.ch_ips.yaml @@ -93,9 +93,9 @@ spec: description: ResponseJSONPath defines the JSON path to the value to be used as IP type: string - responseTextRegex: - description: ResponseTextRegex defines the regular expression - to be used to extract the IP from the response + responseRegex: + description: ResponseRegex defines the regular expression to + be used to extract the IP from the response type: string url: description: URL of the IP source (e.g. https://api.hetzner.cloud/v1/servers/12345) diff --git a/controllers/ip_controller.go b/controllers/ip_controller.go index 1d18ba37..3739f968 100644 --- a/controllers/ip_controller.go +++ b/controllers/ip_controller.go @@ -139,20 +139,20 @@ func (r *IPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Re }) } - ipSourceError := true + var ipSourceError string for _, source := range instance.Spec.IPSources { response, err := r.getIPSource(ctx, source, log) if err != nil { - log.Error(err, "Failed to process source %s", source.URL) + ipSourceError = err.Error() continue } instance.Spec.Address = response - ipSourceError = false + ipSourceError = "" break } - if ipSourceError { - err := r.markFailed(instance, ctx, "Failed to get IP from any source") + if ipSourceError != "" { + err := r.markFailed(instance, ctx, ipSourceError) if err != nil { log.Error(err, "Failed to update IP resource") return ctrl.Result{}, err @@ -288,21 +288,21 @@ func (r *IPReconciler) getIPSource(ctx context.Context, source cfv1beta1.IPSpecI extractedIP = strings.TrimSpace(buf.String()) } - if source.ResponseTextRegex != "" { - re, err := regexp.Compile(source.ResponseTextRegex) + if source.ResponseRegex != "" { + re, err := regexp.Compile(source.ResponseRegex) if err != nil { - return "", fmt.Errorf("failed to compile regex %s: %s", source.ResponseTextRegex, err) + return "", fmt.Errorf("failed to compile regex %s: %s", source.ResponseRegex, err) } match := re.FindStringSubmatch(strings.TrimSpace(string(response))) if len(match) == 0 { - return "", fmt.Errorf("failed to extract IP from %s: %s", source.URL, string(response)) + return "", fmt.Errorf("failed to extract IP from %s. regex returned no matches", source.URL) } extractedIP = strings.TrimSpace(match[len(match)-1]) } if net.ParseIP(extractedIP) == nil { - return "", fmt.Errorf("failed to extract IP from %s: %s", source.URL, string(response)) + return "", fmt.Errorf("ip from source %s is invalid: %s", source.URL, extractedIP) } return extractedIP, nil diff --git a/docs/content/core_concept.md b/docs/content/core_concept.md index 7a56fd25..da654590 100644 --- a/docs/content/core_concept.md +++ b/docs/content/core_concept.md @@ -173,20 +173,20 @@ Default `interval` is set to 5 minutes. An `ipSource` can have the following keys: | Key | Description | Example | -| :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------ | +|:------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------| | url | URL to fetch IPv4 address | `https://ipecho.net` | | requestBody | Additional request body to send to the `url` | | | requestHeaders | Additional request headers to send to the `url`. The key will be passed as http header and the value will be passed as headers value | `Accept: application/json` | | requestHeadersSecretRef | Link to a `secret` with additional http headers. All secret keys will be passed as http header and the corresponding secret values will be passed as headers value | See example below | | requestMethod | HTTP request method. Possible values are `GET`, `POST`, `PUT` or `DELETE` | `GET` | -| responseTextRegex | If the IPv4 address must be extracted from the http response. Uses the default golang regex engine. | `\d{1,3}\.\d{1,3}.\.\d{1,3}\.\d{1,3}` | +| responseRegex | If the IPv4 address must be extracted from the http response. Uses the default golang regex engine. | `\d{1,3}\.\d{1,3}.\.\d{1,3}\.\d{1,3}` | | responseJSONPath | JSONPath to extract IPv4 address. Uses the kubectl jsonpath library. | `'{.ip}'` | -!!! warning "responseTextRegex" +!!! warning "responseRegex" Be aware that the http request will fetch the **complete html document** and not what you see in your browser! !!! note - If neither `responseJSONPath` nor `responseTextRegex` is set, cloudflare-operator will try to parse the **complete html document** as an IPv4 address. + If neither `responseJSONPath` nor `responseRegex` is set, cloudflare-operator will try to parse the **complete html document** as an IPv4 address. Examples: @@ -202,7 +202,7 @@ spec: type: dynamic IpSources: - url: https://ipecho.net - responseTextRegex: \d{1,3}\.\d{1,3}.\.\d{1,3}\.\d{1,3} + responseRegex: \d{1,3}\.\d{1,3}.\.\d{1,3}\.\d{1,3} - url: https://api.ipify.org?format=json responseJSONPath: '{.ip}' - url: https://checkip.amazonaws.com diff --git a/docs/content/installation.md b/docs/content/installation.md index f7a5cd60..be074c2f 100644 --- a/docs/content/installation.md +++ b/docs/content/installation.md @@ -32,7 +32,7 @@ cloudflare-operator requires a number of CRD resources, which must be installed Installing CRDs with kubectl: ```bash -kubectl apply -f https://github.com/containeroo/cloudflare-operator/releases/download/v0.2.0/crds.yaml +kubectl apply -f https://github.com/containeroo/cloudflare-operator/releases/download/v0.2.1/crds.yaml ``` 4. Install cloudflare-operator @@ -44,7 +44,7 @@ helm install \ cloudflare-operator containeroo/cloudflare-operator \ --namespace cloudflare-operator \ --create-namespace \ - --version v0.2.0 + --version v0.2.1 ``` A full list of available Helm values is on [cloudflare-operator’s ArtifactHub page](https://artifacthub.io/packages/helm/containeroo/cloudflare-operator). @@ -58,7 +58,7 @@ helm template \ cloudflare-operator containeroo/cloudflare-operator \ --namespace cloudflare-operator \ --create-namespace \ - --version v0.2.0 \ + --version v0.2.1 \ --set your.value=here ``` @@ -93,7 +93,7 @@ Finally, delete the cloudflare-operator CustomResourceDefinitions using the link This command will also remove installed cloudflare-operator objects. All cloudflare-operator resources will be removed by Kubernetes' garbage collector. ```bash -kubectl delete -f https://github.com/containeroo/cloudflare-operator/releases/download/v0.2.0/crds.yaml +kubectl delete -f https://github.com/containeroo/cloudflare-operator/releases/download/v0.2.1/crds.yaml ``` ## Namespace Stuck in Terminating State