Skip to content

Commit

Permalink
Merge pull request #164 from k0sproject/exec-runner-refactor
Browse files Browse the repository at this point in the history
Pretty much everything has been changed and touched.

Increased modularity and thus improved extendability, flexibility, testability and
component reusability. 

Context is finding its way in.  There is for example `h.ExecContext(ctx, ...)` and
`h.Connect(ctx)`.

Short summary of changes to usage:


Exec.Sudo() is gone. 

  // before:
  h.Exec("command", exec.Sudo())
  h.SudoFsys().OpenFile(...)
  // after:
  h.Sudo().Exec("command")
  h.Sudo().FS().OpenFile(...)

There's no more "Execf".

  // before
  h.Execf("command %s", shellescape.Quote(path))
  // after
  h.Exec(sh.Command("command", path)) // path is automatically quoted like in os/exec.

Configurers are gone. You may still need to implement them for your own usecases,
but they're no longer in rig. Instead, the functionality has been spread out to different
components and more stdlib `os` resembling functionality bundled with the `fs.FS`
implementation in `rig/remotefs`.

  // before
  h.Configurer.WriteFile("foo.txt", "content")
  // after
  h.FS().WriteFile("foo", []byte("content"), 0o600)

  // before
  h.Configurer.InstallPackages(h, "curl", "iptables")
  // after
  h.Sudo().PackageManager().Install(context.Background(), "curl", "iptables")

  // before
  h.Configurer.StartService(h, "foo")
  // after
  h.Service("foo").Start(context.Background())

A great chunk of replicated logic in each of the protocol client implementations was
moved to a centralized `cmd.Executor`. The protocols now only need to implement
a single `StartProcess` function, all of the output capturing, input relaying and so
on is done in the executor. 

There are a number of io.Writers and io.Readers now used internally for things that
were done with string manipulations before. Many of these are reusable for other stuff.

**TODO** - this commit does not include much refactoring for the connection setup
stuff, which has become quite difficult to maintain due to its complexity, especially
the SSH. That will be addressed in a separate PR before a final 2.0.0 is tagged.
  • Loading branch information
kke authored Mar 11, 2024
2 parents b4f7f79 + f1aa362 commit fc225b5
Show file tree
Hide file tree
Showing 182 changed files with 12,349 additions and 6,185 deletions.
Binary file added .github/logo.webp
Binary file not shown.
105 changes: 7 additions & 98 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,113 +4,22 @@ on: [pull_request]

jobs:

unit-linux:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Test
run: |
go mod download
go test -v ./...
integration-linux:
unit-test:
strategy:
fail-fast: false
matrix:
image:
- quay.io/k0sproject/bootloose-ubuntu20.04
- quay.io/k0sproject/bootloose-debian12
- quay.io/k0sproject/bootloose-alpine3.18
needs: unit-linux
runs-on: ubuntu-20.04
runs-on:
- ubuntu-latest
- windows-latest
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: install test dependencies
run: |
sudo apt-get update
sudo apt-get install expect

- name: Run integration tests
env:
LINUX_IMAGE: ${{ matrix.image }}
run: |
cd test
go mod download
make test
windows:
runs-on: windows-2022
steps:
- name: Set up WinRM
shell: pwsh
run: |
Set-Item WSMan:\localhost\Service\AllowUnencrypted -Value $True
Get-ChildItem WSMan:\Localhost\listener | Remove-Item -Recurse
Set-Item -Path WSMan:\LocalHost\MaxTimeoutms -Value '1800000'
Set-Item -Path WSMan:\LocalHost\Shell\MaxMemoryPerShellMB -Value '1024'
Set-Item -Path WSMan:\LocalHost\Service\AllowUnencrypted -Value 'false'
Set-Item -Path WSMan:\LocalHost\Service\Auth\Basic -Value 'true'
Set-Item -Path WSMan:\LocalHost\Service\Auth\CredSSP -Value 'true'
New-NetFirewallRule -Name "WINRM-HTTPS-In-TCP" `
-DisplayName "Windows Remote Management (HTTPS-In)" `
-Description "Inbound rule for Windows Remote Management via WS-Management. [TCP 5986]" `
-Group "Windows Remote Management" `
-Program "System" `
-Protocol TCP `
-LocalPort "5986" `
-Action Allow `
-Profile Domain,Private
$Hostname = [System.Net.Dns]::GetHostByName((hostname)).HostName.ToUpper()
$pfx = New-SelfSignedCertificate -CertstoreLocation Cert:\LocalMachine\My -DnsName $Hostname
$certThumbprint = $pfx.Thumbprint
$certSubjectName = $pfx.SubjectName.Name.TrimStart("CN = ").Trim()
New-Item -Path WSMan:\LocalHost\Listener -Address * -Transport HTTPS -Hostname $certSubjectName -CertificateThumbPrint $certThumbprint -Port "5986" -force
Restart-Service WinRM
$Username = "winrmuser"
$Password = ConvertTo-SecureString "Password123" -AsPlainText -Force
New-LocalUser -Name $Username -Password $Password -FullName "WinRM User" -Description "Local user for WinRM testing"
Add-LocalGroupMember -Group "Administrators" -Member $Username
$Credentials = New-Object System.Management.Automation.PSCredential ($Username, $Password)
# try it out:
$SessionOptions = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
$Session = New-PSSession -ComputerName "localhost" -Credential $Credentials -Authentication Basic -UseSSL:$true -SessionOption $SessionOptions
Invoke-Command -Session $Session -ScriptBlock { cmd.exe /c ver.exe }
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Unit test

