Skip to content

Commit

Permalink
Merge pull request juju#18241 from hpidcock/merge-3.5-3.6-20241015
Browse files Browse the repository at this point in the history
chore: merge 3.5 to 3.6
  • Loading branch information
hpidcock authored Oct 15, 2024
2 parents 079cfe6 + a19374e commit 6dd513e
Show file tree
Hide file tree
Showing 16 changed files with 181 additions and 50 deletions.
3 changes: 3 additions & 0 deletions apiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1132,9 +1132,11 @@ func (srv *Server) serveConn(
h *apiHandler
)

var stateClosing <-chan struct{}
st, err := statePool.Get(resolvedModelUUID)
if err == nil {
defer st.Release()
stateClosing = st.Removing()
h, err = newAPIHandler(srv, st.State, conn, modelUUID, connectionID, host)
}
if errors.Is(err, errors.NotFound) {
Expand All @@ -1158,6 +1160,7 @@ func (srv *Server) serveConn(
select {
case <-conn.Dead():
case <-srv.tomb.Dying():
case <-stateClosing:
}
return conn.Close()
}
Expand Down
48 changes: 48 additions & 0 deletions apiserver/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import (
"github.com/juju/juju/rpc/params"
"github.com/juju/juju/state"
statetesting "github.com/juju/juju/state/testing"
"github.com/juju/juju/storage"
"github.com/juju/juju/testing"
"github.com/juju/juju/worker/gate"
"github.com/juju/juju/worker/modelcache"
Expand Down Expand Up @@ -496,3 +497,50 @@ func (s *apiserverSuite) assertEmbeddedCommand(c *gc.C, cmdArgs params.CLIComman
Error: resultErr,
})
}

// TestModelRemoveClosesRPC tests that when an RPC connection is opened
// to a model that is being removed, the connection is closed
// gracefully.
func (s *apiserverSuite) TestModelRemoveClosesRPC(c *gc.C) {
uuid, err := utils.NewUUID()
c.Assert(err, jc.ErrorIsNil)
modelConfig := testing.CustomModelConfig(c, testing.Attrs{
"name": "testing",
"uuid": uuid.String(),
})

model, st, err := s.Controller.NewModel(state.ModelArgs{
Type: state.ModelTypeIAAS,
CloudName: "dummy",
CloudRegion: "dummy-region",
Config: modelConfig,
Owner: s.Owner,
StorageProviderRegistry: storage.StaticProviderRegistry{},
})
c.Assert(err, jc.ErrorIsNil)
s.AddCleanup(func(c *gc.C) {
st.Close()
})

apiInfo := s.APIInfo(s.apiServer)
apiInfo.Tag = s.Owner
apiInfo.Password = ownerPassword
apiInfo.Nonce = ""
apiInfo.ModelTag = model.ModelTag()

conn, err := api.Open(apiInfo, api.DialOpts{})
c.Assert(err, jc.ErrorIsNil)
c.Assert(conn, gc.NotNil)
s.AddCleanup(func(c *gc.C) {
conn.Close()
})

removed, err := s.StatePool.Remove(model.UUID())
c.Assert(err, jc.ErrorIsNil)
c.Assert(removed, jc.IsFalse)

time.Sleep(testing.ShortWait)

err = conn.APICall("Pinger", 1, "", "Ping", nil, nil)
c.Assert(err, gc.ErrorMatches, `connection is shut down`)
}
3 changes: 2 additions & 1 deletion apiserver/debuglog.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type debugLogHandlerFunc func(
debugLogParams,
debugLogSocket,
<-chan struct{},
<-chan struct{},
) error

