How to use Abort method of command Package

Best Ginkgo code snippet using command.Abort

Run Ginkgo automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

with_transactions_test.go

Source: with_transactions_test.go Github

copy
1// Copyright (C) MongoDB, Inc. 2017-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7package mongo
8
9import (
10	"context"
11	"errors"
12	"math"
13	"strconv"
14	"strings"
15	"testing"
16	"time"
17
18	"go.mongodb.org/mongo-driver/bson"
19	"go.mongodb.org/mongo-driver/event"
20	"go.mongodb.org/mongo-driver/internal/testutil"
21	"go.mongodb.org/mongo-driver/internal/testutil/assert"
22	"go.mongodb.org/mongo-driver/mongo/description"
23	"go.mongodb.org/mongo-driver/mongo/options"
24	"go.mongodb.org/mongo-driver/mongo/readpref"
25	"go.mongodb.org/mongo-driver/mongo/writeconcern"
26	"go.mongodb.org/mongo-driver/x/mongo/driver"
27	"go.mongodb.org/mongo-driver/x/mongo/driver/topology"
28)
29
30var (
31	connsCheckedOut        int
32	errorInterrupted       int32 = 11601
33	withTxnStartedEvents   []*event.CommandStartedEvent
34	withTxnSucceededEvents []*event.CommandSucceededEvent
35	withTxnFailedEvents    []*event.CommandFailedEvent
36)
37
38type wrappedError struct {
39	err error
40}
41
42func (we wrappedError) Error() string {
43	return we.err.Error()
44}
45
46func (we wrappedError) Unwrap() error {
47	return we.err
48}
49
50func TestConvenientTransactions(t *testing.T) {
51	client := setupConvenientTransactions(t)
52	db := client.Database("TestConvenientTransactions")
53	dbAdmin := client.Database("admin")
54
55	defer func() {
56		sessions := client.NumberSessionsInProgress()
57		conns := connsCheckedOut
58
59		err := dbAdmin.RunCommand(bgCtx, bson.D{
60			{"killAllSessions", bson.A{}},
61		}).Err()
62		if err != nil {
63			if ce, ok := err.(CommandError); !ok || ce.Code != errorInterrupted {
64				t.Fatalf("killAllSessions error: %v", err)
65			}
66		}
67
68		_ = db.Drop(bgCtx)
69		_ = client.Disconnect(bgCtx)
70
71		assert.Equal(t, 0, sessions, "%v sessions checked out", sessions)
72		assert.Equal(t, 0, conns, "%v connections checked out", conns)
73	}()
74
75	t.Run("callback raises custom error", func(t *testing.T) {
76		coll := db.Collection(t.Name())
77		_, err := coll.InsertOne(bgCtx, bson.D{{"x", 1}})
78		assert.Nil(t, err, "InsertOne error: %v", err)
79
80		sess, err := client.StartSession()
81		assert.Nil(t, err, "StartSession error: %v", err)
82		defer sess.EndSession(context.Background())
83
84		testErr := errors.New("test error")
85		_, err = sess.WithTransaction(context.Background(), func(sessCtx SessionContext) (interface{}, error) {
86			return nil, testErr
87		})
88		assert.Equal(t, testErr, err, "expected error %v, got %v", testErr, err)
89	})
90	t.Run("callback returns value", func(t *testing.T) {
91		coll := db.Collection(t.Name())
92		_, err := coll.InsertOne(bgCtx, bson.D{{"x", 1}})
93		assert.Nil(t, err, "InsertOne error: %v", err)
94
95		sess, err := client.StartSession()
96		assert.Nil(t, err, "StartSession error: %v", err)
97		defer sess.EndSession(context.Background())
98
99		res, err := sess.WithTransaction(context.Background(), func(sessCtx SessionContext) (interface{}, error) {
100			return false, nil
101		})
102		assert.Nil(t, err, "WithTransaction error: %v", err)
103		resBool, ok := res.(bool)
104		assert.True(t, ok, "expected result type %T, got %T", false, res)
105		assert.False(t, resBool, "expected result false, got %v", resBool)
106	})
107	t.Run("retry timeout enforced", func(t *testing.T) {
108		withTransactionTimeout = time.Second
109
110		coll := db.Collection(t.Name())
111		_, err := coll.InsertOne(bgCtx, bson.D{{"x", 1}})
112		assert.Nil(t, err, "InsertOne error: %v", err)
113
114		t.Run("transient transaction error", func(t *testing.T) {
115			sess, err := client.StartSession()
116			assert.Nil(t, err, "StartSession error: %v", err)
117			defer sess.EndSession(context.Background())
118
119			_, err = sess.WithTransaction(context.Background(), func(sessCtx SessionContext) (interface{}, error) {
120				return nil, CommandError{Name: "test Error", Labels: []string{driver.TransientTransactionError}}
121			})
122			assert.NotNil(t, err, "expected WithTransaction error, got nil")
123			cmdErr, ok := err.(CommandError)
124			assert.True(t, ok, "expected error type %T, got %T", CommandError{}, err)
125			assert.True(t, cmdErr.HasErrorLabel(driver.TransientTransactionError),
126				"expected error with label %v, got %v", driver.TransientTransactionError, cmdErr)
127		})
128		t.Run("unknown transaction commit result", func(t *testing.T) {
129			//set failpoint
130			failpoint := bson.D{{"configureFailPoint", "failCommand"},
131				{"mode", "alwaysOn"},
132				{"data", bson.D{
133					{"failCommands", bson.A{"commitTransaction"}},
134					{"closeConnection", true},
135				}},
136			}
137			err = dbAdmin.RunCommand(bgCtx, failpoint).Err()
138			assert.Nil(t, err, "error setting failpoint: %v", err)
139			defer func() {
140				err = dbAdmin.RunCommand(bgCtx, bson.D{
141					{"configureFailPoint", "failCommand"},
142					{"mode", "off"},
143				}).Err()
144				assert.Nil(t, err, "error turning off failpoint: %v", err)
145			}()
146
147			sess, err := client.StartSession()
148			assert.Nil(t, err, "StartSession error: %v", err)
149			defer sess.EndSession(context.Background())
150
151			_, err = sess.WithTransaction(context.Background(), func(sessCtx SessionContext) (interface{}, error) {
152				_, err := coll.InsertOne(sessCtx, bson.D{{"x", 1}})
153				return nil, err
154			})
155			assert.NotNil(t, err, "expected WithTransaction error, got nil")
156			cmdErr, ok := err.(CommandError)
157			assert.True(t, ok, "expected error type %T, got %T", CommandError{}, err)
158			assert.True(t, cmdErr.HasErrorLabel(driver.UnknownTransactionCommitResult),
159				"expected error with label %v, got %v", driver.UnknownTransactionCommitResult, cmdErr)
160		})
161		t.Run("commit transient transaction error", func(t *testing.T) {
162			//set failpoint
163			failpoint := bson.D{{"configureFailPoint", "failCommand"},
164				{"mode", "alwaysOn"},
165				{"data", bson.D{
166					{"failCommands", bson.A{"commitTransaction"}},
167					{"errorCode", 251},
168				}},
169			}
170			err = dbAdmin.RunCommand(bgCtx, failpoint).Err()
171			assert.Nil(t, err, "error setting failpoint: %v", err)
172			defer func() {
173				err = dbAdmin.RunCommand(bgCtx, bson.D{
174					{"configureFailPoint", "failCommand"},
175					{"mode", "off"},
176				}).Err()
177				assert.Nil(t, err, "error turning off failpoint: %v", err)
178			}()
179
180			sess, err := client.StartSession()
181			assert.Nil(t, err, "StartSession error: %v", err)
182			defer sess.EndSession(context.Background())
183
184			_, err = sess.WithTransaction(context.Background(), func(sessCtx SessionContext) (interface{}, error) {
185				_, err := coll.InsertOne(sessCtx, bson.D{{"x", 1}})
186				return nil, err
187			})
188			assert.NotNil(t, err, "expected WithTransaction error, got nil")
189			cmdErr, ok := err.(CommandError)
190			assert.True(t, ok, "expected error type %T, got %T", CommandError{}, err)
191			assert.True(t, cmdErr.HasErrorLabel(driver.TransientTransactionError),
192				"expected error with label %v, got %v", driver.TransientTransactionError, cmdErr)
193		})
194	})
195	t.Run("abortTransaction does not time out", func(t *testing.T) {
196		// Create a special CommandMonitor that only records information about abortTransaction events and also
197		// records the Context used in the CommandStartedEvent listener.
198		var abortStarted []*event.CommandStartedEvent
199		var abortSucceeded []*event.CommandSucceededEvent
200		var abortFailed []*event.CommandFailedEvent
201		var abortCtx context.Context
202		monitor := &event.CommandMonitor{
203			Started: func(ctx context.Context, evt *event.CommandStartedEvent) {
204				if evt.CommandName == "abortTransaction" {
205					abortStarted = append(abortStarted, evt)
206					if abortCtx == nil {
207						abortCtx = ctx
208					}
209				}
210			},
211			Succeeded: func(_ context.Context, evt *event.CommandSucceededEvent) {
212				if evt.CommandName == "abortTransaction" {
213					abortSucceeded = append(abortSucceeded, evt)
214				}
215			},
216			Failed: func(_ context.Context, evt *event.CommandFailedEvent) {
217				if evt.CommandName == "abortTransaction" {
218					abortFailed = append(abortFailed, evt)
219				}
220			},
221		}
222
223		// Set up a new Client using the command monitor defined above get a handle to a collection. The collection
224		// needs to be explicitly created on the server because implicit collection creation is not allowed in
225		// transactions for server versions <= 4.2.
226		client := setupConvenientTransactions(t, options.Client().SetMonitor(monitor))
227		db := client.Database("foo")
228		coll := db.Collection("bar")
229		err := db.RunCommand(bgCtx, bson.D{{"create", coll.Name()}}).Err()
230		assert.Nil(t, err, "error creating collection on server: %v\n", err)
231
232		sess, err := client.StartSession()
233		assert.Nil(t, err, "StartSession error: %v", err)
234		defer func() {
235			sess.EndSession(bgCtx)
236			_ = coll.Drop(bgCtx)
237			_ = client.Disconnect(bgCtx)
238		}()
239
240		// Create a cancellable Context with a value for ctxKey.
241		type ctxKey struct{}
242		ctx, cancel := context.WithCancel(context.WithValue(context.Background(), ctxKey{}, "foobar"))
243		defer cancel()
244
245		// The WithTransaction callback does an Insert to ensure that the txn has been started server-side. After the
246		// insert succeeds, it cancels the Context created above and returns a non-retryable error, which forces
247		// WithTransaction to abort the txn.
248		callbackErr := errors.New("error")
249		callback := func(sc SessionContext) (interface{}, error) {
250			_, err = coll.InsertOne(sc, bson.D{{"x", 1}})
251			if err != nil {
252				return nil, err
253			}
254
255			cancel()
256			return nil, callbackErr
257		}
258
259		_, err = sess.WithTransaction(ctx, callback)
260		assert.Equal(t, callbackErr, err, "expected WithTransaction error %v, got %v", callbackErr, err)
261
262		// Assert that abortTransaction was sent once and succeede.
263		assert.Equal(t, 1, len(abortStarted), "expected 1 abortTransaction started event, got %d", len(abortStarted))
264		assert.Equal(t, 1, len(abortSucceeded), "expected 1 abortTransaction succeeded event, got %d",
265			len(abortSucceeded))
266		assert.Equal(t, 0, len(abortFailed), "expected 0 abortTransaction failed event, got %d", len(abortFailed))
267
268		// Assert that the Context propagated to the CommandStartedEvent listener for abortTransaction contained a value
269		// for ctxKey.
270		ctxValue, ok := abortCtx.Value(ctxKey{}).(string)
271		assert.True(t, ok, "expected context for abortTransaction to contain ctxKey")
272		assert.Equal(t, "foobar", ctxValue, "expected value for ctxKey to be 'world', got %s", ctxValue)
273	})
274	t.Run("commitTransaction timeout allows abortTransaction", func(t *testing.T) {
275		// Create a special CommandMonitor that only records information about abortTransaction events.
276		var abortStarted []*event.CommandStartedEvent
277		var abortSucceeded []*event.CommandSucceededEvent
278		var abortFailed []*event.CommandFailedEvent
279		monitor := &event.CommandMonitor{
280			Started: func(ctx context.Context, evt *event.CommandStartedEvent) {
281				if evt.CommandName == "abortTransaction" {
282					abortStarted = append(abortStarted, evt)
283				}
284			},
285			Succeeded: func(_ context.Context, evt *event.CommandSucceededEvent) {
286				if evt.CommandName == "abortTransaction" {
287					abortSucceeded = append(abortSucceeded, evt)
288				}
289			},
290			Failed: func(_ context.Context, evt *event.CommandFailedEvent) {
291				if evt.CommandName == "abortTransaction" {
292					abortFailed = append(abortFailed, evt)
293				}
294			},
295		}
296
297		// Set up a new Client using the command monitor defined above get a handle to a collection. The collection
298		// needs to be explicitly created on the server because implicit collection creation is not allowed in
299		// transactions for server versions <= 4.2.
300		client := setupConvenientTransactions(t, options.Client().SetMonitor(monitor))
301		db := client.Database("foo")
302		coll := db.Collection("test")
303		defer func() {
304			_ = coll.Drop(bgCtx)
305		}()
306
307		err := db.RunCommand(bgCtx, bson.D{{"create", coll.Name()}}).Err()
308		assert.Nil(t, err, "error creating collection on server: %v", err)
309
310		// Start session.
311		session, err := client.StartSession()
312		defer session.EndSession(bgCtx)
313		assert.Nil(t, err, "StartSession error: %v", err)
314
315		_ = WithSession(bgCtx, session, func(sessionContext SessionContext) error {
316			// Start transaction.
317			err = session.StartTransaction()
318			assert.Nil(t, err, "StartTransaction error: %v", err)
319
320			// Insert a document.
321			_, err := coll.InsertOne(sessionContext, bson.D{{"val", 17}})
322			assert.Nil(t, err, "InsertOne error: %v", err)
323
324			// Set a timeout of 0 for commitTransaction.
325			commitTimeoutCtx, commitCancel := context.WithTimeout(sessionContext, 0)
326			defer commitCancel()
327
328			// CommitTransaction results in context.DeadlineExceeded.
329			commitErr := session.CommitTransaction(commitTimeoutCtx)
330			assert.True(t, IsTimeout(commitErr),
331				"expected timeout error error; got %v", commitErr)
332
333			// Assert session state is not Committed.
334			clientSession := session.(XSession).ClientSession()
335			assert.False(t, clientSession.TransactionCommitted(), "expected session state to not be Committed")
336
337			// AbortTransaction without error.
338			abortErr := session.AbortTransaction(context.Background())
339			assert.Nil(t, abortErr, "AbortTransaction error: %v", abortErr)
340
341			// Assert that AbortTransaction was started once and succeeded.
342			assert.Equal(t, 1, len(abortStarted), "expected 1 abortTransaction started event, got %d", len(abortStarted))
343			assert.Equal(t, 1, len(abortSucceeded), "expected 1 abortTransaction succeeded event, got %d",
344				len(abortSucceeded))
345			assert.Equal(t, 0, len(abortFailed), "expected 0 abortTransaction failed events, got %d", len(abortFailed))
346
347			return nil
348		})
349	})
350	t.Run("commitTransaction timeout does not retry", func(t *testing.T) {
351		withTransactionTimeout = 2 * time.Second
352
353		coll := db.Collection("test")
354		// Explicitly create the collection on server because implicit collection creation is not allowed in
355		// transactions for server versions <= 4.2.
356		err := db.RunCommand(bgCtx, bson.D{{"create", coll.Name()}}).Err()
357		assert.Nil(t, err, "error creating collection on server: %v", err)
358		defer func() {
359			_ = coll.Drop(bgCtx)
360		}()
361
362		// Start session.
363		sess, err := client.StartSession()
364		assert.Nil(t, err, "StartSession error: %v", err)
365		defer sess.EndSession(context.Background())
366
367		// Defer running killAllSessions to manually close open transaction.
368		defer func() {
369			err := dbAdmin.RunCommand(bgCtx, bson.D{
370				{"killAllSessions", bson.A{}},
371			}).Err()
372			if err != nil {
373				if ce, ok := err.(CommandError); !ok || ce.Code != errorInterrupted {
374					t.Fatalf("killAllSessions error: %v", err)
375				}
376			}
377		}()
378
379		// Create context to manually cancel in callback.
380		cancelCtx, cancel := context.WithCancel(bgCtx)
381		defer cancel()
382
383		// Insert a document within a session and manually cancel context.
384		callback := func() {
385			_, _ = sess.WithTransaction(cancelCtx, func(sessCtx SessionContext) (interface{}, error) {
386				_, err := coll.InsertOne(sessCtx, bson.M{"x": 1})
387				assert.Nil(t, err, "InsertOne error: %v", err)
388				cancel()
389				return nil, nil
390			})
391		}
392
393		// Assert that transaction is canceled within 500ms and not 2 seconds.
394		assert.Soon(t, callback, 500*time.Millisecond)
395	})
396	t.Run("wrapped transient transaction error retried", func(t *testing.T) {
397		sess, err := client.StartSession()
398		assert.Nil(t, err, "StartSession error: %v", err)
399		defer sess.EndSession(context.Background())
400
401		// returnError tracks whether or not the callback is being retried
402		returnError := true
403		res, err := sess.WithTransaction(context.Background(), func(sessCtx SessionContext) (interface{}, error) {
404			if returnError {
405				returnError = false
406				return nil, wrappedError{
407					CommandError{
408						Name:   "test Error",
409						Labels: []string{driver.TransientTransactionError},
410					},
411				}
412			}
413			return false, nil
414		})
415		assert.Nil(t, err, "WithTransaction error: %v", err)
416		resBool, ok := res.(bool)
417		assert.True(t, ok, "expected result type %T, got %T", false, res)
418		assert.False(t, resBool, "expected result false, got %v", resBool)
419	})
420	t.Run("expired context before commitTransaction does not retry", func(t *testing.T) {
421		withTransactionTimeout = 2 * time.Second
422
423		coll := db.Collection("test")
424		// Explicitly create the collection on server because implicit collection creation is not allowed in
425		// transactions for server versions <= 4.2.
426		err := db.RunCommand(bgCtx, bson.D{{"create", coll.Name()}}).Err()
427		assert.Nil(t, err, "error creating collection on server: %v", err)
428		defer func() {
429			_ = coll.Drop(bgCtx)
430		}()
431
432		sess, err := client.StartSession()
433		assert.Nil(t, err, "StartSession error: %v", err)
434		defer sess.EndSession(context.Background())
435
436		// Create context with short timeout.
437		withTransactionContext, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
438		defer cancel()
439		callback := func() {
440			_, _ = sess.WithTransaction(withTransactionContext, func(sessCtx SessionContext) (interface{}, error) {
441				_, err := coll.InsertOne(sessCtx, bson.D{{}})
442				return nil, err
443			})
444		}
445
446		// Assert that transaction fails within 500ms and not 2 seconds.
447		assert.Soon(t, callback, 500*time.Millisecond)
448	})
449	t.Run("canceled context before commitTransaction does not retry", func(t *testing.T) {
450		withTransactionTimeout = 2 * time.Second
451
452		coll := db.Collection("test")
453		// Explicitly create the collection on server because implicit collection creation is not allowed in
454		// transactions for server versions <= 4.2.
455		err := db.RunCommand(bgCtx, bson.D{{"create", coll.Name()}}).Err()
456		assert.Nil(t, err, "error creating collection on server: %v", err)
457		defer func() {
458			_ = coll.Drop(bgCtx)
459		}()
460
461		sess, err := client.StartSession()
462		assert.Nil(t, err, "StartSession error: %v", err)
463		defer sess.EndSession(context.Background())
464
465		// Create context and cancel it immediately.
466		withTransactionContext, cancel := context.WithTimeout(context.Background(), 2*time.Second)
467		cancel()
468		callback := func() {
469			_, _ = sess.WithTransaction(withTransactionContext, func(sessCtx SessionContext) (interface{}, error) {
470				_, err := coll.InsertOne(sessCtx, bson.D{{}})
471				return nil, err
472			})
473		}
474
475		// Assert that transaction fails within 500ms and not 2 seconds.
476		assert.Soon(t, callback, 500*time.Millisecond)
477	})
478	t.Run("slow operation before commitTransaction retries", func(t *testing.T) {
479		withTransactionTimeout = 2 * time.Second
480
481		coll := db.Collection("test")
482		// Explicitly create the collection on server because implicit collection creation is not allowed in
483		// transactions for server versions <= 4.2.
484		err := db.RunCommand(bgCtx, bson.D{{"create", coll.Name()}}).Err()
485		assert.Nil(t, err, "error creating collection on server: %v", err)
486		defer func() {
487			_ = coll.Drop(bgCtx)
488		}()
489
490		// Set failpoint to block insertOne once for 500ms.
491		failpoint := bson.D{{"configureFailPoint", "failCommand"},
492			{"mode", bson.D{
493				{"times", 1},
494			}},
495			{"data", bson.D{
496				{"failCommands", bson.A{"insert"}},
497				{"blockConnection", true},
498				{"blockTimeMS", 500},
499			}},
500		}
501		err = dbAdmin.RunCommand(bgCtx, failpoint).Err()
502		assert.Nil(t, err, "error setting failpoint: %v", err)
503		defer func() {
504			err = dbAdmin.RunCommand(bgCtx, bson.D{
505				{"configureFailPoint", "failCommand"},
506				{"mode", "off"},
507			}).Err()
508			assert.Nil(t, err, "error turning off failpoint: %v", err)
509		}()
510
511		sess, err := client.StartSession()
512		assert.Nil(t, err, "StartSession error: %v", err)
513		defer sess.EndSession(context.Background())
514
515		callback := func() {
516			_, err = sess.WithTransaction(context.Background(), func(sessCtx SessionContext) (interface{}, error) {
517				// Set a timeout of 300ms to cause a timeout on first insertOne
518				// and force a retry.
519				c, cancel := context.WithTimeout(sessCtx, 300*time.Millisecond)
520				defer cancel()
521
522				_, err := coll.InsertOne(c, bson.D{{}})
523				return nil, err
524			})
525			assert.Nil(t, err, "WithTransaction error: %v", err)
526		}
527
528		// Assert that transaction passes within 2 seconds.
529		assert.Soon(t, callback, 2*time.Second)
530	})
531}
532
533func setupConvenientTransactions(t *testing.T, extraClientOpts ...*options.ClientOptions) *Client {
534	cs := testutil.ConnString(t)
535	poolMonitor := &event.PoolMonitor{
536		Event: func(evt *event.PoolEvent) {
537			switch evt.Type {
538			case event.GetSucceeded:
539				connsCheckedOut++
540			case event.ConnectionReturned:
541				connsCheckedOut--
542			}
543		},
544	}
545
546	baseClientOpts := options.Client().
547		ApplyURI(cs.Original).
548		SetReadPreference(readpref.Primary()).
549		SetWriteConcern(writeconcern.New(writeconcern.WMajority())).
550		SetPoolMonitor(poolMonitor)
551	testutil.AddTestServerAPIVersion(baseClientOpts)
552	fullClientOpts := []*options.ClientOptions{baseClientOpts}
553	fullClientOpts = append(fullClientOpts, extraClientOpts...)
554
555	client, err := Connect(bgCtx, fullClientOpts...)
556	assert.Nil(t, err, "Connect error: %v", err)
557
558	version, err := getServerVersion(client.Database("admin"))
559	assert.Nil(t, err, "getServerVersion error: %v", err)
560	topoKind := client.deployment.(*topology.Topology).Kind()
561	if compareVersions(t, version, "4.1") < 0 || topoKind == description.Single {
562		t.Skip("skipping standalones and versions < 4.1")
563	}
564
565	if topoKind != description.Sharded {
566		return client
567	}
568
569	// For sharded clusters, disconnect the previous Client and create a new one that's pinned to a single mongos.
570	_ = client.Disconnect(bgCtx)
571	fullClientOpts = append(fullClientOpts, options.Client().SetHosts([]string{cs.Hosts[0]}))
572	client, err = Connect(bgCtx, fullClientOpts...)
573	assert.Nil(t, err, "Connect error: %v", err)
574	return client
575}
576
577func getServerVersion(db *Database) (string, error) {
578	serverStatus, err := db.RunCommand(
579		context.Background(),
580		bson.D{{"serverStatus", 1}},
581	).DecodeBytes()
582	if err != nil {
583		return "", err
584	}
585
586	version, err := serverStatus.LookupErr("version")
587	if err != nil {
588		return "", err
589	}
590
591	return version.StringValue(), nil
592}
593
594// compareVersions compares two version number strings (i.e. positive integers separated by
595// periods). Comparisons are done to the lesser precision of the two versions. For example, 3.2 is
596// considered equal to 3.2.11, whereas 3.2.0 is considered less than 3.2.11.
597//
598// Returns a positive int if version1 is greater than version2, a negative int if version1 is less
599// than version2, and 0 if version1 is equal to version2.
600func compareVersions(t *testing.T, v1 string, v2 string) int {
601	n1 := strings.Split(v1, ".")
602	n2 := strings.Split(v2, ".")
603
604	for i := 0; i < int(math.Min(float64(len(n1)), float64(len(n2)))); i++ {
605		i1, err := strconv.Atoi(n1[i])
606		if err != nil {
607			return 1
608		}
609
610		i2, err := strconv.Atoi(n2[i])
611		if err != nil {
612			return -1
613		}
614
615		difference := i1 - i2
616		if difference != 0 {
617			return difference
618		}
619	}
620
621	return 0
622}
623
Full Screen

