mirror of
https://github.com/portainer/portainer.git
synced 2025-07-22 23:09:41 +02:00
feat(backup): Add backup/restore to the server
This commit is contained in:
parent
c04bbb5775
commit
a3ec2f8e85
65 changed files with 2394 additions and 564 deletions
217
api/http/offlinegate/offlinegate_test.go
Normal file
217
api/http/offlinegate/offlinegate_test.go
Normal file
|
@ -0,0 +1,217 @@
|
|||
package offlinegate
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_canLockAndUnlock(t *testing.T) {
|
||||
o := NewOfflineGate()
|
||||
|
||||
unlock := o.Lock()
|
||||
unlock()
|
||||
}
|
||||
|
||||
func Test_hasToBeUnlockedToLockAgain(t *testing.T) {
|
||||
// scenario:
|
||||
// 1. first routine starts and locks the gate
|
||||
// 2. first routine starts a second and wait for the second to start
|
||||
// 3. second start but waits for the gate to be released
|
||||
// 4. first continues and unlocks the gate, when done
|
||||
// 5. second be able to continue
|
||||
// 6. second lock the gate, does the job and unlocks it
|
||||
|
||||
o := NewOfflineGate()
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
|
||||
result := make([]string, 0, 2)
|
||||
go func() {
|
||||
unlock := o.Lock()
|
||||
defer unlock()
|
||||
waitForSecondToStart := sync.WaitGroup{}
|
||||
waitForSecondToStart.Add(1)
|
||||
go func() {
|
||||
waitForSecondToStart.Done()
|
||||
unlock := o.Lock()
|
||||
defer unlock()
|
||||
result = append(result, "second")
|
||||
wg.Done()
|
||||
}()
|
||||
waitForSecondToStart.Wait()
|
||||
result = append(result, "first")
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if len(result) != 2 || result[0] != "first" || result[1] != "second" {
|
||||
t.Error("Second call have disresregarded a raised lock")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_waitChannelWillBeEmpty_ifGateIsUnlocked(t *testing.T) {
|
||||
o := NewOfflineGate()
|
||||
|
||||
signalingCh := o.Watch()
|
||||
if signalingCh != nil {
|
||||
t.Error("Signaling channel should be empty")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_startWaitingForSignal_beforeGateGetsUnlocked(t *testing.T) {
|
||||
// scenario:
|
||||
// 1. main routing locks the gate and waits for a consumer to start up
|
||||
// 2. consumer starts up, notifies main and begins waiting for the gate to be unlocked
|
||||
// 3. main unlocks the gate
|
||||
// 4. consumer be able to continue
|
||||
|
||||
o := NewOfflineGate()
|
||||
unlock := o.Lock()
|
||||
|
||||
signalingCh := o.Watch()
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
readerIsReady := sync.WaitGroup{}
|
||||
readerIsReady.Add(1)
|
||||
|
||||
go func(t *testing.T) {
|
||||
readerIsReady.Done()
|
||||
|
||||
// either wait for a signal or timeout
|
||||
select {
|
||||
case <-signalingCh:
|
||||
case <-time.After(10 * time.Second):
|
||||
t.Error("Failed to wait for a signal, exit by timeout")
|
||||
}
|
||||
wg.Done()
|
||||
}(t)
|
||||
|
||||
readerIsReady.Wait()
|
||||
unlock()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func Test_startWaitingForSignal_afterGateGetsUnlocked(t *testing.T) {
|
||||
// scenario:
|
||||
// 1. main routing locks, gets waiting channel and unlocks
|
||||
// 2. consumer starts up and begins waiting for the gate to be unlocked
|
||||
// 3. consumer gets signal immediately and continues
|
||||
|
||||
o := NewOfflineGate()
|
||||
unlock := o.Lock()
|
||||
signalingCh := o.Watch()
|
||||
unlock()
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
|
||||
go func(t *testing.T) {
|
||||
// either wait for a signal or timeout
|
||||
select {
|
||||
case <-signalingCh:
|
||||
case <-time.After(10 * time.Second):
|
||||
t.Error("Failed to wait for a signal, exit by timeout")
|
||||
}
|
||||
wg.Done()
|
||||
}(t)
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func Test_waitingMiddleware_executesImmediately_whenNotLocked(t *testing.T) {
|
||||
// scenario:
|
||||
// 1. create an gate
|
||||
// 2. kick off a waiting middleware that will release immediately as gate wasn't locked
|
||||
// 3. middleware shouldn't timeout
|
||||
|
||||
o := NewOfflineGate()
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "/", nil)
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
timeout := 2 * time.Second
|
||||
start := time.Now()
|
||||
o.WaitingMiddleware(timeout, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
elapsed := time.Since(start)
|
||||
if elapsed >= timeout {
|
||||
t.Error("WaitingMiddleware had likely timeout, when it shouldn't")
|
||||
}
|
||||
w.Write([]byte("success"))
|
||||
})).ServeHTTP(response, request)
|
||||
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
if string(body) != "success" {
|
||||
t.Error("Didn't receive expected result from the hanlder")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_waitingMiddleware_waitsForTheLockToBeReleased(t *testing.T) {
|
||||
// scenario:
|
||||
// 1. create an gate and lock it
|
||||
// 2. kick off a routing that will unlock the gate after 1 second
|
||||
// 3. kick off a waiting middleware that will wait for lock to be eventually released
|
||||
// 4. middleware shouldn't timeout
|
||||
|
||||
o := NewOfflineGate()
|
||||
unlock := o.Lock()
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "/", nil)
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
unlock()
|
||||
}()
|
||||
|
||||
timeout := 10 * time.Second
|
||||
start := time.Now()
|
||||
o.WaitingMiddleware(timeout, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
elapsed := time.Since(start)
|
||||
if elapsed >= timeout {
|
||||
t.Error("WaitingMiddleware had likely timeout, when it shouldn't")
|
||||
}
|
||||
w.Write([]byte("success"))
|
||||
})).ServeHTTP(response, request)
|
||||
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
if string(body) != "success" {
|
||||
t.Error("Didn't receive expected result from the hanlder")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_waitingMiddleware_mayTimeout_whenLockedForTooLong(t *testing.T) {
|
||||
/*
|
||||
scenario:
|
||||
1. create an gate and lock it
|
||||
2. kick off a waiting middleware that will wait for lock to be eventually released
|
||||
3. because we never unlocked the gate, middleware suppose to timeout
|
||||
*/
|
||||
o := NewOfflineGate()
|
||||
o.Lock()
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "/", nil)
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
timeout := 1 * time.Second
|
||||
start := time.Now()
|
||||
o.WaitingMiddleware(timeout, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
elapsed := time.Since(start)
|
||||
if elapsed < timeout {
|
||||
t.Error("WaitingMiddleware suppose to timeout, but it didnt")
|
||||
}
|
||||
w.Write([]byte("success"))
|
||||
})).ServeHTTP(response, request)
|
||||
|
||||
assert.Equal(t, http.StatusRequestTimeout, response.Result().StatusCode, "Request support to timeout waiting for the gate")
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue