Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manage user data #2827

Merged
merged 4 commits into from
Dec 8, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/machine-config-operator/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func runStartCmd(cmd *cobra.Command, args []string) {
ctrlctx.ClientBuilder.ConfigClientOrDie(componentName),
ctrlctx.OpenShiftKubeAPIServerKubeNamespacedInformerFactory.Core().V1().ConfigMaps(),
ctrlctx.KubeInformerFactory.Core().V1().Nodes(),
ctrlctx.KubeMAOSharedInformer.Core().V1().Secrets(),
)

ctrlctx.NamespacedInformerFactory.Start(ctrlctx.Stop)
Expand All @@ -85,6 +86,7 @@ func runStartCmd(cmd *cobra.Command, args []string) {
ctrlctx.ConfigInformerFactory.Start(ctrlctx.Stop)
ctrlctx.OpenShiftKubeAPIServerKubeNamespacedInformerFactory.Start(ctrlctx.Stop)
ctrlctx.OperatorInformerFactory.Start(ctrlctx.Stop)
ctrlctx.KubeMAOSharedInformer.Start(ctrlctx.Stop)
close(ctrlctx.InformersStarted)

go controller.Run(2, ctrlctx.Stop)
Expand Down
9 changes: 9 additions & 0 deletions manifests/userdata_secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: v1
kind: Secret
metadata:
name: {{.Role}}-user-data-managed
namespace: openshift-machine-api
type: Opaque
data:
disableTemplating: "dHJ1ZQo="
userData: {{.PointerConfig}}
4 changes: 4 additions & 0 deletions pkg/controller/common/controller_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type ControllerContext struct {
APIExtInformerFactory apiextinformers.SharedInformerFactory
ConfigInformerFactory configinformers.SharedInformerFactory
OperatorInformerFactory operatorinformers.SharedInformerFactory
KubeMAOSharedInformer informers.SharedInformerFactory

AvailableResources map[schema.GroupVersionResource]bool

Expand Down Expand Up @@ -74,6 +75,8 @@ func CreateControllerContext(cb *clients.Builder, stop <-chan struct{}, targetNa
opt.FieldSelector = fields.OneTermEqualSelector("metadata.name", "kube-apiserver-to-kubelet-client-ca").String()
},
)
// this is needed to listen for changes in MAO user data secrets to re-apply the ones we define in the MCO (since we manage them)
kubeMAOSharedInformer := informers.NewFilteredSharedInformerFactory(kubeClient, resyncPeriod()(), "openshift-machine-api", nil)

// filter out CRDs that do not have the MCO label
assignFilterLabels := func(opts *metav1.ListOptions) {
Expand Down Expand Up @@ -103,5 +106,6 @@ func CreateControllerContext(cb *clients.Builder, stop <-chan struct{}, targetNa
Stop: stop,
InformersStarted: make(chan struct{}),
ResyncPeriod: resyncPeriod(),
KubeMAOSharedInformer: kubeMAOSharedInformer,
}
}
35 changes: 35 additions & 0 deletions pkg/controller/common/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io/ioutil"
"net/url"
"reflect"
"sort"

Expand All @@ -29,6 +30,7 @@ import (
"github.com/ghodss/yaml"
"github.com/golang/glog"
"github.com/pkg/errors"
"github.com/vincent-petithory/dataurl"
kerr "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand Down Expand Up @@ -131,6 +133,39 @@ func MergeMachineConfigs(configs []*mcfgv1.MachineConfig, osImageURL string) (*m
}, nil
}

// PointerConfig generates the stub ignition for the machine to boot properly
// NOTE: If you change this, you also need to change the pointer configuration in openshift/installer, see
// https://github.com/openshift/installer/blob/master/pkg/asset/ignition/machine/node.go#L20
func PointerConfig(ignitionHost string, rootCA []byte) (ign2types.Config, error) {
zaneb marked this conversation as resolved.
Show resolved Hide resolved
configSourceURL := &url.URL{
Scheme: "https",
Host: ignitionHost,
Path: "/config/{{.Role}}",
}
// we do decoding here as curly brackets are escaped to %7B and breaks golang's templates
ignitionHostTmpl, err := url.QueryUnescape(configSourceURL.String())
if err != nil {
return ign2types.Config{}, err
}
return ign2types.Config{
Ignition: ign2types.Ignition{
Version: ign2types.MaxVersion.String(),
Config: ign2types.IgnitionConfig{
Append: []ign2types.ConfigReference{{
Source: ignitionHostTmpl,
}},
},
Security: ign2types.Security{
TLS: ign2types.TLS{
CertificateAuthorities: []ign2types.CaReference{{
Source: dataurl.EncodeBytes(rootCA),
}},
},
},
},
}, nil
}