- name: Test
run: |
go mod download
go test -v ./...
- name: Integration test
run: |
cd test
go mod download
go test -v ./ -args -protocol winrm -host 127.0.0.1 -port 5986 -user winrmuser -winrm-password Password123 -winrm-https
12 changes: 0 additions & 12 deletions .github/workflows/golangci-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,6 @@ jobs:
go-version-file: go.mod
check-latest: true

- name: Go modules cache
uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
~/.cache/go-build
~/Library/Caches/go-build
%LocalAppData%\go-build
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Check go.mod/go.sum to be consistent
run: go mod tidy -v && git diff --exit-code

Expand Down
99 changes: 99 additions & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Integration

on: [pull_request]

jobs:
integration-test-linux:
strategy:
fail-fast: false
matrix:
image:
- quay.io/k0sproject/bootloose-ubuntu20.04
- quay.io/k0sproject/bootloose-debian12
- quay.io/k0sproject/bootloose-alpine3.18
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: install test dependencies
run: |
sudo apt-get update
sudo apt-get install expect
- name: Run integration tests
env:
LINUX_IMAGE: ${{ matrix.image }}
run: |
cd test
go mod download
make test
windows:
runs-on: windows-2022
steps:
- name: Set up WinRM
shell: pwsh
run: |
Set-Item WSMan:\localhost\Service\AllowUnencrypted -Value $True
Get-ChildItem WSMan:\Localhost\listener | Remove-Item -Recurse
Set-Item -Path WSMan:\LocalHost\MaxTimeoutms -Value '1800000'
Set-Item -Path WSMan:\LocalHost\Shell\MaxMemoryPerShellMB -Value '1024'
Set-Item -Path WSMan:\LocalHost\Service\AllowUnencrypted -Value 'false'
Set-Item -Path WSMan:\LocalHost\Service\Auth\Basic -Value 'true'
Set-Item -Path WSMan:\LocalHost\Service\Auth\CredSSP -Value 'true'
New-NetFirewallRule -Name "WINRM-HTTPS-In-TCP" `
-DisplayName "Windows Remote Management (HTTPS-In)" `
-Description "Inbound rule for Windows Remote Management via WS-Management. [TCP 5986]" `
-Group "Windows Remote Management" `
-Program "System" `
-Protocol TCP `
-LocalPort "5986" `
-Action Allow `
-Profile Domain,Private
$Hostname = [System.Net.Dns]::GetHostByName((hostname)).HostName.ToUpper()
$pfx = New-SelfSignedCertificate -CertstoreLocation Cert:\LocalMachine\My -DnsName $Hostname
$certThumbprint = $pfx.Thumbprint
$certSubjectName = $pfx.SubjectName.Name.TrimStart("CN = ").Trim()
New-Item -Path WSMan:\LocalHost\Listener -Address * -Transport HTTPS -Hostname $certSubjectName -CertificateThumbPrint $certThumbprint -Port "5986" -force
Restart-Service WinRM
$Username = "winrmuser"
$Password = ConvertTo-SecureString "Password123" -AsPlainText -Force
New-LocalUser -Name $Username -Password $Password -FullName "WinRM User" -Description "Local user for WinRM testing"
Add-LocalGroupMember -Group "Administrators" -Member $Username
$Credentials = New-Object System.Management.Automation.PSCredential ($Username, $Password)
# try it out:
$SessionOptions = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
$Session = New-PSSession -ComputerName "localhost" -Credential $Credentials -Authentication Basic -UseSSL:$true -SessionOption $SessionOptions
Invoke-Command -Session $Session -ScriptBlock { cmd.exe /c ver.exe }
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Unit test
run: |
go test -v ./...
- name: Integration test
run: |
cd test
go mod download
go test -v ./ -args -protocol winrm -host 127.0.0.1 -port 5986 -user winrmuser -winrm-password Password123 -winrm-https
9 changes: 7 additions & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ run:
linters:
enable-all: true
disable:
- nonamedreturns
- exhaustivestruct
- exhaustruct
- forbidigo
- gochecknoglobals
- gochecknoinits
- godot
- gochecknoglobals
- godox
- golint
- interfacer
Expand Down Expand Up @@ -56,6 +56,11 @@ linters-settings:
- mu sync.Mutex
- wg sync.WaitGroup
- h Host
- fs FS
- fs remotefs.FS
- sb strings.Builder
- sb *strings.Builder
- s string
cyclop:
max-complexity: 12

