Deprecate MPICFullResults feature flag (#8169)
Fixes https://github.com/letsencrypt/boulder/issues/8121
This commit is contained in:
		
							parent
							
								
									b6887a945e
								
							
						
					
					
						commit
						01a299cd0f
					
				|  | @ -23,6 +23,7 @@ type Config struct { | ||||||
| 	InsertAuthzsIndividually    bool | 	InsertAuthzsIndividually    bool | ||||||
| 	EnforceMultiCAA             bool | 	EnforceMultiCAA             bool | ||||||
| 	EnforceMPIC                 bool | 	EnforceMPIC                 bool | ||||||
|  | 	MPICFullResults             bool | ||||||
| 	UnsplitIssuance             bool | 	UnsplitIssuance             bool | ||||||
| 
 | 
 | ||||||
| 	// ServeRenewalInfo exposes the renewalInfo endpoint in the directory and for
 | 	// ServeRenewalInfo exposes the renewalInfo endpoint in the directory and for
 | ||||||
|  | @ -81,11 +82,6 @@ type Config struct { | ||||||
| 	// removing pending authz reuse.
 | 	// removing pending authz reuse.
 | ||||||
| 	NoPendingAuthzReuse bool | 	NoPendingAuthzReuse bool | ||||||
| 
 | 
 | ||||||
| 	// MPICFullResults causes the VA to wait for all remote (MPIC) results, rather
 |  | ||||||
| 	// than cancelling outstanding requests after enough successes or failures for
 |  | ||||||
| 	// the result to be determined.
 |  | ||||||
| 	MPICFullResults bool |  | ||||||
| 
 |  | ||||||
| 	// StoreARIReplacesInOrders causes the SA to store and retrieve the optional
 | 	// StoreARIReplacesInOrders causes the SA to store and retrieve the optional
 | ||||||
| 	// ARI replaces field in the orders table.
 | 	// ARI replaces field in the orders table.
 | ||||||
| 	StoreARIReplacesInOrders bool | 	StoreARIReplacesInOrders bool | ||||||
|  |  | ||||||
|  | @ -38,8 +38,7 @@ | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
| 		"features": { | 		"features": { | ||||||
| 			"DOH": true, | 			"DOH": true | ||||||
| 			"MPICFullResults": true |  | ||||||
| 		}, | 		}, | ||||||
| 		"remoteVAs": [ | 		"remoteVAs": [ | ||||||
| 			{ | 			{ | ||||||
|  |  | ||||||
|  | @ -32,49 +32,20 @@ func collectUserAgentsFromDNSRequests(requests []challtestsrvclient.DNSRequest) | ||||||
| func assertUserAgentsLength(t *testing.T, got []string, checkType string) { | func assertUserAgentsLength(t *testing.T, got []string, checkType string) { | ||||||
| 	t.Helper() | 	t.Helper() | ||||||
| 
 | 
 | ||||||
| 	if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" { |  | ||||||
| 		// We only need 3 checks if the MPICFullResults feature-flag is not
 |  | ||||||
| 		// enabled.
 |  | ||||||
| 		//
 |  | ||||||
| 		// TODO(#8121): Remove this once MPICFullResults has been defaulted to
 |  | ||||||
| 		// true.
 |  | ||||||
| 		if len(got) != 4 && len(got) != 3 { |  | ||||||
| 			t.Errorf("During %s, expected 3 or 4 User-Agents, got %d", checkType, len(got)) |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 	if len(got) != 4 { | 	if len(got) != 4 { | ||||||
| 		t.Errorf("During %s, expected 4 User-Agents, got %d", checkType, len(got)) | 		t.Errorf("During %s, expected 4 User-Agents, got %d", checkType, len(got)) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| func assertExpectedUserAgents(t *testing.T, got []string, checkType string) { | func assertExpectedUserAgents(t *testing.T, got []string, checkType string) { | ||||||
| 	t.Helper() | 	t.Helper() | ||||||
| 
 | 
 | ||||||
| 	if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" { |  | ||||||
| 		// One User-Agent may be missing if the MPICFullResults feature-flag is
 |  | ||||||
| 		// not enabled. This will need to be modified to 2 if we have not
 |  | ||||||
| 		// removed this feature-flag by the time we get to 6+ perspectives.
 |  | ||||||
| 		//
 |  | ||||||
| 		// TODO(#8121): Remove this once MPICFullResults has been defaulted to
 |  | ||||||
| 		// true.
 |  | ||||||
| 		var alreadySkippedOne bool |  | ||||||
| 		for _, ua := range expectedUserAgents { |  | ||||||
| 			if !slices.Contains(got, ua) { |  | ||||||
| 				if alreadySkippedOne { |  | ||||||
| 					t.Errorf("During %s, missing more than 1 User-Agent in %s (got %v)", checkType, expectedUserAgents, got) |  | ||||||
| 				} |  | ||||||
| 				alreadySkippedOne = true |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 	for _, ua := range expectedUserAgents { | 	for _, ua := range expectedUserAgents { | ||||||
| 		if !slices.Contains(got, ua) { | 		if !slices.Contains(got, ua) { | ||||||
| 			t.Errorf("During %s, expected User-Agent %q in %s (got %v)", checkType, ua, expectedUserAgents, got) | 			t.Errorf("During %s, expected User-Agent %q in %s (got %v)", checkType, ua, expectedUserAgents, got) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| func TestMPICTLSALPN01(t *testing.T) { | func TestMPICTLSALPN01(t *testing.T) { | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
|  | @ -129,15 +100,8 @@ func TestMPICTLSALPN01(t *testing.T) { | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 	if os.Getenv("BOULDER_CONFIG_DIR") == "test/config-next" && len(validationEvents) != 4 { | 	if len(validationEvents) != 4 { | ||||||
| 		t.Errorf("expected 4 validation events got %d", len(validationEvents)) | 		t.Errorf("expected 4 validation events got %d", len(validationEvents)) | ||||||
| 	} else if len(validationEvents) != 3 && len(validationEvents) != 4 { |  | ||||||
| 		// We only need 3 checks if the MPICFullResults feature-flag is not
 |  | ||||||
| 		// enabled.
 |  | ||||||
| 		//
 |  | ||||||
| 		// TODO(#8121): Remove this once MPICFullResults has been defaulted to
 |  | ||||||
| 		// true.
 |  | ||||||
| 		t.Errorf("expected 3 or 4 validation events got %d", len(validationEvents)) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	dnsEvents, err := testSrvClient.DNSRequestHistory(domain) | 	dnsEvents, err := testSrvClient.DNSRequestHistory(domain) | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								va/va.go
								
								
								
								
							
							
						
						
									
										13
									
								
								va/va.go
								
								
								
								
							|  | @ -25,7 +25,6 @@ import ( | ||||||
| 	"github.com/letsencrypt/boulder/core" | 	"github.com/letsencrypt/boulder/core" | ||||||
| 	corepb "github.com/letsencrypt/boulder/core/proto" | 	corepb "github.com/letsencrypt/boulder/core/proto" | ||||||
| 	berrors "github.com/letsencrypt/boulder/errors" | 	berrors "github.com/letsencrypt/boulder/errors" | ||||||
| 	"github.com/letsencrypt/boulder/features" |  | ||||||
| 	bgrpc "github.com/letsencrypt/boulder/grpc" | 	bgrpc "github.com/letsencrypt/boulder/grpc" | ||||||
| 	"github.com/letsencrypt/boulder/identifier" | 	"github.com/letsencrypt/boulder/identifier" | ||||||
| 	blog "github.com/letsencrypt/boulder/log" | 	blog "github.com/letsencrypt/boulder/log" | ||||||
|  | @ -610,18 +609,6 @@ func (va *ValidationAuthorityImpl) doRemoteOperation(ctx context.Context, op rem | ||||||
| 			firstProb = currProb | 			firstProb = currProb | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if !features.Get().MPICFullResults { |  | ||||||
| 			// To respond faster, if we get enough successes or too many failures, we cancel remaining RPCs.
 |  | ||||||
| 			// Finish the loop to collect remaining responses into `failed` so we can rely on having a response
 |  | ||||||
| 			// for every request we made.
 |  | ||||||
| 			if len(passed) >= required && len(passedRIRs) >= requiredRIRs { |  | ||||||
| 				cancel() |  | ||||||
| 			} |  | ||||||
| 			if len(failed) > va.maxRemoteFailures { |  | ||||||
| 				cancel() |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Once all the VAs have returned a result, break the loop.
 | 		// Once all the VAs have returned a result, break the loop.
 | ||||||
| 		if len(passed)+len(failed) >= remoteVACount { | 		if len(passed)+len(failed) >= remoteVACount { | ||||||
| 			break | 			break | ||||||
|  |  | ||||||
							
								
								
									
										148
									
								
								va/va_test.go
								
								
								
								
							
							
						
						
									
										148
									
								
								va/va_test.go
								
								
								
								
							|  | @ -12,7 +12,6 @@ import ( | ||||||
| 	"net/http/httptest" | 	"net/http/httptest" | ||||||
| 	"net/netip" | 	"net/netip" | ||||||
| 	"os" | 	"os" | ||||||
| 	"strconv" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"syscall" | 	"syscall" | ||||||
|  | @ -237,11 +236,6 @@ type multiSrv struct { | ||||||
| 	allowedUAs map[string]bool | 	allowedUAs map[string]bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const ( |  | ||||||
| 	slowUA                = "slow" |  | ||||||
| 	slowRemoteSleepMillis = 100 |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func httpMultiSrv(t *testing.T, token string, allowedUAs map[string]bool) *multiSrv { | func httpMultiSrv(t *testing.T, token string, allowedUAs map[string]bool) *multiSrv { | ||||||
| 	t.Helper() | 	t.Helper() | ||||||
| 	m := http.NewServeMux() | 	m := http.NewServeMux() | ||||||
|  | @ -250,9 +244,6 @@ func httpMultiSrv(t *testing.T, token string, allowedUAs map[string]bool) *multi | ||||||
| 	ms := &multiSrv{server, sync.Mutex{}, allowedUAs} | 	ms := &multiSrv{server, sync.Mutex{}, allowedUAs} | ||||||
| 
 | 
 | ||||||
| 	m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | 	m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||||||
| 		if r.UserAgent() == slowUA { |  | ||||||
| 			time.Sleep(slowRemoteSleepMillis * time.Millisecond) |  | ||||||
| 		} |  | ||||||
| 		ms.mu.Lock() | 		ms.mu.Lock() | ||||||
| 		defer ms.mu.Unlock() | 		defer ms.mu.Unlock() | ||||||
| 		if ms.allowedUAs[r.UserAgent()] { | 		if ms.allowedUAs[r.UserAgent()] { | ||||||
|  | @ -645,7 +636,7 @@ func TestMultiVA(t *testing.T) { | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			// With the local and remote VAs seeing diff problems, we expect a problem.
 | 			// With the local and remote VAs seeing diff problems, we expect a problem.
 | ||||||
| 			Name: "Local and remote VA differential, full results, enforce multi VA", | 			Name: "Local and remote VA differential", | ||||||
| 			Remotes: []remoteConf{ | 			Remotes: []remoteConf{ | ||||||
| 				{ua: fail, rir: arin}, | 				{ua: fail, rir: arin}, | ||||||
| 				{ua: fail, rir: ripe}, | 				{ua: fail, rir: ripe}, | ||||||
|  | @ -689,143 +680,6 @@ func TestMultiVA(t *testing.T) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestMultiVAEarlyReturn(t *testing.T) { |  | ||||||
| 	// TODO(#7809): Make this test parallel when it no longer manipulates the
 |  | ||||||
| 	// MPICFullResults feature flag.
 |  | ||||||
| 
 |  | ||||||
| 	testCases := []struct { |  | ||||||
| 		name              string |  | ||||||
| 		remoteConfs       []remoteConf |  | ||||||
| 		wantCorroboration bool |  | ||||||
| 		wantEarlyReturn   bool |  | ||||||
| 	}{ |  | ||||||
| 		{ |  | ||||||
| 			name: "Early return when 2/3 pass", |  | ||||||
| 			remoteConfs: []remoteConf{ |  | ||||||
| 				{ua: pass, rir: arin}, |  | ||||||
| 				{ua: pass, rir: ripe}, |  | ||||||
| 				{ua: slowUA, rir: apnic}, |  | ||||||
| 			}, |  | ||||||
| 			wantCorroboration: true, |  | ||||||
| 			wantEarlyReturn:   true, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "Early return when 2/3 fail", |  | ||||||
| 			remoteConfs: []remoteConf{ |  | ||||||
| 				{ua: fail, rir: arin}, |  | ||||||
| 				{ua: fail, rir: ripe}, |  | ||||||
| 				{ua: slowUA, rir: apnic}, |  | ||||||
| 			}, |  | ||||||
| 			wantCorroboration: false, |  | ||||||
| 			wantEarlyReturn:   true, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "Slow return when first 2/3 are inconclusive", |  | ||||||
| 			remoteConfs: []remoteConf{ |  | ||||||
| 				{ua: pass, rir: arin}, |  | ||||||
| 				{ua: fail, rir: ripe}, |  | ||||||
| 				{ua: slowUA, rir: apnic}, |  | ||||||
| 			}, |  | ||||||
| 			wantCorroboration: false, |  | ||||||
| 			wantEarlyReturn:   false, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "Early return when 4/6 pass", |  | ||||||
| 			remoteConfs: []remoteConf{ |  | ||||||
| 				{ua: pass, rir: arin}, |  | ||||||
| 				{ua: pass, rir: ripe}, |  | ||||||
| 				{ua: pass, rir: apnic}, |  | ||||||
| 				{ua: pass, rir: arin}, |  | ||||||
| 				{ua: fail, rir: ripe}, |  | ||||||
| 				{ua: slowUA, rir: apnic}, |  | ||||||
| 			}, |  | ||||||
| 			wantCorroboration: true, |  | ||||||
| 			wantEarlyReturn:   true, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "Early return when 4/6 fail", |  | ||||||
| 			remoteConfs: []remoteConf{ |  | ||||||
| 				{ua: pass, rir: arin}, |  | ||||||
| 				{ua: fail, rir: ripe}, |  | ||||||
| 				{ua: fail, rir: apnic}, |  | ||||||
| 				{ua: fail, rir: arin}, |  | ||||||
| 				{ua: fail, rir: ripe}, |  | ||||||
| 				{ua: slowUA, rir: apnic}, |  | ||||||
| 			}, |  | ||||||
| 			wantCorroboration: false, |  | ||||||
| 			wantEarlyReturn:   true, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "Slow return when first 5/6 are inconclusive", |  | ||||||
| 			remoteConfs: []remoteConf{ |  | ||||||
| 				{ua: pass, rir: arin}, |  | ||||||
| 				{ua: pass, rir: ripe}, |  | ||||||
| 				{ua: pass, rir: apnic}, |  | ||||||
| 				{ua: fail, rir: arin}, |  | ||||||
| 				{ua: fail, rir: ripe}, |  | ||||||
| 				{ua: slowUA, rir: apnic}, |  | ||||||
| 			}, |  | ||||||
| 			wantCorroboration: false, |  | ||||||
| 			wantEarlyReturn:   false, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for _, mpicFullResults := range []bool{false, true} { |  | ||||||
| 		for _, tc := range testCases { |  | ||||||
| 			t.Run(fmt.Sprintf("%s_(MPICFullResults: %s)", tc.name, strconv.FormatBool(mpicFullResults)), func(t *testing.T) { |  | ||||||
| 				// TODO(#7809): Make this test parallel when it no longer manipulates the
 |  | ||||||
| 				// MPICFullResults feature flag.
 |  | ||||||
| 
 |  | ||||||
| 				// Configure one test server per test case so that all tests can run in parallel.
 |  | ||||||
| 				ms := httpMultiSrv(t, expectedToken, map[string]bool{pass: true, fail: false}) |  | ||||||
| 				defer ms.Close() |  | ||||||
| 
 |  | ||||||
| 				localVA, _ := setupWithRemotes(ms.Server, pass, tc.remoteConfs, nil) |  | ||||||
| 
 |  | ||||||
| 				features.Set(features.Config{MPICFullResults: mpicFullResults}) |  | ||||||
| 				defer features.Reset() |  | ||||||
| 
 |  | ||||||
| 				// Perform all validations
 |  | ||||||
| 				start := time.Now() |  | ||||||
| 				req := createValidationRequest(identifier.NewDNS("localhost"), core.ChallengeTypeHTTP01) |  | ||||||
| 				res, _ := localVA.DoDCV(ctx, req) |  | ||||||
| 
 |  | ||||||
| 				if tc.wantCorroboration { |  | ||||||
| 					if res.Problem != nil { |  | ||||||
| 						t.Errorf("expected corroboration, but got prob %s", res.Problem) |  | ||||||
| 					} |  | ||||||
| 				} else { |  | ||||||
| 					if res.Problem == nil { |  | ||||||
| 						t.Error("expected prob from PerformValidation, got nil") |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				elapsed := time.Since(start).Round(time.Millisecond).Milliseconds() |  | ||||||
| 
 |  | ||||||
| 				if tc.wantEarlyReturn && !mpicFullResults { |  | ||||||
| 					// The slow UA should sleep for `slowRemoteSleepMillis`. But the first remote
 |  | ||||||
| 					// VA should fail quickly and the early-return code should cause the overall
 |  | ||||||
| 					// overall validation to return a prob quickly (i.e. in less than half of
 |  | ||||||
| 					// `slowRemoteSleepMillis`).
 |  | ||||||
| 					if elapsed > slowRemoteSleepMillis/2 { |  | ||||||
| 						t.Errorf( |  | ||||||
| 							"Expected an early return from PerformValidation in < %d ms, took %d ms", |  | ||||||
| 							slowRemoteSleepMillis/2, elapsed) |  | ||||||
| 					} |  | ||||||
| 				} else { |  | ||||||
| 					// The VA will have to wait for all of the results, because the fast
 |  | ||||||
| 					// results aren't sufficient to determine (non)corroboration.
 |  | ||||||
| 					if elapsed < slowRemoteSleepMillis { |  | ||||||
| 						t.Errorf( |  | ||||||
| 							"Expected a slow return from PerformValidation in >= %d ms, took %d ms", |  | ||||||
| 							slowRemoteSleepMillis, elapsed) |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			}) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestMultiVAPolicy(t *testing.T) { | func TestMultiVAPolicy(t *testing.T) { | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue