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

Add Lima provider #9

Merged
merged 3 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
18 changes: 18 additions & 0 deletions assets/configs/config.example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
providers:
- name: "hetzner"
token: "your-provider-token-here"
location: "hel1"
- name: "lima"
token: "your-provider-token-here"
location: "us-east-1"
dns:
provider: "cloudflare"
domain: "your-domain-here"
token: "your-dns-token-here"
zone_id: "your-zone-id-here"

email: "[email protected]"
organization: "your-organization-name"
owner: "your-name"
outputformat: ""
loglevel: ""
4 changes: 2 additions & 2 deletions assets/playbooks/k3s.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
- name: Install K3s agent
ansible.builtin.shell: /tmp/k3s_install.sh
environment:
K3S_URL: "https://{{ hostvars[groups['control_plane'][0]]['inventory_hostname'] }}:6443"
K3S_URL: "https://{{ hostvars[groups['control_plane'][0]]['ansible_host'] }}:6443"
K3S_TOKEN: "{{ hostvars[groups['control_plane'][0]]['node_token']['stdout'] }}"

- name: Configure kubectl on control plane
Expand Down Expand Up @@ -59,7 +59,7 @@
ansible.builtin.replace:
path: /home/{{ ansible_user }}/.kube/config
regexp: "https://127.0.0.1:6443"
replace: "https://{{ inventory_hostname }}:6443"
replace: "https://{{ ansible_host }}:6443"

- name: Fetch kubeconfig to Ansible control host
ansible.builtin.fetch:
Expand Down
5 changes: 5 additions & 0 deletions assets/playbooks/prerequisites.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
path: /etc/cloud/cloud.cfg
line: "preserve_hostname: true"

- name: Remove systemd-timesyncd package # conflicts with ntp
ansible.builtin.package:
name: systemd-timesyncd
state: absent

# Control plane specific tasks
- name: Install base packages
ansible.builtin.package:
Expand Down
52 changes: 26 additions & 26 deletions assets/templates/lab.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,32 @@ spec:
provider: hetzner
location: nbg1
servers:
- name: cp
type: cx22
image: ubuntu-24.04
- name: node-01
type: cx22
image: ubuntu-24.04
- name: cp
type: cx22
image: ubuntu-24.04
- name: node-01
type: cx22
image: ubuntu-24.04
volumes:
- name: volume-01
server: node-01
size: 100
automount: false
format: xfs
- name: volume-02
server: node-01
size: 100
automount: false
format: xfs
- name: volume-03
server: node-01
size: 100
automount: false
format: xfs
- name: volume-04
server: node-01
size: 100
automount: false
format: xfs
- name: volume-01
server: node-01
size: 200
automount: false
format: xfs
- name: volume-02
server: node-01
size: 200
automount: false
format: xfs
- name: volume-03
server: node-01
size: 200
automount: false
format: xfs
- name: volume-04
server: node-01
size: 200
automount: false
format: xfs
ansible:
playbook: site.yml
24 changes: 19 additions & 5 deletions cmd/create_lab.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ func NewCreateLabCmd() *cobra.Command {
if err != nil {
return fmt.Errorf("error parsing lab template: %w", err)
}
if provider != "" {
lab.Spec.Provider = provider // override the provider in the template
}
_, err = createLab(lab)
if err != nil {
return fmt.Errorf("error creating lab: %w", err)
Expand All @@ -46,7 +49,7 @@ func NewCreateLabCmd() *cobra.Command {

defaultTemplate := filepath.Join(os.Getenv("HOME"), config.DefaultConfigDir, config.DefaultTemplateDir, "lab.yaml")
cmd.Flags().StringVar(&template, "template", defaultTemplate, "lab template to use")
cmd.Flags().StringVar(&provider, "provider", config.DefaultProvider, "provider to use")
cmd.Flags().StringVar(&provider, "provider", "", "provider to use")
cmd.Flags().StringVar(&location, "location", config.DefaultLocation, "location to use")
cmd.Flags().StringVar(&ttl, "ttl", config.DefaultTTL, "ttl to use")
cmd.Flags().StringVar(&playbook, "playbook", "site.yml", "playbook to use")
Expand All @@ -69,7 +72,16 @@ func createLab(lab *types.Lab) (*types.Lab, error) {
}
lab.ObjectMeta.Labels["delete_after"] = timeutil.FormatDeleteAfter(time.Now().Add(duration))

fmt.Printf("Lab %s: Creating lab resources on the cloud...\n", lab.ObjectMeta.Name)
err = initProvider(lab.Spec.Provider)
if err != nil {
return nil, fmt.Errorf("failed to initialize provider: %w", err)
}
err = initLabManager()
if err != nil {
return nil, fmt.Errorf("failed to initialize lab manager: %w", err)
}

fmt.Printf("Lab %s: Creating lab resources using provider %s...\n", lab.ObjectMeta.Name, lab.Spec.Provider)
labSvc.Logger.Info("Creating new lab",
"name", lab.ObjectMeta.Name,
"nodes", len(lab.Spec.Servers))
Expand All @@ -84,9 +96,11 @@ func createLab(lab *types.Lab) (*types.Lab, error) {
}
lab.Status = labUpdated.Status

fmt.Printf("Lab %s: Creating DNS records...\n", lab.ObjectMeta.Name)
if err := addDNSRecords(lab); err != nil {
return nil, err
if lab.Spec.Provider != "lima" { // we don't need DNS records for local VMs
fmt.Printf("Lab %s: Creating DNS records...\n", lab.ObjectMeta.Name)
if err := addDNSRecords(lab); err != nil {
return nil, err
}
}
fmt.Printf("Lab %s: Creating ansible inventory file...\n", lab.ObjectMeta.Name)
err = labSvc.CreateAnsibleInventoryFile(lab)
Expand Down
46 changes: 29 additions & 17 deletions cmd/create_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func NewCreateServerCmd() *cobra.Command {
sshKeyNames []string
serverType string
image string
provider string
location string
ttl string
labels map[string]string
Expand All @@ -42,7 +43,7 @@ func NewCreateServerCmd() *cobra.Command {
ServerType: serverType,
Image: image,
Location: location,
Provider: cfg.Provider.Name,
Provider: provider,
SSHKeyNames: sshKeyNames,
},
}
Expand All @@ -58,6 +59,7 @@ func NewCreateServerCmd() *cobra.Command {
cmd.Flags().StringSliceVar(&sshKeyNames, "ssh-keys", []string{}, "SSH key names to use; if not provided, the admin key will be created")
cmd.Flags().StringVar(&serverType, "type", config.DefaultServerType, "Server type")
cmd.Flags().StringVar(&image, "image", config.DefaultImage, "Server image")
cmd.Flags().StringVar(&provider, "provider", config.DefaultProvider, "Server provider")
cmd.Flags().StringVar(&location, "location", config.DefaultLocation, "Server location")
cmd.Flags().StringVar(&ttl, "ttl", config.DefaultTTL, "Server TTL")
cmd.Flags().StringToStringVar(&labels, "labels", map[string]string{}, "Server labels")
Expand All @@ -66,6 +68,12 @@ func NewCreateServerCmd() *cobra.Command {
}

func createServer(server *types.Server) (*types.Server, error) {
err := initProvider(server.Spec.Provider)
if err != nil {
return nil, fmt.Errorf("failed to initialize provider: %w", err)
}

var sshKeys []*types.SSHKey
sshManager := ssh.NewManager(cfg)
// no ssh keys provided, use the admin key
if len(server.Spec.SSHKeyNames) == 0 {
Expand All @@ -74,10 +82,11 @@ func createServer(server *types.Server) (*types.Server, error) {
server.Spec.SSHKeyNames = []string{serverKeyName}
}
// Access fields using map syntax
fmt.Printf("Creating server %s with type %s, image %s, location %s, ssh keys %v\n",
fmt.Printf("Creating server %s with type %s, image %s, provider %s, location %s, ssh keys %v\n",
server.ObjectMeta.Name,
server.Spec.ServerType,
server.Spec.Image,
server.Spec.Provider,
server.Spec.Location,
server.Spec.SSHKeyNames)

Expand All @@ -94,30 +103,33 @@ func createServer(server *types.Server) (*types.Server, error) {
labels["delete_after"] = timeutil.FormatDeleteAfter(time.Now().Add(duration))
labels["owner"] = labelutil.SanitizeValue(cfg.Owner)

// create the ssh keys locally
for _, sshKeyName := range server.Spec.SSHKeyNames {
_, err := sshManager.CreateLocalKeyPair(sshKeyName)
if server.Spec.Provider != "lima" {
// create the ssh keys locally
for _, sshKeyName := range server.Spec.SSHKeyNames {
_, err := sshManager.CreateLocalKeyPair(sshKeyName)
if err != nil {
return nil, fmt.Errorf("failed to create local ssh key: %w", err)
}
}
sshKeys, err = providerSvc.KeyNamesToSSHKeys(server.Spec.SSHKeyNames, options.SSHKeyCreateOpts{
Labels: labels,
})
if err != nil {
return nil, fmt.Errorf("failed to create local ssh key: %w", err)
return nil, fmt.Errorf("failed to upload ssh keys to the cloud: %w", err)
}
}
sshKeys, err := providerSvc.KeyNamesToSSHKeys(server.Spec.SSHKeyNames, options.SSHKeyCreateOpts{
Labels: labels,
})
if err != nil {
return nil, fmt.Errorf("failed to upload ssh keys to the cloud: %w", err)
}

// create the cloud init user data with the admin key
opts, err := providerSvc.ServerToCreateOpts(server)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to convert server to create opts: %w", err)
}
if server.Spec.Provider != "lima" {
opts.SSHKeys = sshKeys
}
opts.SSHKeys = sshKeys
result, err := providerSvc.CreateServer(opts)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to create server: %w", err)
}

return result, err
return result, nil
}
11 changes: 7 additions & 4 deletions cmd/create_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func NewCreateVolumeCmd() *cobra.Command {
labels map[string]string
automount bool
format string
provider string
)

cmd := &cobra.Command{
Expand All @@ -37,6 +38,7 @@ func NewCreateVolumeCmd() *cobra.Command {
Labels: labels,
},
Spec: types.VolumeSpec{
Provider: provider,
Size: size,
ServerID: server,
Labels: labels,
Expand All @@ -53,14 +55,15 @@ func NewCreateVolumeCmd() *cobra.Command {
cmd.Flags().StringToStringVar(&labels, "labels", map[string]string{}, "Volume labels")
cmd.Flags().BoolVar(&automount, "automount", false, "Automount the volume")
cmd.Flags().StringVar(&format, "format", config.DefaultVolumeFormat, "Volume format")
if err := cmd.MarkFlagRequired("server"); err != nil {
panic(err)
}

cmd.Flags().StringVar(&provider, "provider", config.DefaultProvider, "Provider")
return cmd
}

func createVolume(volume *types.Volume) error {
err := initProvider(volume.Spec.Provider)
if err != nil {
return fmt.Errorf("failed to initialize provider: %w", err)
}
fmt.Printf("Creating volume %s with size %d\n",
volume.ObjectMeta.Name,
volume.Spec.Size)
Expand Down
8 changes: 8 additions & 0 deletions cmd/delete_lab.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ func NewDeleteLabCmd() *cobra.Command {
return nil
}

err := initProvider(useProvider)
if err != nil {
return err
}
err = initLabManager()
if err != nil {
return err
}
// Delete the lab using lab manager
if err := labSvc.Delete(labName, skipTimeCheck); err != nil {
return fmt.Errorf("failed to delete lab: %w", err)
Expand Down
13 changes: 13 additions & 0 deletions cmd/get_lab.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ func NewGetLabCmd() *cobra.Command {
}

func listLabs() error {
err := initProvider(useProvider)
if err != nil {
return err
}
err = initLabManager()
if err != nil {
return err
}
labs, err := labSvc.List()
if err != nil {
return err
Expand Down Expand Up @@ -72,6 +80,11 @@ func listLabs() error {
}

func getLab(labName string) error {
err := initProvider(useProvider)
if err != nil {
return err
}
initLabManager()
fmt.Printf("Getting details for lab: %s\n", labName)
lab, err := labSvc.Get(labName)
if err != nil {
Expand Down
16 changes: 10 additions & 6 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,16 @@ func createConfig() error {
return nil
}
var defaultCfg config.Config
defaultCfg.Provider.Name = config.DefaultProvider
defaultCfg.Provider.Location = config.DefaultLocation
defaultCfg.Provider.Token = config.DefaultToken
defaultCfg.Provider.Credentials = map[string]string{
"username": config.DefaultCredentials,
"password": config.DefaultCredentials,
defaultCfg.Providers = []config.ProviderConfig{
{
Name: config.DefaultProvider,
Location: config.DefaultLocation,
Token: config.DefaultToken,
Credentials: map[string]string{
"username": config.DefaultCredentials,
"password": config.DefaultCredentials,
},
},
}
defaultCfg.DNS.Provider = config.DefaultDNSProvider
defaultCfg.DNS.ZoneID = config.DefaultDNSZoneID
Expand Down
Loading