diff --git a/.drone.star b/.drone.star
index 9d0b21c6018..e2ac0e84940 100644
--- a/.drone.star
+++ b/.drone.star
@@ -151,6 +151,12 @@ config = {
"OCM_OCM_PROVIDER_AUTHORIZER_PROVIDERS_FILE": "%s" % dirs["ocmProviders"],
},
},
+ "cli": {
+ "suites": [
+ "cliCommands",
+ ],
+ "skip": False,
+ },
},
"apiTests": {
"numberOfParts": 10,
@@ -859,7 +865,7 @@ def localApiTestPipeline(ctx):
pipeline = {
"kind": "pipeline",
"type": "docker",
- "name": "localApiTests-%s-%s" % (suite, storage),
+ "name": "%s-Tests-%s-%s" % ("CLI" if name.startswith("cli") else "API", suite, storage),
"platform": {
"os": "linux",
"arch": "amd64",
diff --git a/tests/TestHelpers/CliHelper.php b/tests/TestHelpers/CliHelper.php
new file mode 100644
index 00000000000..bf281b5c772
--- /dev/null
+++ b/tests/TestHelpers/CliHelper.php
@@ -0,0 +1,45 @@
+
+ * @copyright Copyright (c) 2024 Sajan Gurung sajan@jankaritech.com
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License,
+ * as published by the Free Software Foundation;
+ * either version 3 of the License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see
+ *
+ */
+
+namespace TestHelpers;
+
+use GuzzleHttp\Exception\ConnectException;
+use GuzzleHttp\Exception\GuzzleException;
+use GuzzleHttp\Psr7\Request;
+use Psr\Http\Message\ResponseInterface;
+use TestHelpers\OcisConfigHelper;
+
+/**
+ * A helper class for running oCIS CLI commands
+ */
+class CliHelper {
+ /**
+ * @param array $body
+ *
+ * @return ResponseInterface
+ * @throws GuzzleException
+ */
+ public static function runCommand(array $body): ResponseInterface {
+ $url = OcisConfigHelper::getWrapperUrl() . "/command";
+ return OcisConfigHelper::sendRequest($url, "POST", \json_encode($body));
+ }
+}
diff --git a/tests/TestHelpers/OcisConfigHelper.php b/tests/TestHelpers/OcisConfigHelper.php
index aa9781d5820..70e49b01458 100644
--- a/tests/TestHelpers/OcisConfigHelper.php
+++ b/tests/TestHelpers/OcisConfigHelper.php
@@ -40,7 +40,7 @@ class OcisConfigHelper {
* @return ResponseInterface
* @throws GuzzleException
*/
- private static function sendRequest(
+ public static function sendRequest(
string $url,
string $method,
?string $body = ""
@@ -54,10 +54,18 @@ private static function sendRequest(
);
try {
- return $client->send($request);
+ $response = $client->send($request);
} catch (ConnectException $e) {
throw new \Error("Cannot connect to the ociswrapper at the moment, make sure that ociswrapper is running before proceeding with the test run.\n" . $e->getMessage());
+ } catch (GuzzleException $ex) {
+ $response = $ex->getResponse();
+
+ if ($response === null) {
+ throw $ex;
+ }
}
+
+ return $response;
}
/**
@@ -90,4 +98,22 @@ public static function rollbackOcis(): ResponseInterface {
$url = self::getWrapperUrl() . "/rollback";
return self::sendRequest($url, "DELETE");
}
+
+ /**
+ * @return ResponseInterface
+ * @throws GuzzleException
+ */
+ public static function stopOcis(): ResponseInterface {
+ $url = self::getWrapperUrl() . "/stop";
+ return self::sendRequest($url, "POST");
+ }
+
+ /**
+ * @return ResponseInterface
+ * @throws GuzzleException
+ */
+ public static function startOcis(): ResponseInterface {
+ $url = self::getWrapperUrl() . "/start";
+ return self::sendRequest($url, "POST");
+ }
}
diff --git a/tests/acceptance/config/behat.yml b/tests/acceptance/config/behat.yml
index 17c1604c12b..1644768d081 100644
--- a/tests/acceptance/config/behat.yml
+++ b/tests/acceptance/config/behat.yml
@@ -368,6 +368,14 @@ default:
- SharingNgContext:
- SpacesContext:
+ cliCommands:
+ paths:
+ - "%paths.base%/../features/cliCommands"
+ context: *common_ldap_suite_context
+ contexts:
+ - FeatureContext: *common_feature_context_params
+ - CliContext:
+
extensions:
rdx\behatvars\BehatVariablesExtension: ~
diff --git a/tests/acceptance/features/bootstrap/CliContext.php b/tests/acceptance/features/bootstrap/CliContext.php
new file mode 100644
index 00000000000..51a7c7e5af5
--- /dev/null
+++ b/tests/acceptance/features/bootstrap/CliContext.php
@@ -0,0 +1,122 @@
+
+ * @copyright Copyright (c) 2024 Sajan Gurung sajan@jankaritech.com
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License,
+ * as published by the Free Software Foundation;
+ * either version 3 of the License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see
+ *
+ */
+
+use Behat\Behat\Hook\Scope\BeforeScenarioScope;
+use Behat\Behat\Context\Context;
+use PHPUnit\Framework\Assert;
+use Psr\Http\Message\ResponseInterface;
+use TestHelpers\CliHelper;
+use TestHelpers\OcisConfigHelper;
+
+/**
+ * CLI context
+ */
+class CliContext implements Context {
+ private FeatureContext $featureContext;
+
+ /**
+ * @BeforeScenario
+ *
+ * @param BeforeScenarioScope $scope
+ *
+ * @return void
+ */
+ public function setUpScenario(BeforeScenarioScope $scope): void {
+ // Get the environment
+ $environment = $scope->getEnvironment();
+ // Get all the contexts you need in this context
+ $this->featureContext = $environment->getContext('FeatureContext');
+ }
+
+ /**
+ * @Given the administrator has stopped the server
+ *
+ * @return void
+ */
+ public function theAdministratorHasStoppedTheServer(): void {
+ $response = OcisConfigHelper::stopOcis();
+ $this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
+ }
+
+ /**
+ * @Given the administrator has started the server
+ *
+ * @return void
+ */
+ public function theAdministratorHasStartedTheServer(): void {
+ $response = OcisConfigHelper::startOcis();
+ $this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
+ }
+
+ /**
+ * @When the administrator resets the password of user :user to :password using the CLI
+ *
+ * @param string $user
+ * @param string $password
+ *
+ * @return void
+ */
+ public function theAdministratorResetsThePasswordOfUserUsingTheCLI(string $user, string $password): void {
+ $command = "idm resetpassword -u $user";
+ $body = [
+ "command" => $command,
+ "inputs" => [$password, $password]
+ ];
+
+ $this->featureContext->setResponse(CliHelper::runCommand($body));
+ $this->featureContext->updateUserPassword($user, $password);
+ }
+
+ /**
+ * @Then the command should be successful
+ *
+ * @return void
+ */
+ public function theCommandShouldBeSuccessful(): void {
+ $response = $this->featureContext->getResponse();
+ $this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
+
+ $jsonResponse = $this->featureContext->getJsonDecodedResponse($response);
+
+ Assert::assertSame("OK", $jsonResponse["status"]);
+ Assert::assertSame(0, $jsonResponse["exitCode"], "Expected exit code to be 0, but got " . $jsonResponse["exitCode"]);
+ }
+
+ /**
+ * @Then /^the command output (should|should not) contain "([^"]*)"$/
+ *
+ * @param string $shouldOrNot
+ * @param string $output
+ *
+ * @return void
+ */
+ public function theCommandOutputShouldContain(string $shouldOrNot, string $output): void {
+ $response = $this->featureContext->getResponse();
+ $jsonResponse = $this->featureContext->getJsonDecodedResponse($response);
+
+ if ($shouldOrNot === "should") {
+ Assert::assertStringContainsString($output, $jsonResponse["message"]);
+ } else {
+ Assert::assertStringNotContainsString($output, $jsonResponse["message"]);
+ }
+ }
+}
diff --git a/tests/acceptance/features/bootstrap/FeatureContext.php b/tests/acceptance/features/bootstrap/FeatureContext.php
index d558882431f..c85700f2710 100644
--- a/tests/acceptance/features/bootstrap/FeatureContext.php
+++ b/tests/acceptance/features/bootstrap/FeatureContext.php
@@ -43,6 +43,7 @@
use TestHelpers\GraphHelper;
use TestHelpers\WebDavHelper;
use TestHelpers\SettingsHelper;
+use TestHelpers\OcisConfigHelper;
require_once 'bootstrap.php';
@@ -655,6 +656,22 @@ public static function logStep(BeforeStepScope $scope): void {
HttpLogger::writeLog(HttpLogger::getScenarioLogPath(), $logMessage);
}
+ /**
+ * FIRST AfterScenario HOOK
+ *
+ * NOTE: This method is called after each scenario having the @env-config tag
+ * This ensures that the server is running for clean-up purposes
+ *
+ * @AfterScenario @env-config
+ *
+ * @return void
+ */
+ public function startOcisServer(): void {
+ $response = OcisConfigHelper::startOcis();
+ // 409 is returned if the server is already running
+ $this->theHTTPStatusCodeShouldBe([200, 409], 'Starting oCIS server', $response);
+ }
+
/**
* Get the externally-defined admin username, if any
*
@@ -1695,6 +1712,24 @@ public function getPasswordForUser(?string $userName): string {
return (string)$this->getActualPassword($this->regularUserPassword);
}
+ /**
+ * @param string $username
+ * @param string $password
+ *
+ * @return void
+ * @throws Exception
+ */
+ public function updateUserPassword(string $username, string $password): void {
+ $username = $this->normalizeUsername($username);
+ if ($username === $this->getAdminUsername()) {
+ $this->adminPassword = $password;
+ } elseif (\array_key_exists($username, $this->createdUsers)) {
+ $this->createdUsers[$username]['password'] = $password;
+ } else {
+ throw new Exception("User '$username' not found");
+ }
+ }
+
/**
* Get the display name of the user.
*
diff --git a/tests/acceptance/features/bootstrap/WebDav.php b/tests/acceptance/features/bootstrap/WebDav.php
index 235fa51a400..c4a5ad692aa 100644
--- a/tests/acceptance/features/bootstrap/WebDav.php
+++ b/tests/acceptance/features/bootstrap/WebDav.php
@@ -443,12 +443,13 @@ public function makeDavRequest(
* @param string $user
* @param string $folder
* @param bool|null $isGivenStep
+ * @param string|null $password
*
* @return ResponseInterface
* @throws JsonException | GuzzleException
* @throws GuzzleException | JsonException
*/
- public function createFolder(string $user, string $folder, ?bool $isGivenStep = false): ResponseInterface {
+ public function createFolder(string $user, string $folder, ?bool $isGivenStep = false, ?string $password = null): ResponseInterface {
$folder = '/' . \ltrim($folder, '/');
return $this->makeDavRequest(
$user,
@@ -459,7 +460,7 @@ public function createFolder(string $user, string $folder, ?bool $isGivenStep =
"files",
null,
false,
- null,
+ $password,
[],
null,
$isGivenStep
@@ -3349,6 +3350,31 @@ public function userShouldBeAbleToCreateFolder(string $user, string $destination
);
}
+ /**
+ * @Then user :user should be able to create folder :destination using password :password
+ *
+ * @param string $user
+ * @param string $destination
+ * @param string $password
+ *
+ * @return void
+ * @throws Exception
+ */
+ public function userShouldBeAbleToCreateFolderUsingPassword(string $user, string $destination, string $password):void {
+ $user = $this->getActualUsername($user);
+ $response = $this->createFolder($user, $destination, true, $password);
+ $this->theHTTPStatusCodeShouldBe(
+ ["201", "204"],
+ "HTTP status code was not 201 or 204 while trying to create folder '$destination' for user '$user'",
+ $response
+ );
+ $this->checkFileOrFolderExistsForUser(
+ $user,
+ "folder",
+ $destination
+ );
+ }
+
/**
* @Then user :user should not be able to create folder :destination
*
@@ -3369,6 +3395,26 @@ public function userShouldNotBeAbleToCreateFolder(string $user, string $destinat
);
}
+ /**
+ * @Then user :user should not be able to create folder :destination using password :password
+ *
+ * @param string $user
+ * @param string $destination
+ * @param string $password
+ *
+ * @return void
+ * @throws Exception
+ */
+ public function userShouldNotBeAbleToCreateFolderUsingPassword(string $user, string $destination, string $password):void {
+ $user = $this->getActualUsername($user);
+ $response = $this->createFolder($user, $destination, false, $password);
+ $this->theHTTPStatusCodeShouldBeBetween(400, 499, $response);
+ $this->checkFileOrFolderDoesNotExistsForUser(
+ $user,
+ "folder",
+ $destination
+ );
+ }
/**
* Old style chunking upload
*
diff --git a/tests/acceptance/features/cliCommands/resetUserPassword.feature b/tests/acceptance/features/cliCommands/resetUserPassword.feature
new file mode 100644
index 00000000000..c5a015c9d9a
--- /dev/null
+++ b/tests/acceptance/features/cliCommands/resetUserPassword.feature
@@ -0,0 +1,17 @@
+@env-config
+Feature: reset user password via CLI command
+
+
+ Scenario: reset user password
+ Given the user "Admin" has created a new user with the following attributes:
+ | userName | Alice |
+ | displayName | Alice Hansen |
+ | password | %alt1% |
+ And the administrator has stopped the server
+ When the administrator resets the password of user "Alice" to "newpass" using the CLI
+ Then the command should be successful
+ And the command output should contain "Password for user 'uid=Alice,ou=users,o=libregraph-idm' updated."
+ But the command output should not contain "Failed to update user password: entry does not exist"
+ And the administrator has started the server
+ And user "Alice" should be able to create folder "newFolder" using password "newpass"
+ But user "Alice" should not be able to create folder "anotherFolder" using password "%alt1%"
diff --git a/tests/ociswrapper/README.md b/tests/ociswrapper/README.md
index cd57f2b7e67..6186e272678 100644
--- a/tests/ociswrapper/README.md
+++ b/tests/ociswrapper/README.md
@@ -17,10 +17,13 @@ When run, **ociswrapper** starts an API server that exposes some endpoints to re
```bash
./bin/ociswrapper serve --bin=
```
+
To check other available options:
+
```bash
./bin/ociswrapper serve --help
```
+
```bash
--url string oCIS server url (default "https://localhost:9200")
--retry string Number of retries to start oCIS server (default "5")
@@ -51,9 +54,9 @@ Also, see `./bin/ociswrapper help` for more information.
Returns:
- * `200 OK` - oCIS is successfully reconfigured
- * `400 Bad Request` - request body is not a valid JSON object
- * `500 Internal Server Error` - oCIS server is not running
+ - `200 OK` - oCIS is successfully reconfigured
+ - `400 Bad Request` - request body is not a valid JSON object
+ - `500 Internal Server Error` - oCIS server is not running
2. `DELETE /rollback`
@@ -61,5 +64,63 @@ Also, see `./bin/ociswrapper help` for more information.
Returns:
- * `200 OK` - rollback is successful
- * `500 Internal Server Error` - oCIS server is not running
+ - `200 OK` - rollback is successful
+ - `500 Internal Server Error` - oCIS server is not running
+
+3. `POST /command`
+
+ Executes the provided command on the oCIS server. The body of the request should be a JSON object with the following structure:
+
+ ```yml
+ {
+ "command": "", # without the ocis binary. e.g. "list"
+ }
+ ```
+
+ If the command requires user input, the body of the request should be a JSON object with the following structure:
+
+ ```json
+ {
+ "command": "",
+ "inputs": ["value1"]
+ }
+ ```
+
+ Returns:
+
+ ```json
+ {
+ "status": "OK",
+ "exitCode": 0,
+ "message": ""
+ }
+ OR
+ {
+ "status": "ERROR",
+ "exitCode": ,
+ "message": ""
+ }
+ ```
+
+ - `200 OK` - command is successfully executed
+ - `400 Bad Request` - request body is not a valid JSON object
+ - `500 Internal Server Error`
+
+4. `POST /start`
+
+ Starts the oCIS server.
+
+ Returns:
+
+ - `200 OK` - oCIS server is started
+ - `409 Conflict` - oCIS server is already running
+ - `500 Internal Server Error` - Unable to start oCIS server
+
+5. `POST /stop`
+
+ Stops the oCIS server.
+
+ Returns:
+
+ - `200 OK` - oCIS server is stopped
+ - `500 Internal Server Error` - Unable to stop oCIS server
diff --git a/tests/ociswrapper/cmd/cmd.go b/tests/ociswrapper/cmd/cmd.go
index d949ddab3d5..0bc430b6214 100644
--- a/tests/ociswrapper/cmd/cmd.go
+++ b/tests/ociswrapper/cmd/cmd.go
@@ -36,7 +36,9 @@ func serveCmd() *cobra.Command {
ocisConfig.Set("adminUsername", cmd.Flag("admin-username").Value.String())
ocisConfig.Set("adminPassword", cmd.Flag("admin-password").Value.String())
- go ocis.Start(nil)
+ if cmd.Flag("skip-ocis-run").Value.String() == "false" {
+ go ocis.Start(nil)
+ }
go wrapper.Start(cmd.Flag("port").Value.String())
},
}
@@ -49,6 +51,7 @@ func serveCmd() *cobra.Command {
serveCmd.Flags().StringP("port", "p", wrapperConfig.Get("port"), "Wrapper API server port")
serveCmd.Flags().StringP("admin-username", "", "", "admin username for oCIS server")
serveCmd.Flags().StringP("admin-password", "", "", "admin password for oCIS server")
+ serveCmd.Flags().Bool("skip-ocis-run", false, "Skip running oCIS server")
return serveCmd
}
diff --git a/tests/ociswrapper/go.mod b/tests/ociswrapper/go.mod
index 999a42e6ab4..1c793d2eae7 100644
--- a/tests/ociswrapper/go.mod
+++ b/tests/ociswrapper/go.mod
@@ -3,6 +3,7 @@ module ociswrapper
go 1.20
require (
+ github.com/creack/pty v1.1.21 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
diff --git a/tests/ociswrapper/go.sum b/tests/ociswrapper/go.sum
index f3366a91aa3..ac2a55135de 100644
--- a/tests/ociswrapper/go.sum
+++ b/tests/ociswrapper/go.sum
@@ -1,4 +1,6 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
+github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
diff --git a/tests/ociswrapper/ocis/ocis.go b/tests/ociswrapper/ocis/ocis.go
index 60e0382d9c4..aa4a69dd997 100644
--- a/tests/ociswrapper/ocis/ocis.go
+++ b/tests/ociswrapper/ocis/ocis.go
@@ -2,8 +2,10 @@ package ocis
import (
"bufio"
+ "context"
"crypto/tls"
"fmt"
+ "io"
"net/http"
"os"
"os/exec"
@@ -16,13 +18,16 @@ import (
"ociswrapper/common"
"ociswrapper/log"
"ociswrapper/ocis/config"
+
+ "github.com/creack/pty"
)
var cmd *exec.Cmd
var retryCount = 0
var stopSignal = false
+var EnvConfigs = []string{}
-func Start(envMap map[string]any) {
+func Start(envMap []string) {
// wait for the log scanner to finish
var wg sync.WaitGroup
wg.Add(2)
@@ -33,14 +38,11 @@ func Start(envMap map[string]any) {
}
cmd = exec.Command(config.Get("bin"), "server")
- cmd.Env = os.Environ()
- var environments []string
- if envMap != nil {
- for key, value := range envMap {
- environments = append(environments, fmt.Sprintf("%s=%v", key, value))
- }
+ if envMap == nil {
+ cmd.Env = append(os.Environ(), EnvConfigs...)
+ } else {
+ cmd.Env = append(os.Environ(), envMap...)
}
- cmd.Env = append(cmd.Env, environments...)
logs, err := cmd.StderrPipe()
if err != nil {
@@ -111,21 +113,47 @@ func Start(envMap map[string]any) {
close(outChan)
}
-func Stop() {
+func Stop() (bool, string) {
log.Println("Stopping oCIS server...")
stopSignal = true
+ if cmd == nil {
+ return true, "oCIS server is not running"
+ }
+
err := cmd.Process.Signal(syscall.SIGINT)
if err != nil {
if !strings.HasSuffix(err.Error(), "process already finished") {
log.Fatalln(err)
+ } else {
+ return true, "oCIS server is already stopped"
}
}
cmd.Process.Wait()
- waitUntilCompleteShutdown()
+ success, message := waitUntilCompleteShutdown()
+
+ cmd = nil
+ return success, message
+}
+
+func Restart(envMap []string) (bool, string) {
+ Stop()
+
+ log.Println("Restarting oCIS server...")
+ common.Wg.Add(1)
+ go Start(envMap)
+
+ return WaitForConnection()
+}
+
+func IsOcisRunning() bool {
+ if cmd != nil {
+ return cmd.Process.Pid > 0
+ }
+ return false
}
-func listAllServices(startTime time.Time, timeout time.Duration) {
+func waitAllServices(startTime time.Time, timeout time.Duration) {
timeoutS := timeout * time.Second
c := exec.Command(config.Get("bin"), "list")
@@ -133,15 +161,15 @@ func listAllServices(startTime time.Time, timeout time.Duration) {
if err != nil {
if time.Since(startTime) <= timeoutS {
time.Sleep(500 * time.Millisecond)
- listAllServices(startTime, timeout)
+ waitAllServices(startTime, timeout)
}
return
}
log.Println("All services are up")
}
-func WaitForConnection() bool {
- listAllServices(time.Now(), 30)
+func WaitForConnection() (bool, string) {
+ waitAllServices(time.Now(), 30)
transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
@@ -168,7 +196,7 @@ func WaitForConnection() bool {
select {
case <-timeout:
log.Println(fmt.Sprintf("%v seconds timeout waiting for oCIS server", int64(timeoutValue.Seconds())))
- return false
+ return false, "Timeout waiting for oCIS server to start"
default:
req.Header.Set("X-Request-ID", "ociswrapper-"+strconv.Itoa(int(time.Now().UnixMilli())))
@@ -180,12 +208,12 @@ func WaitForConnection() bool {
}
log.Println("oCIS server is ready to accept requests")
- return true
+ return true, "oCIS server is up and running"
}
}
}
-func waitUntilCompleteShutdown() {
+func waitUntilCompleteShutdown() (bool, string) {
timeout := 30 * time.Second
startTime := time.Now()
@@ -200,17 +228,46 @@ func waitUntilCompleteShutdown() {
if time.Since(startTime) >= timeout {
log.Println(fmt.Sprintf("Unable to kill oCIS server after %v seconds", int64(timeout.Seconds())))
- break
+ return false, "Timeout waiting for oCIS server to stop"
}
}
+ return true, "oCIS server stopped successfully"
}
-func Restart(envMap map[string]any) bool {
- Stop()
+func RunCommand(command string, inputs []string) (int, string) {
+ logs := new(strings.Builder)
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
- log.Println("Restarting oCIS server...")
- common.Wg.Add(1)
- go Start(envMap)
+ // build the command
+ cmdArgs := strings.Split(command, " ")
+ c := exec.CommandContext(ctx, config.Get("bin"), cmdArgs...)
- return WaitForConnection()
+ // Start the command with a pty (pseudo terminal)
+ // This is required to interact with the command
+ ptyF, err := pty.Start(c)
+ if err != nil {
+ log.Panic(err)
+ }
+ defer ptyF.Close()
+
+ for _, input := range inputs {
+ fmt.Fprintf(ptyF, "%s\n", input)
+ }
+
+ var cmdOutput string
+ if err := c.Wait(); err != nil {
+ if ctx.Err() == context.DeadlineExceeded {
+ cmdOutput = "Command timed out:\n"
+ }
+ }
+
+ // Copy the logs from the pty
+ io.Copy(logs, ptyF)
+ cmdOutput += logs.String()
+
+ // TODO: find if there is a better way to remove stdins from the output
+ cmdOutput = strings.TrimLeft(cmdOutput, strings.Join(inputs, "\r\n"))
+
+ return c.ProcessState.ExitCode(), cmdOutput
}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/.editorconfig b/tests/ociswrapper/vendor/github.com/creack/pty/.editorconfig
new file mode 100644
index 00000000000..349f67aa2dc
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/.editorconfig
@@ -0,0 +1,54 @@
+root = true
+
+# Sane defaults.
+[*]
+# Always use unix end of line.
+end_of_line = lf
+# Always insert a new line at the end of files.
+insert_final_newline = true
+# Don't leave trailing whitespaces.
+trim_trailing_whitespace = true
+# Default to utf8 encoding.
+charset = utf-8
+# Space > tab for consistent aligns.
+indent_style = space
+# Default to 2 spaces for indent/tabs.
+indent_size = 2
+# Flag long lines.
+max_line_length = 140
+
+# Explicitly define settings for commonly used files.
+
+[*.go]
+indent_style = tab
+indent_size = 8
+
+[*.feature]
+indent_style = space
+indent_size = 2
+
+[*.json]
+indent_style = space
+indent_size = 2
+
+[*.{yml,yaml}]
+indent_style = space
+indent_size = 2
+
+[*.tf]
+indent_style = space
+indent_size = 2
+
+[*.md]
+# Don't check line lenghts in files.
+max_line_length = 0
+
+[{Makefile,*.mk}]
+indent_style = tab
+indent_size = 8
+
+[{Dockerfile,Dockerfile.*}]
+indent_size = 4
+
+[*.sql]
+indent_size = 2
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/.gitignore b/tests/ociswrapper/vendor/github.com/creack/pty/.gitignore
new file mode 100644
index 00000000000..1f0a99f2f2b
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/.gitignore
@@ -0,0 +1,4 @@
+[568].out
+_go*
+_test*
+_obj
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/.golangci.yml b/tests/ociswrapper/vendor/github.com/creack/pty/.golangci.yml
new file mode 100644
index 00000000000..f023e0f76a0
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/.golangci.yml
@@ -0,0 +1,324 @@
+---
+# Reference: https://golangci-lint.run/usage/configuration/
+run:
+ timeout: 5m
+ # modules-download-mode: vendor
+
+ # Include test files.
+ tests: true
+
+ skip-dirs: []
+
+ skip-files: []
+
+output:
+ # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number".
+ format: colored-line-number
+ print-issued-lines: true
+ print-linter-name: true
+
+# Linter specific settings. See below in the `linter.enable` section for details on what each linter is doing.
+linters-settings:
+ dogsled:
+ # Checks assignments with too many blank identifiers. Default is 2.
+ max-blank-identifiers: 2
+
+ dupl:
+ # Tokens count to trigger issue.
+ threshold: 150
+
+ errcheck:
+ # Report about not checking of errors in type assertions: `a := b.(MyStruct)`.
+ # Enabled as this is often overlooked by developers.
+ check-type-assertions: true
+ # Report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`.
+ # Disabled as we consider that if the developer did type `_`, it was on purpose.
+ # Note that while this isn't enforced by the linter, each and every case of ignored error should
+ # be accompanied with a comment explaining why that error is being discarded.
+ check-blank: false
+
+ exhaustive:
+ # Indicates that switch statements are to be considered exhaustive if a
+ # 'default' case is present, even if all enum members aren't listed in the
+ # switch.
+ default-signifies-exhaustive: false
+
+ funlen:
+ # funlen checks the number of lines/statements in a function.
+ # While is is always best to keep functions short for readability, maintainability and testing,
+ # the default are a bit too strict (60 lines / 40 statements), increase it to be more flexible.
+ lines: 160
+ statements: 70
+
+ # NOTE: We don't set `gci` for import order as it supports only one prefix. Use `goimports.local-prefixes` instead.
+
+ gocognit:
+ # Minimal code complexity to report, defaults to 30 in gocognit, defaults 10 in golangci.
+ # Use 15 as it allows for some flexibility while preventing too much complexity.
+ # NOTE: Similar to gocyclo.
+ min-complexity: 35
+
+ nestif:
+ # Minimal complexity of if statements to report.
+ min-complexity: 8
+
+ goconst:
+ # Minimal length of string constant.
+ min-len: 4
+ # Minimal occurrences count to trigger.
+ # Increase the default from 3 to 5 as small number of const usage can reduce readability instead of improving it.
+ min-occurrences: 5
+
+ gocritic:
+ # Which checks should be disabled; can't be combined with 'enabled-checks'.
+ # See https://go-critic.github.io/overview#checks-overview
+ # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run`
+ disabled-checks:
+ - hugeParam # Very strict check on the size of variables being copied. Too strict for most developer.
+ # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
+ # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
+ enabled-tags:
+ - diagnostic
+ - style
+ - opinionated
+ - performance
+ settings:
+ rangeValCopy:
+ sizeThreshold: 1024 # Increase the allowed copied bytes in range.
+
+ cyclop:
+ max-complexity: 35
+
+ gocyclo:
+ # Similar check as gocognit.
+ # NOTE: We might be able to remove this linter as it is redundant with gocyclo. It is in golangci-lint, so we keep it for now.
+ min-complexity: 35
+
+ godot:
+ # Check all top-level comments, not only declarations.
+ check-all: true
+
+ gofmt:
+ # simplify code: gofmt with `-s` option.
+ simplify: true
+
+ # NOTE: the goheader settings are set per-project.
+
+ goimports:
+ # Put imports beginning with prefix after 3rd-party packages.
+ # It's a comma-separated list of prefixes.
+ local-prefixes: "github.com/creack/pty"
+
+ golint:
+ # Minimal confidence for issues, default is 0.8.
+ min-confidence: 0.8
+
+ gosimple:
+ # Select the Go version to target. The default is '1.13'.
+ go: "1.18"
+ # https://staticcheck.io/docs/options#checks
+ checks: ["all"]
+
+ gosec:
+
+ govet:
+ # Enable all available checks from go vet.
+ enable-all: false
+ # Report about shadowed variables.
+ check-shadowing: true
+
+ # NOTE: depguard is disabled as it is very slow and made redundant by gomodguard.
+
+ lll:
+ # Make sure everyone is on the same level, fix the tab width to go's default.
+ tab-width: 8
+ # Increase the default max line length to give more flexibility. Forcing newlines can reduce readability instead of improving it.
+ line-length: 180
+
+ misspell:
+ locale: US
+ ignore-words:
+
+ nakedret:
+ # Make an issue if func has more lines of code than this setting and it has naked returns; default is 30.
+ # NOTE: Consider setting this to 1 to prevent naked returns.
+ max-func-lines: 30
+
+ nolintlint:
+ # Prevent ununsed directive to avoid stale comments.
+ allow-unused: false
+ # Require an explanation of nonzero length after each nolint directive.
+ require-explanation: true
+ # Exclude following linters from requiring an explanation.
+ # NOTE: It is strongly discouraged to put anything in there.
+ allow-no-explanation: []
+ # Enable to require nolint directives to mention the specific linter being suppressed. This ensurce the developer understand the reason being the error.
+ require-specific: true
+
+ prealloc:
+ # NOTE: For most programs usage of prealloc will be a premature optimization.
+ # Keep thing simple, pre-alloc what is obvious and profile the program for more complex scenarios.
+ #
+ simple: true # Checkonly on simple loops that have no returns/breaks/continues/gotos in them.
+ range-loops: true # Check range loops, true by default
+ for-loops: false # Check suggestions on for loops, false by default
+
+ rowserrcheck:
+ packages: []
+
+ staticcheck:
+ # Select the Go version to target. The default is '1.13'.
+ go: "1.18"
+ # https://staticcheck.io/docs/options#checks
+ checks: ["all"]
+
+ stylecheck:
+ # Select the Go version to target. The default is '1.13'.
+ go: "1.18"
+ # https://staticcheck.io/docs/options#checks
+ checks: ["all"] # "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022"]
+
+ tagliatelle:
+ # Check the struck tag name case.
+ case:
+ # Use the struct field name to check the name of the struct tag.
+ use-field-name: false
+ rules:
+ # Any struct tag type can be used.
+ # support string case: `camel`, `pascal`, `kebab`, `snake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower`
+ json: snake
+ firestore: camel
+ yaml: camel
+ xml: camel
+ bson: camel
+ avro: snake
+ mapstructure: kebab
+ envconfig: upper
+
+ unparam:
+ # Don't create an error if an exported code have static params being used. It is often expected in libraries.
+ # NOTE: It would be nice if this linter would differentiate between a main package and a lib.
+ check-exported: true
+
+ unused: {}
+
+ whitespace:
+ multi-if: false # Enforces newlines (or comments) after every multi-line if statement
+ multi-func: false # Enforces newlines (or comments) after every multi-line function signature
+
+# Run `golangci-lint help linters` to get the full list of linter with their description.
+linters:
+ disable-all: true
+ # NOTE: enable-all is deprecated because too many people don't pin versions...
+ # We still require explicit documentation on why some linters are disabled.
+ # disable:
+ # - depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false]
+ # - exhaustivestruct # Checks if all struct's fields are initialized [fast: true, auto-fix: false]
+ # - forbidigo # Forbids identifiers [fast: true, auto-fix: false]
+ # - gci # Gci control golang package import order and make it always deterministic. [fast: true, auto-fix: true]
+ # - godox # Tool for detection of FIXME, TODO and other comment keywords [fast: true, auto-fix: false]
+ # - goerr113 # Golang linter to check the errors handling expressions [fast: true, auto-fix: false]
+ # - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes [fast: false, auto-fix: false]
+ # - gomnd # An analyzer to detect magic numbers. [fast: true, auto-fix: false]
+ # - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. [fast: true, auto-fix: false]
+ # - interfacer # Linter that suggests narrower interface types [fast: false, auto-fix: false]
+ # - maligned # Tool to detect Go structs that would take less memory if their fields were sorted [fast: false, auto-fix: false]
+ # - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity [fast: true, auto-fix: false]
+ # - scopelint # Scopelint checks for unpinned variables in go programs [fast: true, auto-fix: false]
+ # - wrapcheck # Checks that errors returned from external packages are wrapped [fast: false, auto-fix: false]
+ # - wsl # Whitespace Linter - Forces you to use empty lines! [fast: true, auto-fix: false]
+
+ # disable-reasons:
+ # - depguard # Checks whitelisted/blacklisted import path, but runs way too slow. Not that useful.
+ # - exhaustivestruct # Good concept, but not mature enough (errors on not assignable fields like locks) and too noisy when using AWS SDK as most fields are unused.
+ # - forbidigo # Great idea, but too strict out of the box. Probably will re-enable soon.
+ # - gci # Conflicts with goimports/gofumpt.
+ # - godox # Don't fail when finding TODO, FIXME, etc.
+ # - goerr113 # Too many false positives.
+ # - golint # Deprecated (since v1.41.0) due to: The repository of the linter has been archived by the owner. Replaced by revive.
+ # - gomnd # Checks for magic numbers. Disabled due to too many false positives not configurable (03/01/2020 v1.23.7).
+ # - gomoddirectives # Doesn't support //nolint to whitelist.
+ # - interfacer # Deprecated (since v1.38.0) due to: The repository of the linter has been archived by the owner.
+ # - maligned # Deprecated (since v1.38.0) due to: The repository of the linter has been archived by the owner. Replaced by govet 'fieldalignment'.
+ # - nlreturn # Actually reduces readability in most cases.
+ # - scopelint # Deprecated (since v1.39.0) due to: The repository of the linter has been deprecated by the owner. Replaced by exportloopref.
+ # - wrapcheck # Good concept, but always warns for http coded errors. Need to re-enable and whitelist our error package.
+ # - wsl # Forces to add newlines around blocks. Lots of false positives, not that useful.
+
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers [fast: true, auto-fix: false]
+ - bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false]
+ - cyclop # checks function and package cyclomatic complexity [fast: false, auto-fix: false]
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false]
+ - dupl # Tool for code clone detection [fast: true, auto-fix: false]
+ - durationcheck # check for two durations multiplied together [fast: false, auto-fix: false]
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: false, auto-fix: false]
+ - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. [fast: false, auto-fix: false]
+ - errorlint # go-errorlint is a source code linter for Go software that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. [fast: false, auto-fix: false]
+ - exhaustive # check exhaustiveness of enum switch statements [fast: false, auto-fix: false]
+ - exportloopref # checks for pointers to enclosing loop variables [fast: false, auto-fix: false]
+ - forcetypeassert # finds forced type assertions [fast: true, auto-fix: false]
+ - funlen # Tool for detection of long functions [fast: true, auto-fix: false]
+ - gochecknoglobals # check that no global variables exist [fast: true, auto-fix: false]
+ - gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
+ - gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false]
+ - goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
+ - gocritic # Provides many diagnostics that check for bugs, performance and style issues. [fast: false, auto-fix: false]
+ - gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false]
+ - godot # Check if comments end in a period [fast: true, auto-fix: true]
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true]
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed. [fast: true, auto-fix: true]
+ - goheader # Checks is file header matches to pattern [fast: true, auto-fix: false]
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports [fast: true, auto-fix: true]
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. [fast: true, auto-fix: false]
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end [fast: true, auto-fix: false]
+ - gosec # (gas): Inspects source code for security problems [fast: false, auto-fix: false]
+ - gosimple # (megacheck): Linter for Go source code that specializes in simplifying a code [fast: false, auto-fix: false]
+ - govet # (vet, vetshadow): Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string [fast: false, auto-fix: false]
+ - importas # Enforces consistent import aliases [fast: false, auto-fix: false]
+ - ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false]
+ - lll # Reports long lines [fast: true, auto-fix: false]
+ - makezero # Finds slice declarations with non-zero initial length [fast: false, auto-fix: false]
+ - misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true]
+ - nakedret # Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false]
+ - nestif # Reports deeply nested if statements [fast: true, auto-fix: false]
+ - nilerr # Finds the code that returns nil even if it checks that the error is not nil. [fast: false, auto-fix: false]
+ - noctx # noctx finds sending http request without context.Context [fast: false, auto-fix: false]
+ - nolintlint # Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: false]
+ - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test [fast: true, auto-fix: false]
+ - prealloc # Finds slice declarations that could potentially be preallocated [fast: true, auto-fix: false]
+ - predeclared # find code that shadows one of Go's predeclared identifiers [fast: true, auto-fix: false]
+ - promlinter # Check Prometheus metrics naming via promlint [fast: true, auto-fix: false]
+ - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false]
+ # Disabled due to generic. Work in progress upstream.
+ # - rowserrcheck # checks whether Err of rows is checked successfully [fast: false, auto-fix: false]
+ # Disabled due to generic. Work in progress upstream.
+ # - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. [fast: false, auto-fix: false]
+ - staticcheck # (megacheck): Staticcheck is a go vet on steroids, applying a ton of static analysis checks [fast: false, auto-fix: false]
+ - stylecheck # Stylecheck is a replacement for golint [fast: false, auto-fix: false]
+ # Disabled due to generic. Work in progress upstream.
+ # - tagliatelle # Checks the struct tags. [fast: true, auto-fix: false]
+ # - testpackage # linter that makes you use a separate _test package [fast: true, auto-fix: false]
+ - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers [fast: false, auto-fix: false]
+ - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes [fast: false, auto-fix: false]
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code [fast: false, auto-fix: false]
+ - unconvert # Remove unnecessary type conversions [fast: false, auto-fix: false]
+ - unparam # Reports unused function parameters [fast: false, auto-fix: false]
+ # Disabled due to way too many false positive in go1.20.
+ # - unused # (megacheck): Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false]
+ # Disabled due to generic. Work in progress upstream.
+ # - wastedassign # wastedassign finds wasted assignment statements. [fast: false, auto-fix: false]
+ - whitespace # Tool for detection of leading and trailing whitespace [fast: true, auto-fix: true]
+
+issues:
+ exclude:
+ # Allow shadowing of 'err'.
+ - 'shadow: declaration of "err" shadows declaration'
+ # Allow shadowing of `ctx`.
+ - 'shadow: declaration of "ctx" shadows declaration'
+ # Maximum issues count per one linter. Set to 0 to disable. Default is 50.
+ max-per-linter: 10
+ # Disable default excludes. Always be explicit on what we exclude.
+ exclude-use-default: false
+ # Exclude some linters from running on tests files.
+ exclude-rules: []
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/Dockerfile.golang b/tests/ociswrapper/vendor/github.com/creack/pty/Dockerfile.golang
new file mode 100644
index 00000000000..b6153421c0a
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/Dockerfile.golang
@@ -0,0 +1,17 @@
+ARG GOVERSION=1.18.2
+FROM golang:${GOVERSION}
+
+# Set base env.
+ARG GOOS=linux
+ARG GOARCH=amd64
+ENV GOOS=${GOOS} GOARCH=${GOARCH} CGO_ENABLED=0 GOFLAGS='-v -ldflags=-s -ldflags=-w'
+
+# Pre compile the stdlib for 386/arm (32bits).
+RUN go build -a std
+
+# Add the code to the image.
+WORKDIR pty
+ADD . .
+
+# Build the lib.
+RUN go build
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/LICENSE b/tests/ociswrapper/vendor/github.com/creack/pty/LICENSE
new file mode 100644
index 00000000000..6b7558b6b42
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2011 Keith Rarick
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute,
+sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall
+be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/README.md b/tests/ociswrapper/vendor/github.com/creack/pty/README.md
new file mode 100644
index 00000000000..a4fe7670d4a
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/README.md
@@ -0,0 +1,107 @@
+# pty
+
+Pty is a Go package for using unix pseudo-terminals.
+
+## Install
+
+```sh
+go get github.com/creack/pty
+```
+
+## Examples
+
+Note that those examples are for demonstration purpose only, to showcase how to use the library. They are not meant to be used in any kind of production environment.
+
+### Command
+
+```go
+package main
+
+import (
+ "io"
+ "os"
+ "os/exec"
+
+ "github.com/creack/pty"
+)
+
+func main() {
+ c := exec.Command("grep", "--color=auto", "bar")
+ f, err := pty.Start(c)
+ if err != nil {
+ panic(err)
+ }
+
+ go func() {
+ f.Write([]byte("foo\n"))
+ f.Write([]byte("bar\n"))
+ f.Write([]byte("baz\n"))
+ f.Write([]byte{4}) // EOT
+ }()
+ io.Copy(os.Stdout, f)
+}
+```
+
+### Shell
+
+```go
+package main
+
+import (
+ "io"
+ "log"
+ "os"
+ "os/exec"
+ "os/signal"
+ "syscall"
+
+ "github.com/creack/pty"
+ "golang.org/x/term"
+)
+
+func test() error {
+ // Create arbitrary command.
+ c := exec.Command("bash")
+
+ // Start the command with a pty.
+ ptmx, err := pty.Start(c)
+ if err != nil {
+ return err
+ }
+ // Make sure to close the pty at the end.
+ defer func() { _ = ptmx.Close() }() // Best effort.
+
+ // Handle pty size.
+ ch := make(chan os.Signal, 1)
+ signal.Notify(ch, syscall.SIGWINCH)
+ go func() {
+ for range ch {
+ if err := pty.InheritSize(os.Stdin, ptmx); err != nil {
+ log.Printf("error resizing pty: %s", err)
+ }
+ }
+ }()
+ ch <- syscall.SIGWINCH // Initial resize.
+ defer func() { signal.Stop(ch); close(ch) }() // Cleanup signals when done.
+
+ // Set stdin in raw mode.
+ oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
+ if err != nil {
+ panic(err)
+ }
+ defer func() { _ = term.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.
+
+ // Copy stdin to the pty and the pty to stdout.
+ // NOTE: The goroutine will keep reading until the next keystroke before returning.
+ go func() { _, _ = io.Copy(ptmx, os.Stdin) }()
+ _, _ = io.Copy(os.Stdout, ptmx)
+
+ return nil
+}
+
+func main() {
+ if err := test(); err != nil {
+ log.Fatal(err)
+ }
+}
+```
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/asm_solaris_amd64.s b/tests/ociswrapper/vendor/github.com/creack/pty/asm_solaris_amd64.s
new file mode 100644
index 00000000000..7fbef8ee66f
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/asm_solaris_amd64.s
@@ -0,0 +1,18 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build gc
+//+build gc
+
+#include "textflag.h"
+
+//
+// System calls for amd64, Solaris are implemented in runtime/syscall_solaris.go
+//
+
+TEXT ·sysvicall6(SB),NOSPLIT,$0-88
+ JMP syscall·sysvicall6(SB)
+
+TEXT ·rawSysvicall6(SB),NOSPLIT,$0-88
+ JMP syscall·rawSysvicall6(SB)
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/doc.go b/tests/ociswrapper/vendor/github.com/creack/pty/doc.go
new file mode 100644
index 00000000000..3c8b3244e8f
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/doc.go
@@ -0,0 +1,16 @@
+// Package pty provides functions for working with Unix terminals.
+package pty
+
+import (
+ "errors"
+ "os"
+)
+
+// ErrUnsupported is returned if a function is not
+// available on the current platform.
+var ErrUnsupported = errors.New("unsupported")
+
+// Open a pty and its corresponding tty.
+func Open() (pty, tty *os.File, err error) {
+ return open()
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ioctl.go b/tests/ociswrapper/vendor/github.com/creack/pty/ioctl.go
new file mode 100644
index 00000000000..7b6b770b7fe
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ioctl.go
@@ -0,0 +1,28 @@
+//go:build !windows && go1.12
+// +build !windows,go1.12
+
+package pty
+
+import "os"
+
+func ioctl(f *os.File, cmd, ptr uintptr) error {
+ return ioctlInner(f.Fd(), cmd, ptr) // Fall back to blocking io.
+}
+
+// NOTE: Unused. Keeping for reference.
+func ioctlNonblock(f *os.File, cmd, ptr uintptr) error {
+ sc, e := f.SyscallConn()
+ if e != nil {
+ return ioctlInner(f.Fd(), cmd, ptr) // Fall back to blocking io (old behavior).
+ }
+
+ ch := make(chan error, 1)
+ defer close(ch)
+
+ e = sc.Control(func(fd uintptr) { ch <- ioctlInner(fd, cmd, ptr) })
+ if e != nil {
+ return e
+ }
+ e = <-ch
+ return e
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_bsd.go b/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_bsd.go
new file mode 100644
index 00000000000..db3bf845bef
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_bsd.go
@@ -0,0 +1,40 @@
+//go:build darwin || dragonfly || freebsd || netbsd || openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package pty
+
+// from
+const (
+ _IOC_VOID uintptr = 0x20000000
+ _IOC_OUT uintptr = 0x40000000
+ _IOC_IN uintptr = 0x80000000
+ _IOC_IN_OUT uintptr = _IOC_OUT | _IOC_IN
+ _IOC_DIRMASK = _IOC_VOID | _IOC_OUT | _IOC_IN
+
+ _IOC_PARAM_SHIFT = 13
+ _IOC_PARAM_MASK = (1 << _IOC_PARAM_SHIFT) - 1
+)
+
+func _IOC_PARM_LEN(ioctl uintptr) uintptr {
+ return (ioctl >> 16) & _IOC_PARAM_MASK
+}
+
+func _IOC(inout uintptr, group byte, ioctl_num uintptr, param_len uintptr) uintptr {
+ return inout | (param_len&_IOC_PARAM_MASK)<<16 | uintptr(group)<<8 | ioctl_num
+}
+
+func _IO(group byte, ioctl_num uintptr) uintptr {
+ return _IOC(_IOC_VOID, group, ioctl_num, 0)
+}
+
+func _IOR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
+ return _IOC(_IOC_OUT, group, ioctl_num, param_len)
+}
+
+func _IOW(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
+ return _IOC(_IOC_IN, group, ioctl_num, param_len)
+}
+
+func _IOWR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
+ return _IOC(_IOC_IN_OUT, group, ioctl_num, param_len)
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_inner.go b/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_inner.go
new file mode 100644
index 00000000000..272b50b971c
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_inner.go
@@ -0,0 +1,20 @@
+//go:build !windows && !solaris && !aix
+// +build !windows,!solaris,!aix
+
+package pty
+
+import "syscall"
+
+// Local syscall const values.
+const (
+ TIOCGWINSZ = syscall.TIOCGWINSZ
+ TIOCSWINSZ = syscall.TIOCSWINSZ
+)
+
+func ioctlInner(fd, cmd, ptr uintptr) error {
+ _, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
+ if e != 0 {
+ return e
+ }
+ return nil
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_legacy.go b/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_legacy.go
new file mode 100644
index 00000000000..f7e923cd079
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_legacy.go
@@ -0,0 +1,10 @@
+//go:build !windows && !go1.12
+// +build !windows,!go1.12
+
+package pty
+
+import "os"
+
+func ioctl(f *os.File, cmd, ptr uintptr) error {
+ return ioctlInner(f.Fd(), cmd, ptr) // fall back to blocking io (old behavior)
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_solaris.go b/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_solaris.go
new file mode 100644
index 00000000000..6fd8bfeee53
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_solaris.go
@@ -0,0 +1,48 @@
+//go:build solaris
+// +build solaris
+
+package pty
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+//go:cgo_import_dynamic libc_ioctl ioctl "libc.so"
+//go:linkname procioctl libc_ioctl
+var procioctl uintptr
+
+const (
+ // see /usr/include/sys/stropts.h
+ I_PUSH = uintptr((int32('S')<<8 | 002))
+ I_STR = uintptr((int32('S')<<8 | 010))
+ I_FIND = uintptr((int32('S')<<8 | 013))
+
+ // see /usr/include/sys/ptms.h
+ ISPTM = (int32('P') << 8) | 1
+ UNLKPT = (int32('P') << 8) | 2
+ PTSSTTY = (int32('P') << 8) | 3
+ ZONEPT = (int32('P') << 8) | 4
+ OWNERPT = (int32('P') << 8) | 5
+
+ // see /usr/include/sys/termios.h
+ TIOCSWINSZ = (uint32('T') << 8) | 103
+ TIOCGWINSZ = (uint32('T') << 8) | 104
+)
+
+type strioctl struct {
+ icCmd int32
+ icTimeout int32
+ icLen int32
+ icDP unsafe.Pointer
+}
+
+// Defined in asm_solaris_amd64.s.
+func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
+
+func ioctlInner(fd, cmd, ptr uintptr) error {
+ if _, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procioctl)), 3, fd, cmd, ptr, 0, 0, 0); errno != 0 {
+ return errno
+ }
+ return nil
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_unsupported.go b/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_unsupported.go
new file mode 100644
index 00000000000..e17908d44ac
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ioctl_unsupported.go
@@ -0,0 +1,13 @@
+//go:build aix
+// +build aix
+
+package pty
+
+const (
+ TIOCGWINSZ = 0
+ TIOCSWINSZ = 0
+)
+
+func ioctlInner(fd, cmd, ptr uintptr) error {
+ return ErrUnsupported
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/mktypes.bash b/tests/ociswrapper/vendor/github.com/creack/pty/mktypes.bash
new file mode 100644
index 00000000000..7f71bda6a65
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/mktypes.bash
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+GOOSARCH="${GOOS}_${GOARCH}"
+case "$GOOSARCH" in
+_* | *_ | _)
+ echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
+ exit 1
+ ;;
+esac
+
+GODEFS="go tool cgo -godefs"
+
+$GODEFS types.go |gofmt > ztypes_$GOARCH.go
+
+case $GOOS in
+freebsd|dragonfly|netbsd|openbsd)
+ $GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go
+ ;;
+esac
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/pty_darwin.go b/tests/ociswrapper/vendor/github.com/creack/pty/pty_darwin.go
new file mode 100644
index 00000000000..eadf6ab7c79
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/pty_darwin.go
@@ -0,0 +1,68 @@
+//go:build darwin
+// +build darwin
+
+package pty
+
+import (
+ "errors"
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+func open() (pty, tty *os.File, err error) {
+ pFD, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ p := os.NewFile(uintptr(pFD), "/dev/ptmx")
+ // In case of error after this point, make sure we close the ptmx fd.
+ defer func() {
+ if err != nil {
+ _ = p.Close() // Best effort.
+ }
+ }()
+
+ sname, err := ptsname(p)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if err := grantpt(p); err != nil {
+ return nil, nil, err
+ }
+
+ if err := unlockpt(p); err != nil {
+ return nil, nil, err
+ }
+
+ t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ return p, t, nil
+}
+
+func ptsname(f *os.File) (string, error) {
+ n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME))
+
+ err := ioctl(f, syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
+ if err != nil {
+ return "", err
+ }
+
+ for i, c := range n {
+ if c == 0 {
+ return string(n[:i]), nil
+ }
+ }
+ return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
+}
+
+func grantpt(f *os.File) error {
+ return ioctl(f, syscall.TIOCPTYGRANT, 0)
+}
+
+func unlockpt(f *os.File) error {
+ return ioctl(f, syscall.TIOCPTYUNLK, 0)
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/pty_dragonfly.go b/tests/ociswrapper/vendor/github.com/creack/pty/pty_dragonfly.go
new file mode 100644
index 00000000000..12803de043c
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/pty_dragonfly.go
@@ -0,0 +1,83 @@
+//go:build dragonfly
+// +build dragonfly
+
+package pty
+
+import (
+ "errors"
+ "os"
+ "strings"
+ "syscall"
+ "unsafe"
+)
+
+// same code as pty_darwin.go
+func open() (pty, tty *os.File, err error) {
+ p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ // In case of error after this point, make sure we close the ptmx fd.
+ defer func() {
+ if err != nil {
+ _ = p.Close() // Best effort.
+ }
+ }()
+
+ sname, err := ptsname(p)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if err := grantpt(p); err != nil {
+ return nil, nil, err
+ }
+
+ if err := unlockpt(p); err != nil {
+ return nil, nil, err
+ }
+
+ t, err := os.OpenFile(sname, os.O_RDWR, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ return p, t, nil
+}
+
+func grantpt(f *os.File) error {
+ _, err := isptmaster(f)
+ return err
+}
+
+func unlockpt(f *os.File) error {
+ _, err := isptmaster(f)
+ return err
+}
+
+func isptmaster(f *os.File) (bool, error) {
+ err := ioctl(f, syscall.TIOCISPTMASTER, 0)
+ return err == nil, err
+}
+
+var (
+ emptyFiodgnameArg fiodgnameArg
+ ioctl_FIODNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
+)
+
+func ptsname(f *os.File) (string, error) {
+ name := make([]byte, _C_SPECNAMELEN)
+ fa := fiodgnameArg{Name: (*byte)(unsafe.Pointer(&name[0])), Len: _C_SPECNAMELEN, Pad_cgo_0: [4]byte{0, 0, 0, 0}}
+
+ err := ioctl(f, ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa)))
+ if err != nil {
+ return "", err
+ }
+
+ for i, c := range name {
+ if c == 0 {
+ s := "/dev/" + string(name[:i])
+ return strings.Replace(s, "ptm", "pts", -1), nil
+ }
+ }
+ return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/pty_freebsd.go b/tests/ociswrapper/vendor/github.com/creack/pty/pty_freebsd.go
new file mode 100644
index 00000000000..47afcfeec8e
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/pty_freebsd.go
@@ -0,0 +1,81 @@
+//go:build freebsd
+// +build freebsd
+
+package pty
+
+import (
+ "errors"
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+func posixOpenpt(oflag int) (fd int, err error) {
+ r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0)
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return fd, err
+}
+
+func open() (pty, tty *os.File, err error) {
+ fd, err := posixOpenpt(syscall.O_RDWR | syscall.O_CLOEXEC)
+ if err != nil {
+ return nil, nil, err
+ }
+ p := os.NewFile(uintptr(fd), "/dev/pts")
+ // In case of error after this point, make sure we close the pts fd.
+ defer func() {
+ if err != nil {
+ _ = p.Close() // Best effort.
+ }
+ }()
+
+ sname, err := ptsname(p)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ return p, t, nil
+}
+
+func isptmaster(f *os.File) (bool, error) {
+ err := ioctl(f, syscall.TIOCPTMASTER, 0)
+ return err == nil, err
+}
+
+var (
+ emptyFiodgnameArg fiodgnameArg
+ ioctlFIODGNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
+)
+
+func ptsname(f *os.File) (string, error) {
+ master, err := isptmaster(f)
+ if err != nil {
+ return "", err
+ }
+ if !master {
+ return "", syscall.EINVAL
+ }
+
+ const n = _C_SPECNAMELEN + 1
+ var (
+ buf = make([]byte, n)
+ arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
+ )
+ if err := ioctl(f, ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil {
+ return "", err
+ }
+
+ for i, c := range buf {
+ if c == 0 {
+ return string(buf[:i]), nil
+ }
+ }
+ return "", errors.New("FIODGNAME string not NUL-terminated")
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/pty_linux.go b/tests/ociswrapper/vendor/github.com/creack/pty/pty_linux.go
new file mode 100644
index 00000000000..e7e01c0aa5e
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/pty_linux.go
@@ -0,0 +1,54 @@
+//go:build linux
+// +build linux
+
+package pty
+
+import (
+ "os"
+ "strconv"
+ "syscall"
+ "unsafe"
+)
+
+func open() (pty, tty *os.File, err error) {
+ p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ // In case of error after this point, make sure we close the ptmx fd.
+ defer func() {
+ if err != nil {
+ _ = p.Close() // Best effort.
+ }
+ }()
+
+ sname, err := ptsname(p)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if err := unlockpt(p); err != nil {
+ return nil, nil, err
+ }
+
+ t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0) //nolint:gosec // Expected Open from a variable.
+ if err != nil {
+ return nil, nil, err
+ }
+ return p, t, nil
+}
+
+func ptsname(f *os.File) (string, error) {
+ var n _C_uint
+ err := ioctl(f, syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) //nolint:gosec // Expected unsafe pointer for Syscall call.
+ if err != nil {
+ return "", err
+ }
+ return "/dev/pts/" + strconv.Itoa(int(n)), nil
+}
+
+func unlockpt(f *os.File) error {
+ var u _C_int
+ // use TIOCSPTLCK with a pointer to zero to clear the lock.
+ return ioctl(f, syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) //nolint:gosec // Expected unsafe pointer for Syscall call.
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/pty_netbsd.go b/tests/ociswrapper/vendor/github.com/creack/pty/pty_netbsd.go
new file mode 100644
index 00000000000..dd5611dbd70
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/pty_netbsd.go
@@ -0,0 +1,69 @@
+//go:build netbsd
+// +build netbsd
+
+package pty
+
+import (
+ "errors"
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+func open() (pty, tty *os.File, err error) {
+ p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ // In case of error after this point, make sure we close the ptmx fd.
+ defer func() {
+ if err != nil {
+ _ = p.Close() // Best effort.
+ }
+ }()
+
+ sname, err := ptsname(p)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if err := grantpt(p); err != nil {
+ return nil, nil, err
+ }
+
+ // In NetBSD unlockpt() does nothing, so it isn't called here.
+
+ t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ return p, t, nil
+}
+
+func ptsname(f *os.File) (string, error) {
+ /*
+ * from ptsname(3): The ptsname() function is equivalent to:
+ * struct ptmget pm;
+ * ioctl(fd, TIOCPTSNAME, &pm) == -1 ? NULL : pm.sn;
+ */
+ var ptm ptmget
+ if err := ioctl(f, uintptr(ioctl_TIOCPTSNAME), uintptr(unsafe.Pointer(&ptm))); err != nil {
+ return "", err
+ }
+ name := make([]byte, len(ptm.Sn))
+ for i, c := range ptm.Sn {
+ name[i] = byte(c)
+ if c == 0 {
+ return string(name[:i]), nil
+ }
+ }
+ return "", errors.New("TIOCPTSNAME string not NUL-terminated")
+}
+
+func grantpt(f *os.File) error {
+ /*
+ * from grantpt(3): Calling grantpt() is equivalent to:
+ * ioctl(fd, TIOCGRANTPT, 0);
+ */
+ return ioctl(f, uintptr(ioctl_TIOCGRANTPT), 0)
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/pty_openbsd.go b/tests/ociswrapper/vendor/github.com/creack/pty/pty_openbsd.go
new file mode 100644
index 00000000000..337c39f3f1c
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/pty_openbsd.go
@@ -0,0 +1,47 @@
+//go:build openbsd
+// +build openbsd
+
+package pty
+
+import (
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+func cInt8ToString(in []int8) string {
+ var s []byte
+ for _, v := range in {
+ if v == 0 {
+ break
+ }
+ s = append(s, byte(v))
+ }
+ return string(s)
+}
+
+func open() (pty, tty *os.File, err error) {
+ /*
+ * from ptm(4):
+ * The PTMGET command allocates a free pseudo terminal, changes its
+ * ownership to the caller, revokes the access privileges for all previous
+ * users, opens the file descriptors for the pty and tty devices and
+ * returns them to the caller in struct ptmget.
+ */
+
+ p, err := os.OpenFile("/dev/ptm", os.O_RDWR|syscall.O_CLOEXEC, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer p.Close()
+
+ var ptm ptmget
+ if err := ioctl(p, uintptr(ioctl_PTMGET), uintptr(unsafe.Pointer(&ptm))); err != nil {
+ return nil, nil, err
+ }
+
+ pty = os.NewFile(uintptr(ptm.Cfd), cInt8ToString(ptm.Cn[:]))
+ tty = os.NewFile(uintptr(ptm.Sfd), cInt8ToString(ptm.Sn[:]))
+
+ return pty, tty, nil
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/pty_solaris.go b/tests/ociswrapper/vendor/github.com/creack/pty/pty_solaris.go
new file mode 100644
index 00000000000..4e22416b013
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/pty_solaris.go
@@ -0,0 +1,171 @@
+//go:build solaris
+// +build solaris
+
+package pty
+
+/* based on:
+http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/pt.c
+*/
+
+import (
+ "errors"
+ "os"
+ "strconv"
+ "syscall"
+ "unsafe"
+)
+
+func open() (pty, tty *os.File, err error) {
+ ptmxfd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ p := os.NewFile(uintptr(ptmxfd), "/dev/ptmx")
+ // In case of error after this point, make sure we close the ptmx fd.
+ defer func() {
+ if err != nil {
+ _ = p.Close() // Best effort.
+ }
+ }()
+
+ sname, err := ptsname(p)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if err := grantpt(p); err != nil {
+ return nil, nil, err
+ }
+
+ if err := unlockpt(p); err != nil {
+ return nil, nil, err
+ }
+
+ ptsfd, err := syscall.Open(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
+ if err != nil {
+ return nil, nil, err
+ }
+ t := os.NewFile(uintptr(ptsfd), sname)
+
+ // In case of error after this point, make sure we close the pts fd.
+ defer func() {
+ if err != nil {
+ _ = t.Close() // Best effort.
+ }
+ }()
+
+ // pushing terminal driver STREAMS modules as per pts(7)
+ for _, mod := range []string{"ptem", "ldterm", "ttcompat"} {
+ if err := streamsPush(t, mod); err != nil {
+ return nil, nil, err
+ }
+ }
+
+ return p, t, nil
+}
+
+func ptsname(f *os.File) (string, error) {
+ dev, err := ptsdev(f)
+ if err != nil {
+ return "", err
+ }
+ fn := "/dev/pts/" + strconv.FormatInt(int64(dev), 10)
+
+ if err := syscall.Access(fn, 0); err != nil {
+ return "", err
+ }
+ return fn, nil
+}
+
+func unlockpt(f *os.File) error {
+ istr := strioctl{
+ icCmd: UNLKPT,
+ icTimeout: 0,
+ icLen: 0,
+ icDP: nil,
+ }
+ return ioctl(f, I_STR, uintptr(unsafe.Pointer(&istr)))
+}
+
+func minor(x uint64) uint64 { return x & 0377 }
+
+func ptsdev(f *os.File) (uint64, error) {
+ istr := strioctl{
+ icCmd: ISPTM,
+ icTimeout: 0,
+ icLen: 0,
+ icDP: nil,
+ }
+
+ if err := ioctl(f, I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
+ return 0, err
+ }
+ var errors = make(chan error, 1)
+ var results = make(chan uint64, 1)
+ defer close(errors)
+ defer close(results)
+
+ var err error
+ var sc syscall.RawConn
+ sc, err = f.SyscallConn()
+ if err != nil {
+ return 0, err
+ }
+ err = sc.Control(func(fd uintptr) {
+ var status syscall.Stat_t
+ if err := syscall.Fstat(int(fd), &status); err != nil {
+ results <- 0
+ errors <- err
+ }
+ results <- uint64(minor(status.Rdev))
+ errors <- nil
+ })
+ if err != nil {
+ return 0, err
+ }
+ return <-results, <-errors
+}
+
+type ptOwn struct {
+ rUID int32
+ rGID int32
+}
+
+func grantpt(f *os.File) error {
+ if _, err := ptsdev(f); err != nil {
+ return err
+ }
+ pto := ptOwn{
+ rUID: int32(os.Getuid()),
+ // XXX should first attempt to get gid of DEFAULT_TTY_GROUP="tty"
+ rGID: int32(os.Getgid()),
+ }
+ istr := strioctl{
+ icCmd: OWNERPT,
+ icTimeout: 0,
+ icLen: int32(unsafe.Sizeof(strioctl{})),
+ icDP: unsafe.Pointer(&pto),
+ }
+ if err := ioctl(f, I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
+ return errors.New("access denied")
+ }
+ return nil
+}
+
+// streamsPush pushes STREAMS modules if not already done so.
+func streamsPush(f *os.File, mod string) error {
+ buf := []byte(mod)
+
+ // XXX I_FIND is not returning an error when the module
+ // is already pushed even though truss reports a return
+ // value of 1. A bug in the Go Solaris syscall interface?
+ // XXX without this we are at risk of the issue
+ // https://www.illumos.org/issues/9042
+ // but since we are not using libc or XPG4.2, we should not be
+ // double-pushing modules
+
+ if err := ioctl(f, I_FIND, uintptr(unsafe.Pointer(&buf[0]))); err != nil {
+ return nil
+ }
+ return ioctl(f, I_PUSH, uintptr(unsafe.Pointer(&buf[0])))
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/pty_unsupported.go b/tests/ociswrapper/vendor/github.com/creack/pty/pty_unsupported.go
new file mode 100644
index 00000000000..c771020fae5
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/pty_unsupported.go
@@ -0,0 +1,12 @@
+//go:build !linux && !darwin && !freebsd && !dragonfly && !netbsd && !openbsd && !solaris
+// +build !linux,!darwin,!freebsd,!dragonfly,!netbsd,!openbsd,!solaris
+
+package pty
+
+import (
+ "os"
+)
+
+func open() (pty, tty *os.File, err error) {
+ return nil, nil, ErrUnsupported
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/run.go b/tests/ociswrapper/vendor/github.com/creack/pty/run.go
new file mode 100644
index 00000000000..4755366200b
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/run.go
@@ -0,0 +1,57 @@
+package pty
+
+import (
+ "os"
+ "os/exec"
+ "syscall"
+)
+
+// Start assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
+// and c.Stderr, calls c.Start, and returns the File of the tty's
+// corresponding pty.
+//
+// Starts the process in a new session and sets the controlling terminal.
+func Start(cmd *exec.Cmd) (*os.File, error) {
+ return StartWithSize(cmd, nil)
+}
+
+// StartWithAttrs assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
+// and c.Stderr, calls c.Start, and returns the File of the tty's
+// corresponding pty.
+//
+// This will resize the pty to the specified size before starting the command if a size is provided.
+// The `attrs` parameter overrides the one set in c.SysProcAttr.
+//
+// This should generally not be needed. Used in some edge cases where it is needed to create a pty
+// without a controlling terminal.
+func StartWithAttrs(c *exec.Cmd, sz *Winsize, attrs *syscall.SysProcAttr) (*os.File, error) {
+ pty, tty, err := Open()
+ if err != nil {
+ return nil, err
+ }
+ defer func() { _ = tty.Close() }() // Best effort.
+
+ if sz != nil {
+ if err := Setsize(pty, sz); err != nil {
+ _ = pty.Close() // Best effort.
+ return nil, err
+ }
+ }
+ if c.Stdout == nil {
+ c.Stdout = tty
+ }
+ if c.Stderr == nil {
+ c.Stderr = tty
+ }
+ if c.Stdin == nil {
+ c.Stdin = tty
+ }
+
+ c.SysProcAttr = attrs
+
+ if err := c.Start(); err != nil {
+ _ = pty.Close() // Best effort.
+ return nil, err
+ }
+ return pty, err
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/start.go b/tests/ociswrapper/vendor/github.com/creack/pty/start.go
new file mode 100644
index 00000000000..9b51635f5e1
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/start.go
@@ -0,0 +1,25 @@
+//go:build !windows
+// +build !windows
+
+package pty
+
+import (
+ "os"
+ "os/exec"
+ "syscall"
+)
+
+// StartWithSize assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
+// and c.Stderr, calls c.Start, and returns the File of the tty's
+// corresponding pty.
+//
+// This will resize the pty to the specified size before starting the command.
+// Starts the process in a new session and sets the controlling terminal.
+func StartWithSize(cmd *exec.Cmd, ws *Winsize) (*os.File, error) {
+ if cmd.SysProcAttr == nil {
+ cmd.SysProcAttr = &syscall.SysProcAttr{}
+ }
+ cmd.SysProcAttr.Setsid = true
+ cmd.SysProcAttr.Setctty = true
+ return StartWithAttrs(cmd, ws, cmd.SysProcAttr)
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/start_windows.go b/tests/ociswrapper/vendor/github.com/creack/pty/start_windows.go
new file mode 100644
index 00000000000..7e9530ba03c
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/start_windows.go
@@ -0,0 +1,19 @@
+//go:build windows
+// +build windows
+
+package pty
+
+import (
+ "os"
+ "os/exec"
+)
+
+// StartWithSize assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
+// and c.Stderr, calls c.Start, and returns the File of the tty's
+// corresponding pty.
+//
+// This will resize the pty to the specified size before starting the command.
+// Starts the process in a new session and sets the controlling terminal.
+func StartWithSize(cmd *exec.Cmd, ws *Winsize) (*os.File, error) {
+ return nil, ErrUnsupported
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/test_crosscompile.sh b/tests/ociswrapper/vendor/github.com/creack/pty/test_crosscompile.sh
new file mode 100644
index 00000000000..40df89add69
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/test_crosscompile.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env sh
+
+# Test script checking that all expected os/arch compile properly.
+# Does not actually test the logic, just the compilation so we make sure we don't break code depending on the lib.
+
+echo2() {
+ echo $@ >&2
+}
+
+trap end 0
+end() {
+ [ "$?" = 0 ] && echo2 "Pass." || (echo2 "Fail."; exit 1)
+}
+
+cross() {
+ os=$1
+ shift
+ echo2 "Build for $os."
+ for arch in $@; do
+ echo2 " - $os/$arch"
+ GOOS=$os GOARCH=$arch go build
+ done
+ echo2
+}
+
+set -e
+
+cross linux amd64 386 arm arm64 ppc64 ppc64le s390x mips mipsle mips64 mips64le riscv64
+cross darwin amd64 arm64
+cross freebsd amd64 386 arm arm64 riscv64
+cross netbsd amd64 386 arm arm64
+cross openbsd amd64 386 arm arm64
+cross dragonfly amd64
+cross solaris amd64
+
+# Not expected to work but should still compile.
+cross windows amd64 386 arm
+
+# TODO: Fix compilation error on openbsd/arm.
+# TODO: Merge the solaris PR.
+
+# Some os/arch require a different compiler. Run in docker.
+if ! hash docker; then
+ # If docker is not present, stop here.
+ return
+fi
+
+# Golang dropped support for darwin 32bits since go1.15. Make sure the lib still compile with go1.14 on those archs.
+echo2 "Build for darwin (32bits)."
+echo2 " - darwin/386"
+docker build -t creack-pty-test -f Dockerfile.golang --build-arg=GOVERSION=1.14 --build-arg=GOOS=darwin --build-arg=GOARCH=386 .
+echo2 " - darwin/arm"
+docker build -t creack-pty-test -f Dockerfile.golang --build-arg=GOVERSION=1.14 --build-arg=GOOS=darwin --build-arg=GOARCH=arm .
+
+# Run a single test for an old go version. Would be best with go1.0, but not available on Dockerhub.
+# Using 1.6 as it is the base version for the RISCV compiler.
+# Would also be better to run all the tests, not just one, need to refactor this file to allow for specifc archs per version.
+echo2 "Build for linux - go1.6."
+echo2 " - linux/amd64"
+docker build -t creack-pty-test -f Dockerfile.golang --build-arg=GOVERSION=1.6 --build-arg=GOOS=linux --build-arg=GOARCH=amd64 .
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/winsize.go b/tests/ociswrapper/vendor/github.com/creack/pty/winsize.go
new file mode 100644
index 00000000000..cfa3e5f391e
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/winsize.go
@@ -0,0 +1,24 @@
+package pty
+
+import "os"
+
+// InheritSize applies the terminal size of pty to tty. This should be run
+// in a signal handler for syscall.SIGWINCH to automatically resize the tty when
+// the pty receives a window size change notification.
+func InheritSize(pty, tty *os.File) error {
+ size, err := GetsizeFull(pty)
+ if err != nil {
+ return err
+ }
+ return Setsize(tty, size)
+}
+
+// Getsize returns the number of rows (lines) and cols (positions
+// in each line) in terminal t.
+func Getsize(t *os.File) (rows, cols int, err error) {
+ ws, err := GetsizeFull(t)
+ if err != nil {
+ return 0, 0, err
+ }
+ return int(ws.Rows), int(ws.Cols), nil
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/winsize_unix.go b/tests/ociswrapper/vendor/github.com/creack/pty/winsize_unix.go
new file mode 100644
index 00000000000..8dbbcda0f07
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/winsize_unix.go
@@ -0,0 +1,35 @@
+//go:build !windows
+// +build !windows
+
+package pty
+
+import (
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+// Winsize describes the terminal size.
+type Winsize struct {
+ Rows uint16 // ws_row: Number of rows (in cells).
+ Cols uint16 // ws_col: Number of columns (in cells).
+ X uint16 // ws_xpixel: Width in pixels.
+ Y uint16 // ws_ypixel: Height in pixels.
+}
+
+// Setsize resizes t to s.
+func Setsize(t *os.File, ws *Winsize) error {
+ //nolint:gosec // Expected unsafe pointer for Syscall call.
+ return ioctl(t, syscall.TIOCSWINSZ, uintptr(unsafe.Pointer(ws)))
+}
+
+// GetsizeFull returns the full terminal size description.
+func GetsizeFull(t *os.File) (size *Winsize, err error) {
+ var ws Winsize
+
+ //nolint:gosec // Expected unsafe pointer for Syscall call.
+ if err := ioctl(t, syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&ws))); err != nil {
+ return nil, err
+ }
+ return &ws, nil
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/winsize_unsupported.go b/tests/ociswrapper/vendor/github.com/creack/pty/winsize_unsupported.go
new file mode 100644
index 00000000000..0d2109938ad
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/winsize_unsupported.go
@@ -0,0 +1,23 @@
+//go:build windows
+// +build windows
+
+package pty
+
+import (
+ "os"
+)
+
+// Winsize is a dummy struct to enable compilation on unsupported platforms.
+type Winsize struct {
+ Rows, Cols, X, Y uint16
+}
+
+// Setsize resizes t to s.
+func Setsize(*os.File, *Winsize) error {
+ return ErrUnsupported
+}
+
+// GetsizeFull returns the full terminal size description.
+func GetsizeFull(*os.File) (*Winsize, error) {
+ return nil, ErrUnsupported
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_386.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_386.go
new file mode 100644
index 00000000000..d126f4aa583
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_386.go
@@ -0,0 +1,12 @@
+//go:build 386
+// +build 386
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+ _C_int int32
+ _C_uint uint32
+)
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_amd64.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_amd64.go
new file mode 100644
index 00000000000..6c4a7677fc7
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_amd64.go
@@ -0,0 +1,12 @@
+//go:build amd64
+// +build amd64
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+ _C_int int32
+ _C_uint uint32
+)
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_arm.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_arm.go
new file mode 100644
index 00000000000..de6fe160ea5
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_arm.go
@@ -0,0 +1,12 @@
+//go:build arm
+// +build arm
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+ _C_int int32
+ _C_uint uint32
+)
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_arm64.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_arm64.go
new file mode 100644
index 00000000000..c4f315cac1a
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_arm64.go
@@ -0,0 +1,12 @@
+//go:build arm64
+// +build arm64
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+ _C_int int32
+ _C_uint uint32
+)
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_dragonfly_amd64.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_dragonfly_amd64.go
new file mode 100644
index 00000000000..183c4214711
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_dragonfly_amd64.go
@@ -0,0 +1,17 @@
+//go:build amd64 && dragonfly
+// +build amd64,dragonfly
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_dragonfly.go
+
+package pty
+
+const (
+ _C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+ Name *byte
+ Len uint32
+ Pad_cgo_0 [4]byte
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_386.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_386.go
new file mode 100644
index 00000000000..d80dbf7172b
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_386.go
@@ -0,0 +1,16 @@
+//go:build 386 && freebsd
+// +build 386,freebsd
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+ _C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+ Len int32
+ Buf *byte
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_amd64.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_amd64.go
new file mode 100644
index 00000000000..bfab4e45825
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_amd64.go
@@ -0,0 +1,17 @@
+//go:build amd64 && freebsd
+// +build amd64,freebsd
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+ _C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+ Len int32
+ Pad_cgo_0 [4]byte
+ Buf *byte
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_arm.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_arm.go
new file mode 100644
index 00000000000..3a8aeae371d
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_arm.go
@@ -0,0 +1,16 @@
+//go:build arm && freebsd
+// +build arm,freebsd
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+ _C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+ Len int32
+ Buf *byte
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_arm64.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_arm64.go
new file mode 100644
index 00000000000..a83924918a0
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_arm64.go
@@ -0,0 +1,16 @@
+//go:build arm64 && freebsd
+// +build arm64,freebsd
+
+// Code generated by cmd/cgo -godefs; DO NOT EDIT.
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+ _C_SPECNAMELEN = 0xff
+)
+
+type fiodgnameArg struct {
+ Len int32
+ Buf *byte
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_ppc64.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_ppc64.go
new file mode 100644
index 00000000000..5fa102fcdf6
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_ppc64.go
@@ -0,0 +1,14 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+ _C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+ Len int32
+ Pad_cgo_0 [4]byte
+ Buf *byte
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_riscv64.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_riscv64.go
new file mode 100644
index 00000000000..b3c544098ce
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_freebsd_riscv64.go
@@ -0,0 +1,13 @@
+// Code generated by cmd/cgo -godefs; DO NOT EDIT.
+// cgo -godefs types_freebsd.go
+
+package pty
+
+const (
+ _C_SPECNAMELEN = 0x3f
+)
+
+type fiodgnameArg struct {
+ Len int32
+ Buf *byte
+}
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_loong64.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_loong64.go
new file mode 100644
index 00000000000..3beb5c17626
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_loong64.go
@@ -0,0 +1,12 @@
+//go:build loong64
+// +build loong64
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+ _C_int int32
+ _C_uint uint32
+)
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_mipsx.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_mipsx.go
new file mode 100644
index 00000000000..281277977ef
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_mipsx.go
@@ -0,0 +1,13 @@
+//go:build (mips || mipsle || mips64 || mips64le) && linux
+// +build mips mipsle mips64 mips64le
+// +build linux
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+ _C_int int32
+ _C_uint uint32
+)
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_netbsd_32bit_int.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_netbsd_32bit_int.go
new file mode 100644
index 00000000000..2ab7c45598f
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_netbsd_32bit_int.go
@@ -0,0 +1,17 @@
+//go:build (386 || amd64 || arm || arm64) && netbsd
+// +build 386 amd64 arm arm64
+// +build netbsd
+
+package pty
+
+type ptmget struct {
+ Cfd int32
+ Sfd int32
+ Cn [1024]int8
+ Sn [1024]int8
+}
+
+var (
+ ioctl_TIOCPTSNAME = 0x48087448
+ ioctl_TIOCGRANTPT = 0x20007447
+)
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_openbsd_32bit_int.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_openbsd_32bit_int.go
new file mode 100644
index 00000000000..1eb0948167e
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_openbsd_32bit_int.go
@@ -0,0 +1,14 @@
+//go:build (386 || amd64 || arm || arm64 || mips64) && openbsd
+// +build 386 amd64 arm arm64 mips64
+// +build openbsd
+
+package pty
+
+type ptmget struct {
+ Cfd int32
+ Sfd int32
+ Cn [16]int8
+ Sn [16]int8
+}
+
+var ioctl_PTMGET = 0x40287401
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_ppc.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_ppc.go
new file mode 100644
index 00000000000..ff0b8fd838f
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_ppc.go
@@ -0,0 +1,9 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+ _C_int int32
+ _C_uint uint32
+)
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_ppc64.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_ppc64.go
new file mode 100644
index 00000000000..bbb3da83220
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_ppc64.go
@@ -0,0 +1,12 @@
+//go:build ppc64
+// +build ppc64
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+ _C_int int32
+ _C_uint uint32
+)
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_ppc64le.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_ppc64le.go
new file mode 100644
index 00000000000..8a4fac3e928
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_ppc64le.go
@@ -0,0 +1,12 @@
+//go:build ppc64le
+// +build ppc64le
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+ _C_int int32
+ _C_uint uint32
+)
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_riscvx.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_riscvx.go
new file mode 100644
index 00000000000..dc5da905061
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_riscvx.go
@@ -0,0 +1,12 @@
+//go:build riscv || riscv64
+// +build riscv riscv64
+
+// Code generated by cmd/cgo -godefs; DO NOT EDIT.
+// cgo -godefs types.go
+
+package pty
+
+type (
+ _C_int int32
+ _C_uint uint32
+)
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_s390x.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_s390x.go
new file mode 100644
index 00000000000..3433be7ca08
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_s390x.go
@@ -0,0 +1,12 @@
+//go:build s390x
+// +build s390x
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types.go
+
+package pty
+
+type (
+ _C_int int32
+ _C_uint uint32
+)
diff --git a/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_sparcx.go b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_sparcx.go
new file mode 100644
index 00000000000..06e44311df8
--- /dev/null
+++ b/tests/ociswrapper/vendor/github.com/creack/pty/ztypes_sparcx.go
@@ -0,0 +1,12 @@
+//go:build sparc || sparc64
+// +build sparc sparc64
+
+// Code generated by cmd/cgo -godefs; DO NOT EDIT.
+// cgo -godefs types.go
+
+package pty
+
+type (
+ _C_int int32
+ _C_uint uint32
+)
diff --git a/tests/ociswrapper/vendor/modules.txt b/tests/ociswrapper/vendor/modules.txt
index 81942bd4b7c..17d24c5d095 100644
--- a/tests/ociswrapper/vendor/modules.txt
+++ b/tests/ociswrapper/vendor/modules.txt
@@ -1,3 +1,6 @@
+# github.com/creack/pty v1.1.21
+## explicit; go 1.13
+github.com/creack/pty
# github.com/inconshreveable/mousetrap v1.1.0
## explicit; go 1.18
github.com/inconshreveable/mousetrap
diff --git a/tests/ociswrapper/wrapper/handlers/handler.go b/tests/ociswrapper/wrapper/handlers/handler.go
index 09e4c64e054..c2bd1cde55a 100644
--- a/tests/ociswrapper/wrapper/handlers/handler.go
+++ b/tests/ociswrapper/wrapper/handlers/handler.go
@@ -3,16 +3,28 @@ package handlers
import (
"encoding/json"
"errors"
+ "fmt"
"io"
"net/http"
+ "ociswrapper/common"
"ociswrapper/ocis"
+ "os"
)
+type BasicResponse struct {
+ Status string `json:"status"`
+ Message string `json:"message"`
+}
+type CommandResponse struct {
+ *BasicResponse
+ ExitCode int `json:"exitCode"`
+}
+
func parseJsonBody(reqBody io.ReadCloser) (map[string]any, error) {
body, _ := io.ReadAll(reqBody)
if len(body) == 0 || !json.Valid(body) {
- return nil, errors.New("Invalid json data")
+ return nil, errors.New("invalid json data")
}
var bodyMap map[string]any
@@ -21,18 +33,41 @@ func parseJsonBody(reqBody io.ReadCloser) (map[string]any, error) {
return bodyMap, nil
}
-func sendResponse(res http.ResponseWriter, ocisStatus bool) {
- resBody := make(map[string]string)
+func sendResponse(res http.ResponseWriter, statusCode int, message string) {
+ res.Header().Set("Content-Type", "application/json")
+ res.WriteHeader(statusCode)
+
+ var status string
+ if statusCode == http.StatusOK {
+ status = "OK"
+ } else {
+ status = "ERROR"
+ }
+
+ resBody := BasicResponse{
+ Status: status,
+ Message: message,
+ }
+
+ jsonResponse, _ := json.Marshal(resBody)
+ res.Write(jsonResponse)
+}
+
+func sendCmdResponse(res http.ResponseWriter, exitCode int, message string) {
+ resBody := CommandResponse{
+ BasicResponse: &BasicResponse{
+ Message: message,
+ },
+ ExitCode: exitCode,
+ }
- if ocisStatus {
- res.WriteHeader(http.StatusOK)
- resBody["status"] = "OK"
- resBody["message"] = "oCIS server is running"
+ if exitCode == 0 {
+ resBody.BasicResponse.Status = "OK"
} else {
- res.WriteHeader(http.StatusInternalServerError)
- resBody["status"] = "ERROR"
- resBody["message"] = "Unable to start oCIS server"
+ resBody.BasicResponse.Status = "ERROR"
}
+
+ res.WriteHeader(http.StatusOK)
res.Header().Set("Content-Type", "application/json")
jsonResponse, _ := json.Marshal(resBody)
@@ -41,28 +76,129 @@ func sendResponse(res http.ResponseWriter, ocisStatus bool) {
func SetEnvHandler(res http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPut {
- http.Error(res, "Method not allowed", http.StatusMethodNotAllowed)
+ sendResponse(res, http.StatusMethodNotAllowed, "")
return
}
- environments, err := parseJsonBody(req.Body)
+ envBody, err := parseJsonBody(req.Body)
if err != nil {
- http.Error(res, "Bad request", http.StatusBadRequest)
+ sendResponse(res, http.StatusMethodNotAllowed, "Invalid json body")
return
}
- ocisStatus := ocis.Restart(environments)
+ var envMap []string
+ for key, value := range envBody {
+ envMap = append(envMap, fmt.Sprintf("%s=%v", key, value))
+ }
+ ocis.EnvConfigs = append(ocis.EnvConfigs, envMap...)
+
+ var message string
+
+ success, _ := ocis.Restart(ocis.EnvConfigs)
+ if success {
+ message = "oCIS configured successfully"
+ sendResponse(res, http.StatusOK, message)
+ return
+ }
- sendResponse(res, ocisStatus)
+ message = "Failed to restart oCIS with new configuration"
+ sendResponse(res, http.StatusInternalServerError, message)
}
func RollbackHandler(res http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodDelete {
- http.Error(res, "Method not allowed", http.StatusMethodNotAllowed)
+ sendResponse(res, http.StatusMethodNotAllowed, "")
+ return
+ }
+
+ var message string
+ ocis.EnvConfigs = []string{}
+ success, _ := ocis.Restart(os.Environ())
+ if success {
+ message = "oCIS configuration rolled back successfully"
+ sendResponse(res, http.StatusOK, message)
+ return
+ }
+
+ message = "Failed to restart oCIS with initial configuration"
+ sendResponse(res, http.StatusInternalServerError, message)
+}
+
+func StopOcisHandler(res http.ResponseWriter, req *http.Request) {
+ if req.Method != http.MethodPost {
+ sendResponse(res, http.StatusMethodNotAllowed, "")
+ return
+ }
+
+ success, message := ocis.Stop()
+ if success {
+ sendResponse(res, http.StatusOK, message)
+ return
+ }
+
+ sendResponse(res, http.StatusInternalServerError, message)
+}
+
+func StartOcisHandler(res http.ResponseWriter, req *http.Request) {
+ if req.Method != http.MethodPost {
+ sendResponse(res, http.StatusMethodNotAllowed, "")
+ return
+ }
+
+ if ocis.IsOcisRunning() {
+ sendResponse(res, http.StatusConflict, "oCIS server is already running")
+ return
+ }
+
+ common.Wg.Add(1)
+ go ocis.Start(nil)
+
+ success, message := ocis.WaitForConnection()
+ if success {
+ sendResponse(res, http.StatusOK, message)
+ return
+ }
+
+ sendResponse(res, http.StatusInternalServerError, message)
+}
+
+func CommandHandler(res http.ResponseWriter, req *http.Request) {
+ if req.Method != http.MethodPost {
+ sendResponse(res, http.StatusMethodNotAllowed, "")
return
}
- ocisStatus := ocis.Restart(nil)
+ if req.Body == nil {
+ sendResponse(res, http.StatusBadRequest, "Body is missing")
+ return
+ }
+
+ body, err := parseJsonBody(req.Body)
+ if err != nil {
+ sendResponse(res, http.StatusBadRequest, "Invalid json body")
+ return
+ }
+ if _, ok := body["command"]; !ok {
+ sendResponse(res, http.StatusBadRequest, "Command is missing")
+ return
+ }
+
+ command := body["command"].(string)
+
+ stdIn := []string{}
+ if _, ok := body["inputs"]; ok {
+ if inputs, ok := body["inputs"].([]interface{}); ok {
+ for _, input := range inputs {
+ if _, ok := input.(string); ok {
+ stdIn = append(stdIn, input.(string))
+ } else {
+ sendResponse(res, http.StatusBadRequest, "Invalid input data. Expected string")
+ return
+ }
+ }
+ }
+ }
- sendResponse(res, ocisStatus)
+ exitCode, output := ocis.RunCommand(command, stdIn)
+ sendCmdResponse(res, exitCode, output)
}
diff --git a/tests/ociswrapper/wrapper/wrapper.go b/tests/ociswrapper/wrapper/wrapper.go
index e5fc80c9aee..35b7873134a 100644
--- a/tests/ociswrapper/wrapper/wrapper.go
+++ b/tests/ociswrapper/wrapper/wrapper.go
@@ -24,6 +24,9 @@ func Start(port string) {
mux.HandleFunc("/", http.NotFound)
mux.HandleFunc("/config", handlers.SetEnvHandler)
mux.HandleFunc("/rollback", handlers.RollbackHandler)
+ mux.HandleFunc("/command", handlers.CommandHandler)
+ mux.HandleFunc("/stop", handlers.StopOcisHandler)
+ mux.HandleFunc("/start", handlers.StartOcisHandler)
httpServer.Handler = mux