// NewIgnConfig returns an empty ignition config with version set as latest version
func NewIgnConfig() ign3types.Config {
return ign3types.Config{
Expand Down
98 changes: 64 additions & 34 deletions pkg/operator/assets/bindata.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/operator/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func RenderBootstrap(
templatectrl.BaremetalRuntimeCfgKey: imgs.BaremetalRuntimeCfg,
}

config := getRenderConfig("", string(filesData[kubeAPIServerServingCA]), spec, &imgs.RenderConfigImages, infra.Status.APIServerInternalURL)
config := getRenderConfig("", string(filesData[kubeAPIServerServingCA]), spec, &imgs.RenderConfigImages, infra.Status.APIServerInternalURL, nil)

manifests := []manifest{
{
Expand Down
5 changes: 5 additions & 0 deletions pkg/operator/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ type Operator struct {
oseKubeAPIListerSynced cache.InformerSynced
nodeListerSynced cache.InformerSynced
dnsListerSynced cache.InformerSynced
maoSecretInformerSynced cache.InformerSynced

// queue only ever has one item, but it has nice error handling backoff/retry semantics
queue workqueue.RateLimitingInterface
Expand Down Expand Up @@ -134,6 +135,7 @@ func New(
configClient configclientset.Interface,
oseKubeAPIInformer coreinformersv1.ConfigMapInformer,
nodeInformer coreinformersv1.NodeInformer,
maoSecretInformer coreinformersv1.SecretInformer,
) *Operator {
eventBroadcaster := record.NewBroadcaster()
eventBroadcaster.StartLogging(glog.Infof)
Expand Down Expand Up @@ -168,6 +170,7 @@ func New(
oseKubeAPIInformer.Informer(),
nodeInformer.Informer(),
dnsInformer.Informer(),
maoSecretInformer.Informer(),
} {
i.AddEventHandler(optr.eventHandler())
}
Expand All @@ -189,6 +192,7 @@ func New(
optr.nodeLister = nodeInformer.Lister()
optr.nodeListerSynced = nodeInformer.Informer().HasSynced

optr.maoSecretInformerSynced = maoSecretInformer.Informer().HasSynced
optr.serviceAccountInformerSynced = serviceAccountInfomer.Informer().HasSynced
optr.clusterRoleInformerSynced = clusterRoleInformer.Informer().HasSynced
optr.clusterRoleBindingInformerSynced = clusterRoleBindingInformer.Informer().HasSynced
Expand Down Expand Up @@ -237,6 +241,7 @@ func (optr *Operator) Run(workers int, stopCh <-chan struct{}) {
optr.clusterCmListerSynced,
optr.serviceAccountInformerSynced,
optr.clusterRoleInformerSynced,
optr.maoSecretInformerSynced,
optr.clusterRoleBindingInformerSynced,
optr.networkListerSynced,
optr.proxyListerSynced,
Expand Down
49 changes: 41 additions & 8 deletions pkg/operator/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,46 @@ type renderConfig struct {
KubeAPIServerServingCA string
Infra configv1.Infrastructure
Constants map[string]string
PointerConfig string
}

func renderAsset(config *renderConfig, path string) ([]byte, error) {
objBytes, err := manifests.ReadFile(path)
type assetRenderer struct {
Path string
tmpl *template.Template
templateData string
}

func newAssetRenderer(path string) *assetRenderer {
return &assetRenderer{
Path: path,
tmpl: template.New(path),
}
}

func (a *assetRenderer) read() error {
objBytes, err := manifests.ReadFile(a.Path)
if err != nil {
return nil, fmt.Errorf("error getting asset %s: %v", path, err)
return fmt.Errorf("error getting asset %s: %v", a.Path, err)
}
a.templateData = string(objBytes)
return nil
}

func (a *assetRenderer) addTemplateFuncs() {
funcs := sprig.TxtFuncMap()
funcs["toYAML"] = toYAML
funcs["onPremPlatformAPIServerInternalIP"] = onPremPlatformAPIServerInternalIP
funcs["onPremPlatformIngressIP"] = onPremPlatformIngressIP
funcs["onPremPlatformShortName"] = onPremPlatformShortName
funcs["onPremPlatformKeepalivedEnableUnicast"] = onPremPlatformKeepalivedEnableUnicast

if config.Constants == nil {
config.Constants = constants.ConstantsByName
}
a.tmpl = a.tmpl.Funcs(funcs)
}

tmpl, err := template.New(path).Funcs(funcs).Parse(string(objBytes))
func (a *assetRenderer) render(config interface{}) ([]byte, error) {
tmpl, err := a.tmpl.Parse(a.templateData)
if err != nil {
return nil, fmt.Errorf("failed to parse asset %s: %v", path, err)
return nil, fmt.Errorf("failed to parse asset %s: %v", a.Path, err)
}

buf := new(bytes.Buffer)
Expand All @@ -63,6 +81,21 @@ func renderAsset(config *renderConfig, path string) ([]byte, error) {
return buf.Bytes(), nil
}

func renderAsset(config *renderConfig, path string) ([]byte, error) {
asset := newAssetRenderer(path)
if err := asset.read(); err != nil {
return nil, err
}

if config.Constants == nil {
config.Constants = constants.ConstantsByName
}

asset.addTemplateFuncs()

return asset.render(config)
}

func toYAML(i interface{}) []byte {
out, err := yaml.Marshal(i)
if err != nil {
Expand Down
80 changes: 78 additions & 2 deletions pkg/operator/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import (
"encoding/pem"
"fmt"
"io/ioutil"
"net"
"net/url"
"strconv"
"strings"
"time"

Expand All @@ -29,8 +32,10 @@ import (
"github.com/openshift/machine-config-operator/lib/resourceread"
"github.com/openshift/machine-config-operator/manifests"
mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1"
ctrlcommon "github.com/openshift/machine-config-operator/pkg/controller/common"
templatectrl "github.com/openshift/machine-config-operator/pkg/controller/template"
daemonconsts "github.com/openshift/machine-config-operator/pkg/daemon/constants"
"github.com/openshift/machine-config-operator/pkg/server"
"github.com/openshift/machine-config-operator/pkg/version"
)

Expand Down Expand Up @@ -274,11 +279,48 @@ func (optr *Operator) syncRenderConfig(_ *renderConfig) error {
templatectrl.BaremetalRuntimeCfgKey: imgs.BaremetalRuntimeCfg,
}

ignitionHost, err := getIgnitionHost(&infra.Status)
if err != nil {
return err
}

pointerConfig, err := ctrlcommon.PointerConfig(ignitionHost, rootCA)
if err != nil {
return err
}
pointerConfigData, err := json.Marshal(pointerConfig)
if err != nil {
return err
}

// create renderConfig
optr.renderConfig = getRenderConfig(optr.namespace, string(kubeAPIServerServingCABytes), spec, &imgs.RenderConfigImages, infra.Status.APIServerInternalURL)
optr.renderConfig = getRenderConfig(optr.namespace, string(kubeAPIServerServingCABytes), spec, &imgs.RenderConfigImages, infra.Status.APIServerInternalURL, pointerConfigData)
return nil
}

func getIgnitionHost(infraStatus *configv1.InfrastructureStatus) (string, error) {
internalURL := infraStatus.APIServerInternalURL
internalURLParsed, err := url.Parse(internalURL)
if err != nil {
return "", err
}
securePortStr := strconv.Itoa(server.SecurePort)
ignitionHost := fmt.Sprintf("%s:%s", internalURLParsed.Hostname(), securePortStr)
switch infraStatus.PlatformStatus.Type {
case configv1.BareMetalPlatformType:
ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.BareMetal.APIServerInternalIP, securePortStr)
case configv1.OpenStackPlatformType:
ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.OpenStack.APIServerInternalIP, securePortStr)
case configv1.OvirtPlatformType:
ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.Ovirt.APIServerInternalIP, securePortStr)
case configv1.VSpherePlatformType:
if infraStatus.PlatformStatus.VSphere.APIServerInternalIP != "" {
ignitionHost = net.JoinHostPort(infraStatus.PlatformStatus.VSphere.APIServerInternalIP, securePortStr)
}
}
return ignitionHost, nil
}

func (optr *Operator) syncCustomResourceDefinitions() error {
crds := []string{
"manifests/controllerconfig.crd.yaml",
Expand Down Expand Up @@ -322,6 +364,39 @@ func (optr *Operator) syncMachineConfigPools(config *renderConfig) error {
}
}

userDataTemplatePath := "manifests/userdata_secret.yaml"
pools, err := optr.mcpLister.List(labels.Everything())
if err != nil {
return err
}
// base64.StdEncoding.EncodeToString
for _, pool := range pools {
pointerConfigAsset := newAssetRenderer("pointer-config")
pointerConfigAsset.templateData = config.PointerConfig
pointerConfigData, err := pointerConfigAsset.render(struct{ Role string }{pool.Name})
if err != nil {
return err
}

userDataAsset := newAssetRenderer(userDataTemplatePath)
if err := userDataAsset.read(); err != nil {
return err
}
userDataAsset.addTemplateFuncs()
userdataBytes, err := userDataAsset.render(struct{ Role, PointerConfig string }{
pool.Name,
base64.StdEncoding.EncodeToString(pointerConfigData),
})
if err != nil {
return err
}
p := resourceread.ReadSecretV1OrDie(userdataBytes)
_, _, err = resourceapply.ApplySecret(optr.kubeClient.CoreV1(), p)
if err != nil {
return err
}
}

return nil
}

Expand Down Expand Up @@ -840,14 +915,15 @@ func (optr *Operator) getGlobalConfig() (*configv1.Infrastructure, *configv1.Net
return infra, network, proxy, dns, nil
}

func getRenderConfig(tnamespace, kubeAPIServerServingCA string, ccSpec *mcfgv1.ControllerConfigSpec, imgs *RenderConfigImages, apiServerURL string) *renderConfig {
func getRenderConfig(tnamespace, kubeAPIServerServingCA string, ccSpec *mcfgv1.ControllerConfigSpec, imgs *RenderConfigImages, apiServerURL string, pointerConfigData []byte) *renderConfig {
return &renderConfig{
TargetNamespace: tnamespace,
Version: version.Raw,
ControllerConfig: *ccSpec,
Images: imgs,
APIServerURL: apiServerURL,
KubeAPIServerServingCA: kubeAPIServerServingCA,
PointerConfig: string(pointerConfigData),
}
}

Expand Down