Skip to content

Commit

Permalink
feat: add an option to reference IPMI creds via the secret refs
Browse files Browse the repository at this point in the history
v0.3 docs are a plain copy of v0.2 plus changed the
`Configuration/servers.md` for this feature.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
  • Loading branch information
smira authored and talos-bot committed Apr 22, 2021
1 parent 0613b8f commit 74d9bf9
Show file tree
Hide file tree
Showing 20 changed files with 1,249 additions and 8 deletions.
49 changes: 47 additions & 2 deletions app/metal-controller-manager/api/v1alpha1/server_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,66 @@
package v1alpha1

import (
"context"
"fmt"
"reflect"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.

// BMC defines data about how to talk to the node via ipmitool.
type BMC struct {
// BMC endpoint.
Endpoint string `json:"endpoint"`
User string `json:"user"`
Pass string `json:"pass"`
// BMC user value.
// +optional
User string `json:"user,omitempty"`
// Source for the user value. Cannot be used if User is not empty.
// +optional
UserFrom *CredentialSource `json:"userFrom,omitempty"`
// BMC password value.
// +optional
Pass string `json:"pass,omitempty"`
// Source for the password value. Cannot be used if Pass is not empty.
// +optional
PassFrom *CredentialSource `json:"passFrom,omitempty"`
}

// CredentialSource defines a reference to the credential value.
type CredentialSource struct {
// Selects a key of a secret in the cluster namespace
// +optional
SecretKeyRef *corev1.SecretKeySelector `json:"secretKeyRef,omitempty"`
}

// Resolve the value using the references.
func (source *CredentialSource) Resolve(ctx context.Context, reader client.Client) (string, error) {
if source == nil {
return "", nil
}

if source.SecretKeyRef == nil {
return "", fmt.Errorf("missing secretKeyRef")
}

var secrets corev1.Secret

if err := reader.Get(ctx, client.ObjectKey{Namespace: corev1.NamespaceDefault, Name: source.SecretKeyRef.Name}, &secrets); err != nil {
return "", fmt.Errorf("error getting secret %q: %w", source.SecretKeyRef.Name, err)
}

rawValue, ok := secrets.Data[source.SecretKeyRef.Key]
if !ok {
return "", fmt.Errorf("secret key %q is missing in secret %q", source.SecretKeyRef.Key, source.SecretKeyRef.Name)
}

return string(rawValue), nil
}

// ManagementAPI defines data about how to talk to the node via simple HTTP API.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,54 @@ spec:
description: BMC defines data about how to talk to the node via ipmitool.
properties:
endpoint:
description: BMC endpoint.
type: string
pass:
description: BMC password value.
type: string
passFrom:
description: Source for the password value. Cannot be used if Pass is not empty.
properties:
secretKeyRef:
description: Selects a key of a secret in the cluster namespace
properties:
key:
description: The key of the secret to select from. Must be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be defined
type: boolean
required:
- key
type: object
type: object
user:
description: BMC user value.
type: string
userFrom:
description: Source for the user value. Cannot be used if User is not empty.
properties:
secretKeyRef:
description: Selects a key of a secret in the cluster namespace
properties:
key:
description: The key of the secret to select from. Must be a valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be defined
type: boolean
required:
- key
type: object
type: object
required:
- endpoint
- pass
- user
type: object
configPatches:
items:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (r *ServerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
return ctrl.Result{}, err
}

mgmtClient, err := metal.NewManagementClient(&s.Spec)
mgmtClient, err := metal.NewManagementClient(ctx, r.Client, &s.Spec)
if err != nil {
log.Error(err, "failed to create management client")
r.Recorder.Event(serverRef, corev1.EventTypeWarning, "Server Management", fmt.Sprintf("Failed to initialize management client: %s.", err))
Expand Down
26 changes: 24 additions & 2 deletions app/metal-controller-manager/internal/power/metal/metal.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
package metal

import (
"context"

"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/talos-systems/sidero/app/metal-controller-manager/api/v1alpha1"
"github.com/talos-systems/sidero/app/metal-controller-manager/internal/power/api"
"github.com/talos-systems/sidero/app/metal-controller-manager/internal/power/ipmi"
Expand All @@ -22,10 +26,28 @@ type ManagementClient interface {
}

// NewManagementClient builds ManagementClient from the server spec.
func NewManagementClient(spec *v1alpha1.ServerSpec) (ManagementClient, error) {
func NewManagementClient(ctx context.Context, client client.Client, spec *v1alpha1.ServerSpec) (ManagementClient, error) {
switch {
case spec.BMC != nil:
return ipmi.NewClient(*spec.BMC)
var err error

bmcSpec := *spec.BMC

if bmcSpec.User == "" {
bmcSpec.User, err = bmcSpec.UserFrom.Resolve(ctx, client)
if err != nil {
return nil, err
}
}

if bmcSpec.Pass == "" {
bmcSpec.Pass, err = bmcSpec.PassFrom.Resolve(ctx, client)
if err != nil {
return nil, err
}
}

return ipmi.NewClient(bmcSpec)
case spec.ManagementAPI != nil:
return api.NewClient(*spec.ManagementAPI)
default:
Expand Down
78 changes: 78 additions & 0 deletions docs/website/content/docs/v0.3/Configuration/environments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
description: ""
weight: 1
---

# Environments

Environments are a custom resource provided by the Metal Controller Manager.
An environment is a codified description of what should be returned by the PXE server when a physical server attempts to PXE boot.

Especially important in the environment types are the kernel args.
From here, one can tweak the IP to the metadata server as well as various other kernel options that [Talos](https://www.talos.dev/docs/v0.8/introduction/getting-started/#kernel-parameters) and/or the Linux kernel supports.

Environments can be supplied to a given server either at the Server or the ServerClass level.
The hierarchy from most to least respected is:

- `.spec.environmentRef` provided at `Server` level
- `.spec.environmentRef` provided at `ServerClass` level
- `"default"` `Environment` created by administrator

A sample environment definition looks like this:

```yaml
apiVersion: metal.sidero.dev/v1alpha1
kind: Environment
metadata:
name: default
spec:
kernel:
url: "https://github.com/talos-systems/talos/releases/download/v0.8.1/vmlinuz-amd64"
sha512: ""
args:
- init_on_alloc=1
- init_on_free=1
- slab_nomerge
- pti=on
- consoleblank=0
- random.trust_cpu=on
- ima_template=ima-ng
- ima_appraise=fix
- ima_hash=sha512
- console=tty0
- console=ttyS1,115200n8
- earlyprintk=ttyS1,115200n8
- panic=0
- printk.devkmsg=on
- talos.platform=metal
- talos.config=http://$PUBLIC_IP:9091/configdata?uuid=
initrd:
url: "https://github.com/talos-systems/talos/releases/download/v0.8.1/initramfs-amd64.xz"
sha512: ""
```

Example of overriding `"default"` `Environment` at the `Server` level:

```yaml
apiVersion: metal.sidero.dev/v1alpha1
kind: Server
...
spec:
environmentRef:
namespace: default
name: boot
...
```

Example of overriding `"default"` `Environment` at the `ServerClass` level:

```yaml
apiVersion: metal.sidero.dev/v1alpha1
kind: ServerClass
...
spec:
environmentRef:
namespace: default
name: boot
...
```
30 changes: 30 additions & 0 deletions docs/website/content/docs/v0.3/Configuration/metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
description: ""
weight: 4
---

# Metadata

The Metadata server manages the Machine metadata.
In terms of Talos (the OS on which the Kubernetes cluster is formed), this is the
"[machine config](https://www.talos.dev/docs/v0.8/reference/configuration/)",
which is used during the automated installation.

## Talos Machine Configuration

The configuration of each machine is constructed from a number of sources:

- The Talos bootstrap provider.
- The `Cluster` of which the `Machine` is a member.
- The `ServerClass` which was used to select the `Server` into the `Cluster`.
- Any `Server`-specific patches.

The base template is constructed from the Talos bootstrap provider, using data from the associated `Cluster` manifest.
Then, any configuration patches are applied from the `ServerClass` and `Server`.

Only configuration patches are allowed in the `ServerClass` and `Server` resources.
These patches take the form of an [RFC 6902](https://tools.ietf.org/html/rfc6902) JSON (or YAML) patch.
An example of the use of this patch method can be found in [Patching Guide](../../guides/patching/).

Also note that while a `Server` can be a member of any number of `ServerClass`es, only the `ServerClass` which is used to select the `Server` into the `Cluster` will be used for the generation of the configuration of the `Machine`.
In this way, `Servers` may have a number of different configuration patch sets based on which `Cluster` they are in at any given time.
33 changes: 33 additions & 0 deletions docs/website/content/docs/v0.3/Configuration/serverclasses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
description: ""
weight: 3
---

# Server Classes

Server classes are a way to group distinct server resources.
The "qualifiers" key allows the administrator to specify criteria upon which to group these servers.
There are currently three keys: `cpu`, `systemInformation`, and `labelSelectors`.
Each of these keys accepts a list of entries.
The top level keys are a "logical AND", while the lists under each key are a "logical OR".
Qualifiers that are not specified are not evaluated.

An example:

```yaml
apiVersion: metal.sidero.dev/v1alpha1
kind: ServerClass
metadata:
name: default
spec:
qualifiers:
cpu:
- manufacturer: Intel(R) Corporation
version: Intel(R) Atom(TM) CPU C3558 @ 2.20GHz
- manufacturer: Advanced Micro Devices, Inc.
version: AMD Ryzen 7 2700X Eight-Core Processor
labelSelectors:
- "my-server-label": "true"
```

Servers would only be added to the above class if they had _EITHER_ CPU info, _AND_ the label associated with the server resource.
Loading

0 comments on commit 74d9bf9

Please sign in to comment.