Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
varnishapi
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
uplex-varnish
varnishapi
Commits
6af60ff6
Commit
6af60ff6
authored
Aug 24, 2018
by
Geoff Simmons
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add package admin.
parent
765d4f8b
Changes
3
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
1045 additions
and
0 deletions
+1045
-0
admin.go
pkg/admin/admin.go
+518
-0
cli.go
pkg/admin/cli.go
+229
-0
e2e_test.go
pkg/admin/e2e_test.go
+298
-0
No files found.
pkg/admin/admin.go
0 → 100644
View file @
6af60ff6
This diff is collapsed.
Click to expand it.
pkg/admin/cli.go
0 → 100644
View file @
6af60ff6
/*-
* Copyright (c) 2018 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package
admin
import
(
"errors"
"strconv"
"strings"
"time"
)
// Pong represents the response to the "ping" command.
type
Pong
struct
{
Time
time
.
Time
CLIVersion
string
}
// Ping encapsulates the "ping" command. error is non-nil when the
// response was not OK. In that case, the error is an
// UnexpectedResponse, which wraps the response from Varnish.
func
(
adm
*
Admin
)
Ping
()
(
Pong
,
error
)
{
pong
:=
Pong
{}
resp
,
err
:=
adm
.
Command
(
"ping"
)
if
err
!=
nil
{
return
pong
,
err
}
if
resp
.
Code
!=
OK
{
badResp
:=
UnexpectedResponse
{
Response
:
resp
}
return
pong
,
badResp
}
answer
:=
strings
.
Split
(
resp
.
Msg
,
" "
)
epoch
,
err
:=
strconv
.
Atoi
(
answer
[
1
])
if
err
!=
nil
{
return
pong
,
err
}
pong
.
Time
=
time
.
Unix
(
int64
(
epoch
),
0
)
pong
.
CLIVersion
=
answer
[
2
]
return
pong
,
err
}
// A State represents the status of the Varnish child process.
type
State
uint8
const
(
// Stopped indicates that the child was stopped regularly.
Stopped
State
=
iota
// Starting indicates that the management process has begun
// but not completed starting the child process.
Starting
// Running indicates that the child process is running
// regularly.
Running
// Stopping indicates that the management process is shutting
// down the child process regularly.
Stopping
// Died indicates that the child process process stopped
// unexpectedly, and will be restarted by the management
// process.
Died
)
func
(
state
State
)
String
()
string
{
switch
state
{
case
Stopped
:
return
"stopped"
case
Starting
:
return
"starting"
case
Running
:
return
"running"
case
Stopping
:
return
"stopping"
case
Died
:
return
"died unexpectedly (being restarted)"
default
:
panic
(
"Invalid state"
)
}
}
// Status encapsulates the "status" command. error is non-nil when the
// response was not OK. In that case, the error is an
// UnexpectedResponse, which wraps the response from Varnish.
func
(
adm
*
Admin
)
Status
()
(
State
,
error
)
{
resp
,
err
:=
adm
.
Command
(
"status"
)
if
err
!=
nil
{
return
Died
,
err
}
if
resp
.
Code
!=
OK
{
badResp
:=
UnexpectedResponse
{
Response
:
resp
}
return
Died
,
badResp
}
if
strings
.
Contains
(
resp
.
Msg
,
"running"
)
{
return
Running
,
nil
}
if
strings
.
Contains
(
resp
.
Msg
,
"stopped"
)
{
return
Stopped
,
nil
}
if
strings
.
Contains
(
resp
.
Msg
,
"starting"
)
{
return
Starting
,
nil
}
if
strings
.
Contains
(
resp
.
Msg
,
"stopping"
)
{
return
Stopping
,
nil
}
if
strings
.
Contains
(
resp
.
Msg
,
"died"
)
{
return
Died
,
nil
}
return
Died
,
errors
.
New
(
"unknown status"
)
}
// Start encapsulates the "start" command. error is non-nil when the
// response was not OK. In that case, the error is an
// UnexpectedResponse, which wraps the response from Varnish.
func
(
adm
*
Admin
)
Start
()
error
{
resp
,
err
:=
adm
.
Command
(
"start"
)
if
err
!=
nil
{
return
err
}
if
resp
.
Code
!=
OK
{
badResp
:=
UnexpectedResponse
{
Response
:
resp
}
return
badResp
}
return
nil
}
// Stop encapsulates the "stop" command. error is non-nil when the
// response was not OK. In that case, the error is an
// UnexpectedResponse, which wraps the response from Varnish.
func
(
adm
*
Admin
)
Stop
()
error
{
resp
,
err
:=
adm
.
Command
(
"stop"
)
if
err
!=
nil
{
return
err
}
if
resp
.
Code
!=
OK
{
badResp
:=
UnexpectedResponse
{
Response
:
resp
}
return
badResp
}
return
nil
}
// VCLLoad encapsulates the "vcl.load" command. error is non-nil when
// the response was not OK. In that case, the error is an
// UnexpectedResponse, which wraps the response from Varnish.
//
// Varnish usually returns status 106 (Param) when the load fails due
// to a compile failure, and the error message is in the wrapped
// Response.Msg.
//
// config is the configuration name under which the VCL instance can
// be referenced afterward. The name may not be already in use. path
// is file path at which the VCL source is located.
func
(
adm
*
Admin
)
VCLLoad
(
config
string
,
path
string
)
error
{
resp
,
err
:=
adm
.
Command
(
"vcl.load"
,
config
,
path
)
if
err
!=
nil
{
return
err
}
if
resp
.
Code
!=
OK
{
badResp
:=
UnexpectedResponse
{
Response
:
resp
}
return
badResp
}
return
nil
}
// VCLUse encapsulates the "vcl.use" command. error is non-nil when
// the response was not OK. In that case, the error is an
// UnexpectedResponse, which wraps the response from Varnish.
//
// config is the configuration name previousy set with VCLLoad(), or a
// previous invocation of "vcl.load". On success, that config becomes
// the active VCL instance.
func
(
adm
*
Admin
)
VCLUse
(
config
string
)
error
{
resp
,
err
:=
adm
.
Command
(
"vcl.use"
,
config
)
if
err
!=
nil
{
return
err
}
if
resp
.
Code
!=
OK
{
badResp
:=
UnexpectedResponse
{
Response
:
resp
}
return
badResp
}
return
nil
}
// VCLDiscard encapsulates the "vcl.discard" command. error is non-nil
// when the response was not OK. In that case, the error is an
// UnexpectedResponse, which wraps the response from Varnish.
//
// config is the configuration name previousy set with VCLLoad(), or a
// previous invocation of "vcl.load". The config may not be the
// current active config. On success, that config is no longer
// available, and cannot become active via VCLUse() or "vcl.use".
func
(
adm
*
Admin
)
VCLDiscard
(
config
string
)
error
{
resp
,
err
:=
adm
.
Command
(
"vcl.discard"
,
config
)
if
err
!=
nil
{
return
err
}
if
resp
.
Code
!=
OK
{
badResp
:=
UnexpectedResponse
{
Response
:
resp
}
return
badResp
}
return
nil
}
pkg/admin/e2e_test.go
0 → 100644
View file @
6af60ff6
// +build e2e
/*-
* Copyright (c) 2018 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
// This test is only run if go test is invoked with a -tags option
// that includes "e2e". It requires that two instances of Varnish are
// running: a default instance (varnishd invoked without the -n
// option), and an instance named "gotest" (varnishd -n "gotest"). For
// the purposes of this test, it does not matter how either instance
// is configured, only that they are running.
package
admin
import
(
"uplex.de/varnishapi/internal/pkg/vsm"
"io/ioutil"
"os"
"strings"
"testing"
"time"
)
var
(
address
string
secret
[]
byte
)
func
TestMain
(
m
*
testing
.
M
)
{
vsm
:=
vsm
.
New
()
if
vsm
==
nil
{
panic
(
"Cannot open VSM"
)
}
defer
vsm
.
Destroy
()
if
err
:=
vsm
.
Attach
(
""
);
err
!=
nil
{
panic
(
err
)
}
targ
,
err
:=
vsm
.
Get
(
"Arg"
,
"-T"
)
if
err
!=
nil
{
panic
(
err
)
}
targ
=
strings
.
TrimSpace
(
targ
)
address
=
strings
.
Replace
(
targ
,
" "
,
":"
,
1
)
sarg
,
err
:=
vsm
.
Get
(
"Arg"
,
"-S"
)
if
err
!=
nil
{
panic
(
err
)
}
sarg
=
strings
.
TrimSpace
(
sarg
)
sfile
,
err
:=
os
.
Open
(
sarg
)
if
err
!=
nil
{
panic
(
err
)
}
secret
,
err
=
ioutil
.
ReadAll
(
sfile
)
if
err
!=
nil
{
panic
(
err
)
}
os
.
Exit
(
m
.
Run
())
}
func
TestAttach
(
t
*
testing
.
T
)
{
adm
,
err
:=
Attach
(
""
,
time
.
Duration
(
0
))
if
err
!=
nil
{
t
.
Fatal
(
"Attach():"
,
err
)
return
}
defer
adm
.
Close
()
if
adm
.
Banner
==
""
{
t
.
Error
(
"Banner is empty after Attach()"
)
}
if
!
strings
.
Contains
(
adm
.
Banner
,
"Varnish Cache CLI"
)
{
t
.
Error
(
"Banner does not contain 'Varnish Cache CLI'"
)
}
tadm
,
err
:=
Attach
(
"gotest"
,
time
.
Duration
(
0
))
if
err
!=
nil
{
t
.
Fatal
(
"Attach(gotest):"
,
err
)
return
}
defer
tadm
.
Close
()
if
tadm
.
Banner
==
""
{
t
.
Error
(
"Banner is empty after Attach()"
)
}
if
!
strings
.
Contains
(
adm
.
Banner
,
"Varnish Cache CLI"
)
{
t
.
Error
(
"Banner does not contain 'Varnish Cache CLI'"
)
}
}
func
TestDial
(
t
*
testing
.
T
)
{
adm
,
err
:=
Dial
(
address
,
secret
,
time
.
Duration
(
0
))
if
err
!=
nil
{
t
.
Fatal
(
"Dial():"
,
err
)
return
}
defer
adm
.
Close
()
if
adm
.
Banner
==
""
{
t
.
Error
(
"Banner is empty after Attach()"
)
}
if
!
strings
.
Contains
(
adm
.
Banner
,
"Varnish Cache CLI"
)
{
t
.
Error
(
"Banner does not contain 'Varnish Cache CLI'"
)
}
}
func
TestListen
(
t
*
testing
.
T
)
{
adm
,
err
:=
Listen
(
"127.0.0.1:0"
)
if
err
!=
nil
{
t
.
Fatal
(
"Listen():"
,
err
)
return
}
defer
adm
.
Close
()
}
func
TestClose
(
t
*
testing
.
T
)
{
adm
,
err
:=
Dial
(
address
,
secret
,
time
.
Duration
(
0
))
if
err
!=
nil
{
t
.
Fatal
(
"Dial():"
,
err
)
return
}
if
err
=
adm
.
Close
();
err
!=
nil
{
t
.
Error
(
"Close():"
,
err
)
}
var
n
*
Admin
if
err
=
n
.
Close
();
err
==
nil
{
t
.
Error
(
"Expected nil.Close() to fail"
)
}
}
func
TestLocalAddr
(
t
*
testing
.
T
)
{
adm
,
err
:=
Dial
(
address
,
secret
,
time
.
Duration
(
0
))
if
err
!=
nil
{
t
.
Fatal
(
"Dial():"
,
err
)
return
}
defer
adm
.
Close
()
addr
,
err
:=
adm
.
LocalAddr
()
if
err
!=
nil
{
t
.
Fatal
(
"LocalAddr():"
,
err
)
return
}
if
addr
.
Network
()
!=
"tcp"
{
t
.
Errorf
(
"LocalAddr().Network() want=tcp got=%v"
,
addr
.
Network
())
}
str
:=
addr
.
String
()
if
str
==
""
{
t
.
Error
(
"LocalAddr().String() is empty"
)
}
var
n
*
Admin
if
addr
,
err
=
n
.
LocalAddr
();
err
==
nil
{
t
.
Error
(
"Expected nil.LocalAddr() to fail"
)
}
unconn
:=
new
(
Admin
)
if
addr
,
err
=
unconn
.
LocalAddr
();
err
==
nil
{
t
.
Error
(
"Expected unconnected.LocalAddr() to fail"
)
}
}
func
TestRemoteAddr
(
t
*
testing
.
T
)
{
adm
,
err
:=
Dial
(
address
,
secret
,
time
.
Duration
(
0
))
if
err
!=
nil
{
t
.
Fatal
(
"Dial():"
,
err
)
return
}
defer
adm
.
Close
()
addr
,
err
:=
adm
.
RemoteAddr
()
if
err
!=
nil
{
t
.
Fatal
(
"RemoteAddr():"
,
err
)
return
}
if
addr
.
Network
()
!=
"tcp"
{
t
.
Errorf
(
"RemoteAddr().Network() want=tcp got=%v"
,
addr
.
Network
())
}
str
:=
addr
.
String
()
if
str
==
""
{
t
.
Error
(
"RemoteAddr().String() is empty"
)
}
if
str
!=
address
{
t
.
Errorf
(
"RemoteAddr().String() want=%v got=%v"
,
address
,
str
)
}
var
n
*
Admin
if
addr
,
err
=
n
.
RemoteAddr
();
err
==
nil
{
t
.
Error
(
"Expected nil.RemoteAddr() to fail"
)
}
unconn
:=
new
(
Admin
)
if
addr
,
err
=
unconn
.
RemoteAddr
();
err
==
nil
{
t
.
Error
(
"Expected unconnected.RemoteAddr() to fail"
)
}
}
func
TestListenerAddr
(
t
*
testing
.
T
)
{
adm
,
err
:=
Listen
(
"127.0.0.1:0"
)
if
err
!=
nil
{
t
.
Fatal
(
"Listen():"
,
err
)
return
}
defer
adm
.
Close
()
addr
,
err
:=
adm
.
ListenerAddr
()
if
err
!=
nil
{
t
.
Fatal
(
"ListenerAddr():"
,
err
)
return
}
if
addr
.
Network
()
!=
"tcp"
{
t
.
Errorf
(
"ListenerAddr().Network() want=tcp got=%v"
,
addr
.
Network
())
}
str
:=
addr
.
String
()
if
str
==
""
{
t
.
Error
(
"ListenerAddr().String() is empty"
)
}
var
n
*
Admin
if
addr
,
err
=
n
.
ListenerAddr
();
err
==
nil
{
t
.
Error
(
"Expected nil.ListenerAddr() to fail"
)
}
unconn
:=
new
(
Admin
)
if
addr
,
err
=
unconn
.
ListenerAddr
();
err
==
nil
{
t
.
Error
(
"Expected unconnected.ListenerAddr() to fail"
)
}
}
func
TestCommand
(
t
*
testing
.
T
)
{
adm
,
err
:=
Dial
(
address
,
secret
,
time
.
Duration
(
0
))
if
err
!=
nil
{
t
.
Fatal
(
"Dial():"
,
err
)
return
}
defer
adm
.
Close
()
resp
,
err
:=
adm
.
Command
(
"banner"
)
if
err
!=
nil
{
t
.
Fatal
(
"Command():"
,
err
)
return
}
if
resp
.
Msg
==
""
{
t
.
Error
(
"resp.Msg after Command() is empty"
)
}
if
resp
.
Msg
!=
adm
.
Banner
{
t
.
Errorf
(
"resp.Msg after Command(banner) want=%v got=%v"
,
resp
.
Msg
,
adm
.
Banner
)
}
if
resp
.
Code
<
100
||
resp
.
Code
>
999
{
t
.
Errorf
(
"Expected 100 <= resp.Code <= 999 got=%v"
,
resp
.
Code
)
}
for
_
,
cmd
:=
range
[]
string
{
"ping"
,
"status"
}
{
resp
,
err
:=
adm
.
Command
(
cmd
)
if
err
!=
nil
{
t
.
Fatalf
(
"Command(%s): %v"
,
cmd
,
err
)
return
}
if
resp
.
Msg
==
""
{
t
.
Errorf
(
"resp.Msg after Command(%s) is empty"
,
cmd
)
}
if
resp
.
Code
<
100
||
resp
.
Code
>
999
{
t
.
Errorf
(
"Command(%s): Expected 100 <= resp.Code "
+
"<= 999 got=%v"
,
cmd
,
resp
.
Code
)
}
}
var
n
*
Admin
if
resp
,
err
=
n
.
Command
(
"banner"
);
err
==
nil
{
t
.
Error
(
"Expected nil.Command() to fail"
)
}
unconn
:=
new
(
Admin
)
if
resp
,
err
=
unconn
.
Command
(
"banner"
);
err
==
nil
{
t
.
Error
(
"Expected unconnected.Command() to fail"
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment