Skip to content

Commit

Permalink
Merge pull request #162 from essentialkaos/develop
Browse files Browse the repository at this point in the history
Version 8.0.0
  • Loading branch information
andyone authored Nov 1, 2023
2 parents ae3102f + 11cd8fe commit 9e9ed0b
Show file tree
Hide file tree
Showing 29 changed files with 599 additions and 129 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ jobs:
- name: Check scripts with Shellcheck
uses: essentialkaos/shellcheck-action@v1
with:
files: scripts/*
files: scripts/bibop-dep scripts/bibop-docker scripts/bibop-entrypoint scripts/bibop-libtest-gen scripts/bibop-linked scripts/bibop-massive scripts/bibop-multi-check scripts/bibop-so-exported

Hadolint:
name: Hadolint
Expand Down
68 changes: 68 additions & 0 deletions COOKBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* [Common](#common)
* [`exit`](#exit)
* [`wait`](#wait)
* [`template`](#template)
* [Input/Output](#inputoutput)
* [`expect`](#expect)
* [`print`](#print)
Expand Down Expand Up @@ -490,6 +491,71 @@ command "echo 'ABCD'" "Simple echo command"

<a href="#"><img src="https://gh.kaos.st/separator.svg"/></a>

##### `template`

Creates a file from a template. If file already exists, it will be rewritten with the same UID, GID and mode.

_Note that 'template' action will not automatically backup the destination file if it already exists (use `backup` and `backup-restore` actions to preserve the original file). Also, the created file will remain after tests execution if it was created outside the working directory._

You can use the following methods in your templates:

- `Var "name"` - get variable value;
- `Is "name" "value"` - compare variable value.

Simple example:

```
# Sysconfig for postgresql service
PG_ENGINE=""
PG_POSTMASTER=""
{{ if not .Is "data_dir" "" }}
PG_DATA="{{ .Var "data_dir" }}/db"
{{ else }}
PG_DATA=""
{{ end }}
PG_LOG=""
PG_UPLOG=""
PG_SOCKET_DIR=""
TIMEOUT=""
DISABLE_AUTO_NUMA=""
```

**Syntax:** `template <source> <dest> [file-mode]`

**Arguments:**

* `source` - Path to template file (_String_)
* `dest` - Destination path (_String_)
* `file-mode` - Destination file mode (_Integer_) [Optional | 644]

**Negative form:** No

**Example:**

```yang
command "-" "Create configuration file"
template app.template /etc/myapp.conf
```

```yang
command "-" "Create configuration file"
template app.template /etc/myapp.conf 640
```

```yang
command "-" "Replace configuration file"
backup /etc/myapp.conf
template app.template /etc/myapp.conf 640
...
command "-" "Restore original configuration file"
backup-restore /etc/myapp.conf
```

<a href="#"><img src="https://gh.kaos.st/separator.svg"/></a>

#### Input/Output

Be aware that the output store limited to 2 Mb of data for each stream (`stdout` _and_ `stderr`). So if command generates lots of output data, it better to use `expect` action to working with the output.
Expand Down Expand Up @@ -563,6 +629,8 @@ command "echo 'ABCD'" "Simple echo command"
wait-output 10.0
```

<a href="#"><img src="https://gh.kaos.st/separator.svg"/></a>

##### `output-match`

Checks output with given [regular expression](https://en.wikipedia.org/wiki/Regular_expression).
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@

<br/>

`bibop` is a utility for testing command-line tools and daemons. Initially, this utility was created for testing packages from [ESSENTIAL KAOS Public Repository](https://pkgs.kaos.st).
`bibop` is a utility for testing command-line tools, packages and daemons. Initially, this utility was created for testing packages from [ESSENTIAL KAOS Public Repository](https://kaos.sh/kaos-repo).

Information about bibop recipe syntax you can find in our [cookbook](COOKBOOK.md).

### Usage demo

[![demo](https://gh.kaos.st/bibop-600.gif)](#usage-demo)
https://github.com/essentialkaos/bibop/assets/182020/c63dc147-fa44-40df-92e2-12f530c411af

### Installation

Expand Down Expand Up @@ -99,7 +99,8 @@ Usage: bibop {options} recipe
Options
--dry-run, -D Parse and validate recipe
--extra, -X Print the last lines from command output if action was failed
--extra, -X lines Number of output lines for failed action (default: 10)
--pause, -P duration Pause between commands in seconds
--list-packages, -L List required packages
--list-packages-flat, -L1 List required packages in one line (useful for scripts)
--variables, -V List recipe variables
Expand Down Expand Up @@ -128,6 +129,12 @@ Examples
bibop app.recipe --tag init,service
Run tests from app.recipe and execute commands with tags init and service
bibop app.recipe --extra
Run tests from app.recipe and print the last 10 lines from command output if action was failed
bibop app.recipe --extra=50
Run tests from app.recipe and print the last 50 lines from command output if action was failed
bibop app.recipe --format json 1> ~/results/app.json
Run tests from app.recipe and save result in JSON format
Expand Down
7 changes: 4 additions & 3 deletions action/auxi.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type OutputContainer struct {
// ////////////////////////////////////////////////////////////////////////////////// //

// escapeCharRegex is regexp for searching escape characters
var escapeCharRegex = regexp.MustCompile(`\x1b\[[0-9\;]+m`)
var escapeCharRegex = regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))")

// ////////////////////////////////////////////////////////////////////////////////// //

Expand Down Expand Up @@ -101,11 +101,11 @@ func (c *OutputContainer) Tail(lines int) string {
}

if line == lines {
return strings.TrimRight(string(data[i+1:]), " \n\r")
return strings.Trim(string(data[i+1:]), " \n\r")
}
}

return strings.TrimRight(string(data), " \n\r")
return strings.Trim(string(data), " \n\r")
}

// IsEmpty returns true if container is empty
Expand Down Expand Up @@ -156,5 +156,6 @@ func fmtValue(v string) string {

// sanitizeData removes escape characters
func sanitizeData(data []byte) []byte {
data = bytes.ReplaceAll(data, []byte("\r"), nil)
return escapeCharRegex.ReplaceAll(data, nil)
}
2 changes: 1 addition & 1 deletion action/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func Wait(action *recipe.Action) error {
return err
}

durSec = mathutil.BetweenF64(durSec, 0.01, 3600.0)
durSec = mathutil.Between(durSec, 0.01, 3600.0)

time.Sleep(timeutil.SecondsToDuration(durSec))

Expand Down
2 changes: 1 addition & 1 deletion action/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ func Cleanup(action *recipe.Action) error {
err = os.RemoveAll(obj)

if err != nil {
return fmt.Errorf("Can't remove object %q: %v", err)
return fmt.Errorf("Can't remove object %q: %v", obj, err)
}
}

Expand Down
13 changes: 2 additions & 11 deletions action/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,16 +297,7 @@ func HTTPSetHeader(action *recipe.Action) error {

// ////////////////////////////////////////////////////////////////////////////////// //

// isHTTPMethodSupported returns true if HTTP method is supported
func isHTTPMethodSupported(method string) bool {
switch method {
case req.GET, req.POST, req.DELETE, req.PUT, req.PATCH, req.HEAD:
return true
}

return false
}

// checkRequestData checks request data
func checkRequestData(method, payload string) error {
switch method {
case req.GET, req.POST, req.DELETE, req.PUT, req.PATCH, req.HEAD:
Expand Down Expand Up @@ -353,6 +344,6 @@ func makeHTTPRequest(action *recipe.Action, method, url, payload string) *req.Re

// parseJSONQuery converts json query to slice
func parseJSONQuery(q string) []string {
q = strings.Replace(q, "[", ".[", -1)
q = strings.ReplaceAll(q, "[", ".[")
return strings.Split(q, ".")
}
4 changes: 2 additions & 2 deletions action/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func Expect(action *recipe.Action, output *OutputContainer) error {
}

start := time.Now()
timeout = mathutil.BetweenF64(timeout, 0.01, 3600.0)
timeout = mathutil.Between(timeout, 0.01, 3600.0)
timeoutDur := timeutil.SecondsToDuration(timeout)

for range time.NewTicker(_DATA_READ_PERIOD).C {
Expand Down Expand Up @@ -98,7 +98,7 @@ func Input(action *recipe.Action, input *os.File, output *OutputContainer) error
}

if !strings.HasSuffix(text, "\n") {
text = text + "\n"
text += "\n"
}

output.Purge()
Expand Down
2 changes: 1 addition & 1 deletion action/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func WaitService(action *recipe.Action) error {
}

start := time.Now()
timeout = mathutil.BetweenF64(timeout, 0.01, 3600.0)
timeout = mathutil.Between(timeout, 0.01, 3600.0)
timeoutDur := timeutil.SecondsToDuration(timeout)

for range time.NewTicker(time.Second / 2).C {
Expand Down
8 changes: 4 additions & 4 deletions action/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func WaitPID(action *recipe.Action) error {
}

start := time.Now()
timeout = mathutil.BetweenF64(timeout, 0.01, 3600.0)
timeout = mathutil.Between(timeout, 0.01, 3600.0)
timeoutDur := timeutil.SecondsToDuration(timeout)

for range time.NewTicker(25 * time.Millisecond).C {
Expand Down Expand Up @@ -147,7 +147,7 @@ func WaitFS(action *recipe.Action) error {
}

start := time.Now()
timeout = mathutil.BetweenF64(timeout, 0.01, 3600.0)
timeout = mathutil.Between(timeout, 0.01, 3600.0)
timeoutDur := timeutil.SecondsToDuration(timeout)

for range time.NewTicker(25 * time.Millisecond).C {
Expand Down Expand Up @@ -203,7 +203,7 @@ func WaitConnect(action *recipe.Action) error {
}

start := time.Now()
timeout = mathutil.BetweenF64(timeout, 0.01, 3600.0)
timeout = mathutil.Between(timeout, 0.01, 3600.0)
timeoutDur := timeutil.SecondsToDuration(timeout)

for range time.NewTicker(25 * time.Millisecond).C {
Expand Down Expand Up @@ -264,7 +264,7 @@ func Connect(action *recipe.Action) error {
timeout = 1.0
}

timeout = mathutil.BetweenF64(timeout, 0.01, 3600.0)
timeout = mathutil.Between(timeout, 0.01, 3600.0)
timeoutDur := timeutil.SecondsToDuration(timeout)

conn, err := net.DialTimeout(network, address, timeoutDur)
Expand Down
114 changes: 114 additions & 0 deletions action/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package action

// ////////////////////////////////////////////////////////////////////////////////// //
// //
// Copyright (c) 2023 ESSENTIAL KAOS //
// Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0> //
// //
// ////////////////////////////////////////////////////////////////////////////////// //

import (
"fmt"
"os"
"strconv"
"text/template"

"github.com/essentialkaos/bibop/recipe"
)

// ////////////////////////////////////////////////////////////////////////////////// //

// varWrapper is a recipe wrapper for accessing variables
type varWrapper struct {
r *recipe.Recipe
}

// ////////////////////////////////////////////////////////////////////////////////// //

// Template is action processor for "template"
func Template(action *recipe.Action) error {
mode := uint64(0644)
source, err := action.GetS(0)

if err != nil {
return err
}

dest, err := action.GetS(1)

if err != nil {
return err
}

isSafePath, err := checkPathSafety(action.Command.Recipe, source)

if err != nil {
return err
}

if !isSafePath {
return fmt.Errorf("Action uses unsafe path (%s)", source)
}

isSafePath, err = checkPathSafety(action.Command.Recipe, dest)

if err != nil {
return err
}

if !isSafePath {
return fmt.Errorf("Action uses unsafe path (%s)", dest)
}

if action.Has(2) {
modeStr, _ := action.GetS(1)
mode, err = strconv.ParseUint(modeStr, 8, 32)

if err != nil {
return err
}
}

tmplData, err := os.ReadFile(source)

if err != nil {
fmt.Errorf("Can't read template %q: %v", source, err)
}

tmpl, err := template.New("").Parse(string(tmplData))

if err != nil {
return fmt.Errorf("Can't parse template %q: %v", source, err)
}

fd, err := os.OpenFile(dest, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.FileMode(mode))

if err != nil {
return fmt.Errorf("Can't save template data into %q: %v", dest, err)
}

defer fd.Close()

vw := &varWrapper{action.Command.Recipe}
err = tmpl.Execute(fd, vw)

if err != nil {
return fmt.Errorf("Can't render template %q: %v", source, err)
}

return nil
}

// ////////////////////////////////////////////////////////////////////////////////// //

// Var returns variable value
func (vw *varWrapper) Var(name string) string {
return vw.r.GetVariable(name, true)
}

// Is compares variable value
func (vw *varWrapper) Is(name, value string) bool {
return vw.r.GetVariable(name, true) == value
}

// ////////////////////////////////////////////////////////////////////////////////// //
2 changes: 1 addition & 1 deletion cli/barcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func getPackagesInfo(pkgs []string) ([]byte, error) {

// getRPMPackagesInfo returns info about installed packages from rpm
func getRPMPackagesInfo(pkgs []string) ([]byte, error) {
cmd := exec.Command("rpm", "-q", "--queryformat", "%{FILEMD5S}\n")
cmd := exec.Command("rpm", "-q", "--qf", "%{pkgid}\n")
cmd.Env = []string{"LC_ALL=C"}
cmd.Args = append(cmd.Args, pkgs...)

Expand Down
Loading

0 comments on commit 9e9ed0b

Please sign in to comment.