Expand Down
75 changes: 15 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,79 +1,34 @@
**Note:** The main branch contains a work in progress for rig v2. If you are looking for the stable version, please browse the tags of v0.x releases.

### Rig

[![GoDoc](https://godoc.org/github.com/k0sproject/rig?status.svg)](https://godoc.org/github.com/k0sproject/rig)
[![Go Report Card](https://goreportcard.com/badge/github.com/k0sproject/rig)](https://goreportcard.com/report/github.com/k0sproject/rig)
[![Build Status](https://travis-ci.com/k0sproject/rig.svg?branch=main)](https://travis-ci.com/k0sproject/rig)
[![codecov](https://codecov.io/gh/k0sproject/rig/branch/main/graph/badge.svg)](https://codecov.io/gh/k0sproject/rig)

<img src=".github/logo.webp" alt="Rig" width="200" align="left"/>

A golang package for adding multi-protocol connectivity and multi-os operation functionality to your application's Host objects.

#### Design goals

Rig's intention is to be easy to use and extend.

It should be easy to add support for new operating systems and to add new commands to the multi-os support mechanism without breaking go's type checking.

All of the relevant structs have YAML tags and default values to make unmarshaling from YAML configurations as easy as possible.
It should be easy to add support for new operating systems and to add new components to the multi-os support mechanism without breaking type safety and testability.

#### Protocols

Currently rig comes with the most common ways to connect to hosts:

- SSH for connecting to hosts that accept SSH connections. **Pageant**
or [**openssh agent**](https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse)
- SSH for connecting to hosts that accept SSH connections. With ssh agent and config support and sane familiar defaults. Pageant
or [openssh agent](https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse)
can be used on Windows.
- OpenSSH for connecting to hosts using the system's own openssh "ssh" executable and utilizing session multiplexing for performance.
- WinRM as an alternative to SSH for windows hosts (SSH works too)
- Local for treating the localhost as it was one of the remote hosts
- Localhost for treating the local host as it was a remote host using go's os/exec.

#### Usage

The intended way to use rig is to embed the `rig.Connection` struct into your own.

Example:

```go
package main

import "github.com/k0sproject/rig"

type host struct {
rig.Connection
}

func main() {
h := host{
connection: rig.Connection{
SSH: &rig.SSH{
Address: 10.0.0.1
}
}
}

if err := h.Connect(); err != nil {
panic(err)
}

output, err := h.ExecOutput("ls -al")
if err != nil {
panic(err)
}
println(output)
}
```

But of course you can use it directly on its own too:

```go
package main

import "github.com/k0sproject/rig"

func main() {
h := rig.Connection{
SSH: &rig.SSH{
Address: 10.0.0.1
}
}

if err := h.Connect(); err != nil {
panic(err)
}
}
```
TBD - for now see godoc, tests and sources.

See more usage examples in the [examples/](examples/) directory.
Loading

0 comments on commit fc225b5

Please sign in to comment.