Skip to content

Commit bd11670

Browse files
Allow to add options to /etc/resolv.conf on worker nodes (#342)
* add systemd service to update resolv.conf according to resolvConfOptions field * added unit tests for EnsureAdditionalUnits and EnsureAdditionalFiles; minor refactoring * use []string for option list, fix newline for flatcar
1 parent 7fa624f commit bd11670

File tree

9 files changed

+353
-0
lines changed

9 files changed

+353
-0
lines changed

docs/usage-as-operator.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ machineImages:
6565
# serverGroupPolicies:
6666
# - soft-anti-affinity
6767
# - anti-affinity
68+
# resolvConfOptions:
69+
# - rotate
70+
# - timeout:1
6871
constraints:
6972
floatingPools:
7073
- name: fp-pool-1
@@ -121,6 +124,10 @@ omit `keystoneURL` and always specify `region`.
121124

122125
If Gardener creates and manages the router of a shoot cluster, it is additionally possible to specify that the [enable_snat](https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/resources/networking_router_v2#enable_snat) field is set to `true` via `useSNAT: true` in the `CloudProfileConfig`.
123126

127+
On some OpenStack enviroments, there may be the need to set options in the file `/etc/resolv.conf` on worker nodes.
128+
If the field `resolvConfOptions` is set, a systemd service will be installed which copies `/run/systemd/resolve/resolv.conf`
129+
on every change to `/etc/resolv.conf` and appends the given options.
130+
124131
## Example `CloudProfile` manifest
125132

126133
Please find below an example `CloudProfile` manifest:

hack/api-reference/api.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,18 @@ bool
216216
<p>ServerGroupPolicies specify the allowed server group policies for worker groups.</p>
217217
</td>
218218
</tr>
219+
<tr>
220+
<td>
221+
<code>resolvConfOptions</code></br>
222+
<em>
223+
[]string
224+
</em>
225+
</td>
226+
<td>
227+
<em>(Optional)</em>
228+
<p>ResolvConfOptions specifies options to be added to /etc/resolv.conf on workers</p>
229+
</td>
230+
</tr>
219231
</tbody>
220232
</table>
221233
<h3 id="openstack.provider.extensions.gardener.cloud/v1alpha1.ControlPlaneConfig">ControlPlaneConfig

pkg/apis/openstack/types_cloudprofile.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ type CloudProfileConfig struct {
5858
UseSNAT *bool
5959
// ServerGroupPolicies specify the allowed server group policies for worker groups.
6060
ServerGroupPolicies []string
61+
// ResolvConfOptions specifies options to be added to /etc/resolv.conf on workers
62+
ResolvConfOptions []string
6163
}
6264

6365
// Constraints is an object containing constraints for the shoots.

pkg/apis/openstack/v1alpha1/types_cloudprofile.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ type CloudProfileConfig struct {
6767
// ServerGroupPolicies specify the allowed server group policies for worker groups.
6868
// +optional
6969
ServerGroupPolicies []string `json:"serverGroupPolicies,omitempty"`
70+
// ResolvConfOptions specifies options to be added to /etc/resolv.conf on workers
71+
// +optional
72+
ResolvConfOptions []string `json:"resolvConfOptions,omitempty"`
7073
}
7174

7275
// Constraints is an object containing constraints for the shoots.

pkg/apis/openstack/v1alpha1/zz_generated.conversion.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/openstack/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/openstack/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/webhook/controlplane/ensurer.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ package controlplane
1717
import (
1818
"context"
1919
"fmt"
20+
"strings"
2021

22+
apisopenstack "github.com/gardener/gardener-extension-provider-openstack/pkg/apis/openstack"
23+
"github.com/gardener/gardener-extension-provider-openstack/pkg/apis/openstack/helper"
2124
"github.com/gardener/gardener-extension-provider-openstack/pkg/openstack"
2225

2326
"github.com/coreos/go-systemd/v22/unit"
@@ -28,6 +31,7 @@ import (
2831
"github.com/gardener/gardener/extensions/pkg/webhook/controlplane"
2932
"github.com/gardener/gardener/extensions/pkg/webhook/controlplane/genericmutator"
3033
v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants"
34+
extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1"
3135
kutil "github.com/gardener/gardener/pkg/utils/kubernetes"
3236
versionutils "github.com/gardener/gardener/pkg/utils/version"
3337
"github.com/go-logr/logr"
@@ -451,3 +455,128 @@ func (e *ensurer) EnsureKubeletCloudProviderConfig(ctx context.Context, gctx gco
451455
*data = string(secret.Data[openstack.CloudProviderConfigDataKey])
452456
return nil
453457
}
458+
459+
// EnsureAdditionalUnits ensures that additional required system units are added.
460+
func (e *ensurer) EnsureAdditionalUnits(ctx context.Context, gctx gcontext.GardenContext, new, old *[]extensionsv1alpha1.Unit) error {
461+
cloudProfileConfig, err := getCloudProfileConfig(ctx, gctx)
462+
if err != nil {
463+
return err
464+
}
465+
if len(getResolveConfOptions(cloudProfileConfig)) > 0 {
466+
e.addAdditionalUnitsForResolvConfOptions(new)
467+
}
468+
return nil
469+
}
470+
471+
// addAdditionalUnitsForResolvConfOptions installs a systemd service to update `resolv.conf`
472+
// after each change of `/run/systemd/resolve/resolv.conf`.
473+
func (e *ensurer) addAdditionalUnitsForResolvConfOptions(new *[]extensionsv1alpha1.Unit) {
474+
var (
475+
trueVar = true
476+
customPathContent = `[Path]
477+
PathChanged=/run/systemd/resolve/resolv.conf
478+
479+
[Install]
480+
WantedBy=multi-user.target
481+
`
482+
customUnitContent = `[Unit]
483+
Description=add options to /etc/resolv.conf after every change of /run/systemd/resolve/resolv.conf
484+
After=network.target
485+
StartLimitIntervalSec=0
486+
487+
[Service]
488+
Type=oneshot
489+
ExecStart=/opt/bin/update-resolv-conf.sh
490+
`
491+
)
492+
493+
extensionswebhook.AppendUniqueUnit(new, extensionsv1alpha1.Unit{
494+
Name: "update-resolv-conf.path",
495+
Enable: &trueVar,
496+
Content: &customPathContent,
497+
})
498+
extensionswebhook.AppendUniqueUnit(new, extensionsv1alpha1.Unit{
499+
Name: "update-resolv-conf.service",
500+
Enable: &trueVar,
501+
Content: &customUnitContent,
502+
})
503+
}
504+
505+
// EnsureAdditionalFiles ensures that additional required system files are added.
506+
func (e *ensurer) EnsureAdditionalFiles(ctx context.Context, gctx gcontext.GardenContext, new, old *[]extensionsv1alpha1.File) error {
507+
cloudProfileConfig, err := getCloudProfileConfig(ctx, gctx)
508+
if err != nil {
509+
return err
510+
}
511+
if options := getResolveConfOptions(cloudProfileConfig); len(options) > 0 {
512+
e.addAdditionalFilesForResolvConfOptions(options, new)
513+
}
514+
return nil
515+
}
516+
517+
// addAdditionalFilesForResolvConfOptions writes the script to update `/etc/resolv.conf` from
518+
// `/run/systemd/resolve/resolv.conf` and adds a options line to it.
519+
func (e *ensurer) addAdditionalFilesForResolvConfOptions(options []string, new *[]extensionsv1alpha1.File) {
520+
var (
521+
permissions int32 = 0755
522+
template = `#!/bin/sh
523+
524+
file=/run/systemd/resolve/resolv.conf
525+
tmp=/etc/resolv.conf.new
526+
dest=/etc/resolv.conf
527+
line=%q
528+
529+
if [ -f "$file" ]; then
530+
cp "$file" "$tmp"
531+
echo "" >> "$tmp"
532+
echo "# updated by update-resolv-conf.service (installed by gardener-extension-provider-openstack)" >> "$tmp"
533+
echo "$line" >> "$tmp"
534+
mv "$tmp" "$dest"
535+
fi`
536+
)
537+
538+
content := fmt.Sprintf(template, fmt.Sprintf("options %s", strings.Join(options, " ")))
539+
file := extensionsv1alpha1.File{
540+
Path: "/opt/bin/update-resolv-conf.sh",
541+
Permissions: &permissions,
542+
Content: extensionsv1alpha1.FileContent{
543+
Inline: &extensionsv1alpha1.FileContentInline{
544+
Encoding: "",
545+
Data: content,
546+
},
547+
},
548+
}
549+
appendUniqueFile(new, file)
550+
}
551+
552+
func getCloudProfileConfig(ctx context.Context, gctx gcontext.GardenContext) (*apisopenstack.CloudProfileConfig, error) {
553+
cluster, err := gctx.GetCluster(ctx)
554+
if err != nil {
555+
return nil, err
556+
}
557+
cloudProfileConfig, err := helper.CloudProfileConfigFromCluster(cluster)
558+
if err != nil {
559+
return nil, err
560+
}
561+
return cloudProfileConfig, nil
562+
}
563+
564+
func getResolveConfOptions(cloudProfileConfig *apisopenstack.CloudProfileConfig) []string {
565+
if cloudProfileConfig == nil {
566+
return nil
567+
}
568+
return cloudProfileConfig.ResolvConfOptions
569+
}
570+
571+
// appendUniqueFile appends a unit file only if it does not exist, otherwise overwrite content of previous files
572+
func appendUniqueFile(files *[]extensionsv1alpha1.File, file extensionsv1alpha1.File) {
573+
resFiles := make([]extensionsv1alpha1.File, 0, len(*files))
574+
575+
for _, f := range *files {
576+
if f.Path != file.Path {
577+
resFiles = append(resFiles, f)
578+
}
579+
}
580+
581+
*files = append(resFiles, file)
582+
}

0 commit comments

Comments
 (0)