#!/usr/bin/env expect
############################################################################
# Purpose: Test of slurmrestd OpenAPI generation
############################################################################
# Copyright (C) 2021 SchedMD LLC
# Written by Nathan Rini <nate@schedmd.gov>
#
# This file is part of Slurm, a resource management program.
# For details, see <https://slurm.schedmd.com/>.
# Please also read the included file: DISCLAIMER.
#
# Slurm is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Slurm 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 General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with Slurm; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
############################################################################
source ./globals

set spec $test_dir/openapi.spec
set pyapi $test_dir/py_api
set pyenv $test_dir/env
set usock $test_dir/slurmrestd.sock
set external_slurmrestd 0
set scripts "[$bin_pwd]/${test_name}_scripts"

# Avoid fatal on slurmrestd because it should not be run as SlurmUser
set env(SLURMRESTD_SECURITY) disable_user_check

# Set env(SLURM_TESTSUITE_SLURMRESTD_SOCKET) to use external slurmrestd service
# instead. If SLURM_TESTSUITE_SLURMRESTD_SOCKET is set test won't start/stop
# its own slurmrestd but will relay into an externally running one.
if {[info exists env(SLURM_TESTSUITE_SLURMRESTD_SOCKET)]} {
	set usock $env(SLURM_TESTSUITE_SLURMRESTD_SOCKET)
	set external_slurmrestd 1
}

if {![file exists $slurmrestd]} {
	skip "slurmrestd not installed"
}

# TODO: Currently only OpenAPI-Generator version 4.x is supported by the test.
#       See Bug 11963.
set oasgen_check [run_command -nolog "$bin_oasgen version"]
if {[dict get $oasgen_check exit_code] ||
    ![regexp {^([0-9]+)[.]\S+} [dict get $oasgen_check output] fullmatch oas_gen_maj]} {
	skip "$bin_oasgen not found"
}
if {$oas_gen_maj != "4"} {
	skip "unsupported openapi-generator-cli version: $fullmatch != 4.x"
}


if {[run_command_status -nolog "$bin_py3 --version"]} {
	skip "$bin_py3 not found"
}

if {[run_command_status -nolog "$bin_socat -V"]} {
	skip "$bin_socat not found"
}

if {[run_command_status -nolog "$bin_ip -V"]} {
	skip "$bin_ip not found"
}

if {[run_command_status -nolog "$bin_unshare -V"]} {
	skip "$bin_unshare not found"
}

if {[run_command_status -nolog "$bin_virtualenv  --version"]} {
	skip "$bin_virtualenv not found"
}

proc cleanup {} {
}

foreach content_path [glob -type d $scripts/*] {
	set content [file tail $content_path]
	log_debug [concat "content: " $content]
	set result [run_command -nolog -fail -subtest -stdin \
		"GET /openapi HTTP/1.1\r\nConnection: Close\r\n\r\n" "$slurmrestd -s $content -a rest_auth/local"]

	set lines [split [dict get $result "output"] "\n"]
	set found 0
	set fd [open $spec "w"]
	set printed 0
	set cntlng 1.0E+10
	# Filter out the response headers by the first empty line
	foreach line $lines {
		if {$cntlng <= $printed} {
			break;
		}
		if {$cntlng <= $printed + [string bytelength $line]} {
			set line [string range $line 0 [expr {$cntlng - $printed - 1}]]
		}

		if {$found} {
			set printed [expr {$printed + [string bytelength $line] + 1}]
			puts $fd [concat $line "\n"]
		} else {
			set matched ""
			regexp -expanded {Content-Length:\s+(\d+)} $line matched cntlng
			if {$matched != ""} {
				log_debug "max length: $cntlng"
			}

			log_debug [concat "header: " $line]
		}

		if {$line eq "\r"} {
			set found 1
		}
	}
	close $fd
	unset lines

	set rc [run_command_status -nolog "$bin_jq -h"]
	if {$rc != 0} {
		subskip "jq not found"
	} else {
		run_command_status -subtest "$bin_jq . '$spec' >/dev/null"
	}

	# move into the test dir since install oas leaves trash in the cwd
	cd $test_dir

	run_command -nolog -fail \
		"$bin_oasgen generate -i '$spec' -g python --strict-spec=true -o '$pyapi'"

	run_command -nolog -fail \
		"$bin_virtualenv --clear --no-download --system-site-packages $pyenv"

	run_command -nolog -fail \
		"source $pyenv/bin/activate; pip3 install '$pyapi'"

	foreach script [glob -type f $scripts/$content/*.py] {
		log_debug "running: $script"

		if {!$external_slurmrestd} {
			file delete $usock
			log_debug "forking slurmrestd"
			set restpid [exec $slurmrestd "-vv" "-s" $content "-a" "rest_auth/local" "unix:${usock}" "&"]

			# Wait for slurmrestd to start
			run_command_status -timeout 10 -subtest "while \[ \! -S ${usock} \]; do sleep 0.1; done"
		}

		# To avoid collisions with any existing listening service, we will
		# unshare the network namespace and use socat to talk to the unix
		# socket since python will not
		run_command -subtest "$bin_unshare -U -r -n bash -c '
				$bin_ip link set lo up
				$bin_socat TCP-LISTEN:80 UNIX-CONNECT:${usock} &
				SOPID=\$!
				source $pyenv/bin/activate;
				$bin_py3 $script
				rc=$?
				kill -9 \$SOPID
				wait
				exit \$rc
			'
		"

		if {!$external_slurmrestd} {
			run_command -nolog -fail "$bin_kill -s INT $restpid"
		}
	}
}
