Skip to content

Commit

Permalink
Configure DNS and NTP in machine allocation (#571)
Browse files Browse the repository at this point in the history
  • Loading branch information
simcod authored Nov 8, 2024
1 parent 3d4b264 commit 5ce407c
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 1 deletion.
6 changes: 6 additions & 0 deletions cmd/metal-api/internal/datastore/machine_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ func (_ *machineTestable) defaultBody(m *metal.Machine) *metal.Machine {
if m.Allocation.SSHPubKeys == nil {
m.Allocation.SSHPubKeys = []string{}
}
if m.Allocation.DNSServers == nil {
m.Allocation.DNSServers = metal.DNSServers{}
}
if m.Allocation.NTPServers == nil {
m.Allocation.NTPServers = metal.NTPServers{}
}
for i := range m.Allocation.MachineNetworks {
n := m.Allocation.MachineNetworks[i]
if n.Prefixes == nil {
Expand Down
59 changes: 59 additions & 0 deletions cmd/metal-api/internal/metal/machine.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package metal

import (
"errors"
"fmt"
"log/slog"
"net"
"net/netip"
"os"
"path/filepath"
"slices"
"strings"
"time"

"github.com/asaskevich/govalidator"
"github.com/dustin/go-humanize"
mn "github.com/metal-stack/metal-lib/pkg/net"
"github.com/samber/lo"
Expand Down Expand Up @@ -153,6 +156,8 @@ type MachineAllocation struct {
VPN *MachineVPN `rethinkdb:"vpn" json:"vpn"`
UUID string `rethinkdb:"uuid" json:"uuid"`
FirewallRules *FirewallRules `rethinkdb:"firewall_rules" json:"firewall_rules"`
DNSServers DNSServers `rethinkdb:"dns_servers" json:"dns_servers"`
NTPServers NTPServers `rethinkdb:"ntp_servers" json:"ntp_servers"`
}

type FirewallRules struct {
Expand All @@ -175,6 +180,18 @@ type IngressRule struct {
Comment string `rethinkdb:"comment" json:"comment"`
}

type DNSServers []DNSServer

type DNSServer struct {
IP string `rethinkdb:"ip" json:"ip" description:"ip address of this dns server"`
}

type NTPServers []NTPServer

type NTPServer struct {
Address string `address:"address" json:"address" description:"ip address or dns hostname of this ntp server"`
}

type Protocol string

const (
Expand Down Expand Up @@ -717,3 +734,45 @@ func (i *MachineIPMISuperUser) User() string {
func DisabledIPMISuperUser() MachineIPMISuperUser {
return MachineIPMISuperUser{}
}

func (d DNSServers) Validate() error {
if len(d) == 0 {
return nil
}

if len(d) > 3 {
return errors.New("please specify a maximum of three dns servers")
}

for _, dnsServer := range d {
_, err := netip.ParseAddr(dnsServer.IP)
if err != nil {
return fmt.Errorf("ip: %s for dns server not correct err: %w", dnsServer, err)
}
}
return nil
}

func (n NTPServers) Validate() error {
if len(n) == 0 {
return nil
}

if len(n) < 3 || len(n) > 5 {
return errors.New("please specify a minimum of 3 and a maximum of 5 ntp servers")
}

for _, ntpserver := range n {
if net.ParseIP(ntpserver.Address) != nil {
_, err := netip.ParseAddr(ntpserver.Address)
if err != nil {
return fmt.Errorf("ip: %s for ntp server not correct err: %w", ntpserver, err)
}
} else {
if !govalidator.IsDNSName(ntpserver.Address) {
return fmt.Errorf("dns name: %s for ntp server not correct", ntpserver)
}
}
}
return nil
}
2 changes: 2 additions & 0 deletions cmd/metal-api/internal/metal/partition.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ type Partition struct {
MgmtServiceAddress string `rethinkdb:"mgmtserviceaddr" json:"mgmtserviceaddr"`
PrivateNetworkPrefixLength uint8 `rethinkdb:"privatenetworkprefixlength" json:"privatenetworkprefixlength"`
Labels map[string]string `rethinkdb:"labels" json:"labels"`
DNSServers DNSServers `rethinkdb:"dns_servers" json:"dns_servers"`
NTPServers NTPServers `rethinkdb:"ntp_servers" json:"ntp_servers"`
}

// BootConfiguration defines the metal-hammer initrd, kernel and commandline
Expand Down
40 changes: 40 additions & 0 deletions cmd/metal-api/internal/service/machine-service.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ type machineAllocationSpec struct {
PlacementTags []string
EgressRules []metal.EgressRule
IngressRules []metal.IngressRule
DNSServers metal.DNSServers
NTPServers metal.NTPServers
}

// allocationNetwork is intermediate struct to create machine networks from regular networks during machine allocation
Expand Down Expand Up @@ -1143,6 +1145,40 @@ func createMachineAllocationSpec(ds *datastore.RethinkStore, machineRequest v1.M
return nil, fmt.Errorf("size:%s not found err:%w", sizeID, err)
}

partition, err := ds.FindPartition(partitionID)
if err != nil {
return nil, fmt.Errorf("partition:%s not found err:%w", partitionID, err)
}

var (
dnsServers = partition.DNSServers
ntpServers = partition.NTPServers
)
if len(machineRequest.DNSServers) != 0 {
dnsServers = metal.DNSServers{}
for _, s := range machineRequest.DNSServers {
dnsServers = append(dnsServers, metal.DNSServer{
IP: s.IP,
})
}
}
if len(machineRequest.NTPServers) != 0 {
ntpServers = []metal.NTPServer{}
for _, s := range machineRequest.NTPServers {
ntpServers = append(ntpServers, metal.NTPServer{
Address: s.Address,
})
}
}

if err := dnsServers.Validate(); err != nil {
return nil, err
}

if err := ntpServers.Validate(); err != nil {
return nil, err
}

return &machineAllocationSpec{
Creator: user.EMail,
UUID: uuid,
Expand All @@ -1164,6 +1200,8 @@ func createMachineAllocationSpec(ds *datastore.RethinkStore, machineRequest v1.M
PlacementTags: machineRequest.PlacementTags,
EgressRules: egress,
IngressRules: ingress,
DNSServers: dnsServers,
NTPServers: ntpServers,
}, nil
}

Expand Down Expand Up @@ -1247,6 +1285,8 @@ func allocateMachine(ctx context.Context, logger *slog.Logger, ds *datastore.Ret
VPN: allocationSpec.VPN,
FirewallRules: firewallRules,
UUID: uuid.New().String(),
DNSServers: allocationSpec.DNSServers,
NTPServers: allocationSpec.NTPServers,
}
rollbackOnError := func(err error) error {
if err != nil {
Expand Down
29 changes: 29 additions & 0 deletions cmd/metal-api/internal/service/partition-service.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,33 @@ func (r *partitionResource) createPartition(request *restful.Request, response *
commandLine = *requestPayload.PartitionBootConfiguration.CommandLine
}

var dnsServers metal.DNSServers
if len(requestPayload.DNSServers) != 0 {
for _, s := range requestPayload.DNSServers {
dnsServers = append(dnsServers, metal.DNSServer{
IP: s.IP,
})
}
}
var ntpServers metal.NTPServers
if len(requestPayload.NTPServers) != 0 {
for _, s := range requestPayload.NTPServers {
ntpServers = append(ntpServers, metal.NTPServer{
Address: s.Address,
})
}
}

if err := dnsServers.Validate(); err != nil {
r.sendError(request, response, httperrors.BadRequest(err))
return
}

if err := ntpServers.Validate(); err != nil {
r.sendError(request, response, httperrors.BadRequest(err))
return
}

p := &metal.Partition{
Base: metal.Base{
ID: requestPayload.ID,
Expand All @@ -219,6 +246,8 @@ func (r *partitionResource) createPartition(request *restful.Request, response *
KernelURL: kernelURL,
CommandLine: commandLine,
},
DNSServers: dnsServers,
NTPServers: ntpServers,
}

fqn := metal.TopicMachine.GetFQN(p.GetID())
Expand Down
30 changes: 30 additions & 0 deletions cmd/metal-api/internal/service/v1/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ type MachineAllocation struct {
VPN *MachineVPN `json:"vpn" description:"vpn connection info for machine" optional:"true"`
AllocationUUID string `json:"allocationuuid" description:"a unique identifier for this machine allocation, can be used to distinguish between machine allocations over time."`
FirewallRules *FirewallRules `json:"firewall_rules,omitempty" description:"a set of firewall rules to apply" optional:"true"`
DNSServers []DNSServer `json:"dns_servers,omitempty" description:"the dns servers used for the machine" optional:"true"`
NTPServers []NTPServer `json:"ntp_servers,omitempty" description:"the ntp servers used for the machine" optional:"true"`
}

type FirewallRules struct {
Expand Down Expand Up @@ -229,6 +231,8 @@ type MachineAllocateRequest struct {
Networks MachineAllocationNetworks `json:"networks" description:"the networks that this machine will be placed in." optional:"true"`
IPs []string `json:"ips" description:"the ips to attach to this machine additionally" optional:"true"`
PlacementTags []string `json:"placement_tags,omitempty" description:"by default machines are spread across the racks inside a partition for every project. if placement tags are provided, the machine candidate has an additional anti-affinity to other machines having the same tags"`
DNSServers []DNSServer `json:"dns_servers,omitempty" description:"the dns servers used for the machine" optional:"true"`
NTPServers []NTPServer `json:"ntp_servers,omitempty" description:"the ntp servers used for the machine" optional:"true"`
}

type MachineAllocationNetworks []MachineAllocationNetwork
Expand Down Expand Up @@ -332,6 +336,14 @@ type MachineIssue struct {
Details string `json:"details" description:"details of the issue"`
}

type DNSServer struct {
IP string `json:"ip" description:"ip address of this dns server"`
}

type NTPServer struct {
Address string `json:"address" description:"ip address or dns hostname of this ntp server"`
}

func NewMetalIPMI(r *MachineIPMI) metal.IPMI {
var chassisPartNumber string
if r.Fru.ChassisPartNumber != nil {
Expand Down Expand Up @@ -580,6 +592,22 @@ func NewMachineResponse(m *metal.Machine, s *metal.Size, p *metal.Partition, i *
}
}

var (
dnsServers []DNSServer
ntpServers []NTPServer
)

for _, s := range m.Allocation.DNSServers {
dnsServers = append(dnsServers, DNSServer{
IP: s.IP,
})
}
for _, s := range m.Allocation.NTPServers {
ntpServers = append(ntpServers, NTPServer{
Address: s.Address,
})
}

allocation = &MachineAllocation{
Creator: m.Allocation.Creator,
Created: m.Allocation.Created,
Expand All @@ -597,6 +625,8 @@ func NewMachineResponse(m *metal.Machine, s *metal.Size, p *metal.Partition, i *
VPN: NewMachineVPN(m.Allocation.VPN),
AllocationUUID: m.Allocation.UUID,
FirewallRules: firewallRules,
DNSServers: dnsServers,
NTPServers: ntpServers,
}

allocation.Reinstall = m.Allocation.Reinstall
Expand Down
20 changes: 20 additions & 0 deletions cmd/metal-api/internal/service/v1/partition.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ type PartitionBase struct {
MgmtServiceAddress *string `json:"mgmtserviceaddress" description:"the address to the management service of this partition" optional:"true"`
PrivateNetworkPrefixLength *int `json:"privatenetworkprefixlength" description:"the length of private networks for the machine's child networks in this partition, default 22" optional:"true" minimum:"16" maximum:"30"`
Labels map[string]string `json:"labels" description:"free labels that you associate with this partition" optional:"true"`
DNSServers []DNSServer `json:"dns_servers" description:"the dns servers for this partition" optional:"true"`
NTPServers []NTPServer `json:"ntp_servers" description:"the ntp servers for this partition" optional:"true"`
}

type PartitionBootConfiguration struct {
Expand Down Expand Up @@ -104,6 +106,22 @@ func NewPartitionResponse(p *metal.Partition) *PartitionResponse {
labels = p.Labels
}

var (
dnsServers []DNSServer
ntpServers []NTPServer
)

for _, s := range p.DNSServers {
dnsServers = append(dnsServers, DNSServer{
IP: s.IP,
})
}
for _, s := range p.NTPServers {
ntpServers = append(ntpServers, NTPServer{
Address: s.Address,
})
}

return &PartitionResponse{
Common: Common{
Identifiable: Identifiable{
Expand All @@ -117,6 +135,8 @@ func NewPartitionResponse(p *metal.Partition) *PartitionResponse {
PartitionBase: PartitionBase{
MgmtServiceAddress: &p.MgmtServiceAddress,
PrivateNetworkPrefixLength: &prefixLength,
DNSServers: dnsServers,
NTPServers: ntpServers,
},
PartitionBootConfiguration: PartitionBootConfiguration{
ImageURL: &p.BootConfiguration.ImageURL,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
Expand Down
Loading

0 comments on commit 5ce407c

Please sign in to comment.