func newDebugLogHandler(
Expand Down Expand Up @@ -119,7 +120,7 @@ func (h *debugLogHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
clock := h.ctxt.srv.clock
maxDuration := h.ctxt.srv.shared.maxDebugLogDuration()

if err := h.handle(clock, maxDuration, st, params, socket, h.ctxt.stop()); err != nil {
if err := h.handle(clock, maxDuration, st, params, socket, h.ctxt.stop(), st.Removing()); err != nil {
if isBrokenPipe(err) {
logger.Tracef("debug-log handler stopped (client disconnected)")
} else {
Expand Down
3 changes: 3 additions & 0 deletions apiserver/debuglog_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func handleDebugLogDBRequest(
reqParams debugLogParams,
socket debugLogSocket,
stop <-chan struct{},
stateClosing <-chan struct{},
) error {
tailerParams := makeLogTailerParams(reqParams)
tailer, err := newLogTailer(st, tailerParams)
Expand All @@ -47,6 +48,8 @@ func handleDebugLogDBRequest(
var lineCount uint
for {
select {
case <-stateClosing:
return nil
case <-stop:
return nil
case <-timeout:
Expand Down
8 changes: 4 additions & 4 deletions apiserver/debuglog_db_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (s *debugLogDBIntSuite) TestParamConversion(c *gc.C) {

stop := make(chan struct{})
close(stop) // Stop the request immediately.
err := handleDebugLogDBRequest(s.clock, s.timeout, nil, reqParams, s.sock, stop)
err := handleDebugLogDBRequest(s.clock, s.timeout, nil, reqParams, s.sock, stop, nil)
c.Assert(err, jc.ErrorIsNil)
c.Assert(called, jc.IsTrue)
}
Expand All @@ -96,7 +96,7 @@ func (s *debugLogDBIntSuite) TestParamConversionReplay(c *gc.C) {

stop := make(chan struct{})
close(stop) // Stop the request immediately.
err := handleDebugLogDBRequest(s.clock, s.timeout, nil, reqParams, s.sock, stop)
err := handleDebugLogDBRequest(s.clock, s.timeout, nil, reqParams, s.sock, nil, stop)
c.Assert(err, jc.ErrorIsNil)
c.Assert(called, jc.IsTrue)
}
Expand Down Expand Up @@ -187,7 +187,7 @@ func (s *debugLogDBIntSuite) TestRequestStopsWhenTailerStops(c *gc.C) {
return tailer, nil
})

err := handleDebugLogDBRequest(s.clock, s.timeout, nil, debugLogParams{}, s.sock, nil)
err := handleDebugLogDBRequest(s.clock, s.timeout, nil, debugLogParams{}, s.sock, nil, nil)
c.Assert(err, jc.ErrorIsNil)
c.Assert(tailer.stopped, jc.IsTrue)
}
Expand Down Expand Up @@ -225,7 +225,7 @@ func (s *debugLogDBIntSuite) TestMaxLines(c *gc.C) {
func (s *debugLogDBIntSuite) runRequest(params debugLogParams, stop chan struct{}) chan error {
done := make(chan error)
go func() {
done <- handleDebugLogDBRequest(s.clock, s.timeout, &fakeState{}, params, s.sock, stop)
done <- handleDebugLogDBRequest(s.clock, s.timeout, &fakeState{}, params, s.sock, stop, nil)
}()
return done
}
Expand Down
18 changes: 9 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,13 @@ require (
github.com/vishvananda/netlink v1.2.1-beta.2
github.com/vmware/govmomi v0.34.1
go.uber.org/mock v0.4.0
golang.org/x/crypto v0.26.0
golang.org/x/net v0.28.0
golang.org/x/oauth2 v0.21.0
golang.org/x/crypto v0.28.0
golang.org/x/net v0.30.0
golang.org/x/oauth2 v0.23.0
golang.org/x/sync v0.8.0
golang.org/x/sys v0.24.0
golang.org/x/time v0.6.0
golang.org/x/tools v0.24.0
golang.org/x/sys v0.26.0
golang.org/x/time v0.7.0
golang.org/x/tools v0.26.0
google.golang.org/api v0.154.0
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
gopkg.in/httprequest.v1 v1.2.1
Expand Down Expand Up @@ -291,9 +291,9 @@ require (
go.opentelemetry.io/otel/trace v1.21.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/term v0.23.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/term v0.25.0 // indirect
golang.org/x/text v0.19.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
Expand Down
36 changes: 18 additions & 18 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -968,8 +968,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -1009,8 +1009,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20150829230318-ea47fc708ee3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -1059,8 +1059,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand All @@ -1073,8 +1073,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -1154,8 +1154,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
Expand All @@ -1164,8 +1164,8 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -1180,13 +1180,13 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181008205924-a2b3f7f249e9/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand Down Expand Up @@ -1243,8 +1243,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
2 changes: 1 addition & 1 deletion state/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ func (st *State) isCAASController() (bool, error) {

func (st *State) apiHostPortsForCAAS(public bool) (addresses []network.SpaceHostPorts, err error) {
defer func() {
logger.Debugf("getting api hostports for CAAS: public %t, addresses %v", public, addresses)
logger.Tracef("getting api hostports for CAAS: public %t, addresses %v", public, addresses)
}()

if st.ModelUUID() != st.controllerModelTag.Id() {
Expand Down
2 changes: 2 additions & 0 deletions state/applicationoffers.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ func (op *RemoveOfferOperation) Build(attempt int) (ops []txn.Op, err error) {
}
ops = append(ops, proxyOps...)
}

sortRemovalOpsLast(ops)
return ops, nil
}

Expand Down
26 changes: 24 additions & 2 deletions state/applicationoffers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func (s *applicationOffersSuite) TestAddApplicationOfferBadEndpoints(c *gc.C) {
c.Assert(err, jc.ErrorIsNil)
}

func (s *applicationOffersSuite) TestFaillAddApplicationOfferNonGlobalEndpoint(c *gc.C) {
func (s *applicationOffersSuite) TestFailAddApplicationOfferNonGlobalEndpoint(c *gc.C) {
s.AddTestingApplication(c, "local-wordpress", s.AddTestingCharm(c, "wordpress"))
// logging-dir is a container scoped relation.
eps := map[string]string{"logging-dir": "logging-dir"}
Expand Down Expand Up @@ -877,15 +877,37 @@ func (s *applicationOffersSuite) TestRemoveOffersWithConnectionsForce(c *gc.C) {
})
c.Assert(err, jc.ErrorIsNil)

ao := state.NewApplicationOffers(s.State)
ao := state.NewApplicationOffersExposingOps(s.State)

// First check that if we generate the removal model operation,
// any removals are last in the ops list.
// RemoveOfferOperation is called internally by Remove further down.
modelOp, err := ao.RemoveOfferOperation("hosted-mysql", true)
c.Assert(err, jc.ErrorIsNil)

ops, err := modelOp.Build(0)
c.Assert(err, jc.ErrorIsNil)

var observedRemoval bool
for _, op := range ops {
if op.Remove {
observedRemoval = true
continue
}
c.Assert(observedRemoval, jc.IsFalse)
}

// Now run it all.
err = ao.Remove("hosted-mysql", true)
c.Assert(err, jc.ErrorIsNil)

_, err = ao.ApplicationOffer("hosted-mysql")
c.Assert(err, jc.Satisfies, errors.IsNotFound)

conn, err := s.State.OfferConnections(offer.OfferUUID)
c.Assert(err, jc.ErrorIsNil)
c.Assert(conn, gc.HasLen, 1)

offerRel, err := s.State.Relation(conn[0].RelationId())
c.Assert(err, jc.ErrorIsNil)
c.Assert(offerRel.Life(), gc.Equals, state.Dying)
Expand Down
20 changes: 19 additions & 1 deletion state/modeloperation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package state

import (
"sort"

"github.com/juju/errors"
"github.com/juju/mgo/v3/txn"
jujutxn "github.com/juju/txn/v3"
Expand Down Expand Up @@ -73,7 +75,7 @@ func ComposeModelOperations(modelOps ...ModelOperation) ModelOperation {
return ops, nil
},
doneFn: func(err error) error {
// Unfortunately, we cannot detect the extact
// Unfortunately, we cannot detect the exact
// ModelOperation that caused the error. For now, just
// pass the error to each done method and ignore the
// return value. Then, return the original error back
Expand Down Expand Up @@ -104,3 +106,19 @@ func (st *State) ApplyOperation(op ModelOperation) error {
err := st.db().Run(op.Build)
return op.Done(err)
}

// sortRemovalOpsLast re-orders a slice of transaction operations so that any
// document removals occur at the end of the slice.
// This is important for server-side transactions because of two execution
// characteristics:
// 1. All assertions are verified first.
// 2. We can read our own writes inside the transaction.
// This means it is possible for a removal and a subsequent update operation
// on the same document to pass the assertions, then fail with "not found" upon
// actual processing.
// Legacy client-side transactions do not exhibit this behaviour.
func sortRemovalOpsLast(ops []txn.Op) {
sort.Slice(ops, func(i, j int) bool {
return !ops[i].Remove && ops[j].Remove
})
}
Loading

0 comments on commit 6dd513e

Please sign in to comment.