abort.go

Source: abort.go Github

copy
1// Copyright 2019 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.
3
4package model
5
6import (
7	"fmt"
8
9	"github.com/juju/cmd"
10	"github.com/juju/errors"
11	"github.com/juju/gnuflag"
12
13	"github.com/juju/juju/api/modelgeneration"
14	jujucmd "github.com/juju/juju/cmd"
15	"github.com/juju/juju/cmd/modelcmd"
16	"github.com/juju/juju/core/model"
17)
18
19const (
20	abortSummary = "Aborts a branch in the model."
21	abortDoc     = `
22Aborting a branch aborts changes made to that branch.  A branch
23can only be aborted if no units are tracked by that branch.
24
25Examples:
26    juju abort upgrade-postgresql
27
28See also:
29    track
30    branch
31    commit
32    add-branch
33    diff
34`
35)
36
37// NewAbortCommand wraps abortCommand with sane model settings.
38func NewAbortCommand() cmd.Command {
39	return modelcmd.Wrap(&abortCommand{})
40}
41
42// abortCommand supplies the "add-branch" CLI command used to add a new branch to
43// the current model.
44type abortCommand struct {
45	modelcmd.ModelCommandBase
46
47	api AbortCommandAPI
48
49	branchName string
50}
51
52// AbortCommandAPI describes API methods required
53// to execute the branch command.
54//go:generate go run github.com/golang/mock/mockgen -package mocks -destination ./mocks/abort_mock.go github.com/juju/juju/cmd/juju/model AbortCommandAPI
55type AbortCommandAPI interface {
56	Close() error
57
58	// Abort aborts an existing branch to the model.
59	AbortBranch(branchName string) error
60
61	// HasActiveBranch returns true if the model has an
62	// "in-flight" branch with the input name.
63	HasActiveBranch(branchName string) (bool, error)
64}
65
66// Info implements part of the cmd.Command interface.
67func (c *abortCommand) Info() *cmd.Info {
68	info := &cmd.Info{
69		Name:    "abort",
70		Args:    "<branch name>",
71		Purpose: abortSummary,
72		Doc:     abortDoc,
73	}
74	return jujucmd.Info(info)
75}
76
77// SetFlags implements part of the cmd.Command interface.
78func (c *abortCommand) SetFlags(f *gnuflag.FlagSet) {
79	c.ModelCommandBase.SetFlags(f)
80}
81
82// Init implements part of the cmd.Command interface.
83func (c *abortCommand) Init(args []string) error {
84	if len(args) != 1 {
85		return errors.Errorf("expected a branch name")
86	}
87	if err := model.ValidateBranchName(args[0]); err != nil {
88		return err
89	}
90	c.branchName = args[0]
91	return nil
92}
93
94// getAPI returns the API that supplies methods
95// required to execute this command.
96func (c *abortCommand) getAPI() (AbortCommandAPI, error) {
97	if c.api != nil {
98		return c.api, nil
99	}
100	api, err := c.NewAPIRoot()
101	if err != nil {
102		return nil, errors.Annotate(err, "opening API connection")
103	}
104	client := modelgeneration.NewClient(api)
105	return client, nil
106}
107
108// Run implements the meaty part of the cmd.Command interface.
109func (c *abortCommand) Run(ctx *cmd.Context) error {
110	client, err := c.getAPI()
111	if err != nil {
112		return err
113	}
114	defer func() { _ = client.Close() }()
115
116	hasBranch, err := client.HasActiveBranch(c.branchName)
117	if err != nil {
118		return err
119	}
120	if !hasBranch {
121		return errors.Errorf("this model has no active branch %q", c.branchName)
122	}
123
124	if err = client.AbortBranch(c.branchName); err != nil {
125		return err
126	}
127
128	// Update the model store with the master branch for this model.
129	if err = c.SetActiveBranch(model.GenerationMaster); err != nil {
130		return err
131	}
132
133	msg := fmt.Sprintf("Aborting all changes in %q and closing branch.\n", c.branchName)
134	msg = msg + fmt.Sprintf("Active branch set to %q\n", model.GenerationMaster)
135	_, err = ctx.Stdout.Write([]byte(msg))
136	return err
137}
138
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Trigger Abort code on LambdaTest Cloud Grid

Execute automation tests with Abort on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)