diff --git a/Makefile b/Makefile index 65e2deb4a6..72d494b53c 100644 --- a/Makefile +++ b/Makefile @@ -191,7 +191,7 @@ lint: golangci-lint run --fix .PHONY: test -test: test-kustomize +test: go test -covermode=count -coverprofile=coverage.out ${TEST_TARGET} .PHONY: test-kustomize @@ -204,7 +204,7 @@ start-e2e: .PHONY: test-e2e test-e2e: - go test -timeout 15m -v -count 1 --tags e2e -p ${E2E_PARALLEL} --short ./test/e2e ${E2E_TEST_OPTIONS} + go test -timeout 20m -v -count 1 --tags e2e -p ${E2E_PARALLEL} --short ./test/e2e ${E2E_TEST_OPTIONS} .PHONY: coverage coverage: test diff --git a/go.mod b/go.mod index ce5d5b61e0..fe52912cdc 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a google.golang.org/grpc v1.33.1 google.golang.org/grpc/examples v0.0.0-20210331235824-f6bb3972ed15 // indirect + google.golang.org/protobuf v1.25.0 gopkg.in/yaml.v2 v2.3.0 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect k8s.io/api v0.20.4 diff --git a/manifests/crds/rollout-crd.yaml b/manifests/crds/rollout-crd.yaml index 763411fad6..0579631ad1 100644 --- a/manifests/crds/rollout-crd.yaml +++ b/manifests/crds/rollout-crd.yaml @@ -296,6 +296,12 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + scaleDownDelayRevisionLimit: + format: int32 + type: integer + scaleDownDelaySeconds: + format: int32 + type: integer stableMetadata: properties: annotations: diff --git a/manifests/install.yaml b/manifests/install.yaml index 519bd631b8..bb58e36c2c 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -9987,6 +9987,12 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + scaleDownDelayRevisionLimit: + format: int32 + type: integer + scaleDownDelaySeconds: + format: int32 + type: integer stableMetadata: properties: annotations: diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index a0d6d009e7..b532ed44ea 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -9987,6 +9987,12 @@ spec: - type: integer - type: string x-kubernetes-int-or-string: true + scaleDownDelayRevisionLimit: + format: int32 + type: integer + scaleDownDelaySeconds: + format: int32 + type: integer stableMetadata: properties: annotations: diff --git a/pkg/apiclient/rollout/rollout.pb.go b/pkg/apiclient/rollout/rollout.pb.go index d505843bf9..f9391eb133 100644 --- a/pkg/apiclient/rollout/rollout.pb.go +++ b/pkg/apiclient/rollout/rollout.pb.go @@ -8,11 +8,11 @@ import ( fmt "fmt" v1alpha1 "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" proto "github.com/gogo/protobuf/proto" - empty "github.com/golang/protobuf/ptypes/empty" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" io "io" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" math "math" @@ -1586,14 +1586,14 @@ type RolloutServiceClient interface { WatchRolloutInfo(ctx context.Context, in *RolloutInfoQuery, opts ...grpc.CallOption) (RolloutService_WatchRolloutInfoClient, error) ListRolloutInfos(ctx context.Context, in *RolloutInfoListQuery, opts ...grpc.CallOption) (*RolloutInfoList, error) WatchRolloutInfos(ctx context.Context, in *RolloutInfoListQuery, opts ...grpc.CallOption) (RolloutService_WatchRolloutInfosClient, error) - GetNamespace(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*NamespaceInfo, error) + GetNamespace(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*NamespaceInfo, error) RestartRollout(ctx context.Context, in *RestartRolloutRequest, opts ...grpc.CallOption) (*v1alpha1.Rollout, error) PromoteRollout(ctx context.Context, in *PromoteRolloutRequest, opts ...grpc.CallOption) (*v1alpha1.Rollout, error) AbortRollout(ctx context.Context, in *AbortRolloutRequest, opts ...grpc.CallOption) (*v1alpha1.Rollout, error) SetRolloutImage(ctx context.Context, in *SetImageRequest, opts ...grpc.CallOption) (*v1alpha1.Rollout, error) UndoRollout(ctx context.Context, in *UndoRolloutRequest, opts ...grpc.CallOption) (*v1alpha1.Rollout, error) RetryRollout(ctx context.Context, in *RetryRolloutRequest, opts ...grpc.CallOption) (*v1alpha1.Rollout, error) - Version(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*VersionInfo, error) + Version(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*VersionInfo, error) } type rolloutServiceClient struct { @@ -1686,7 +1686,7 @@ func (x *rolloutServiceWatchRolloutInfosClient) Recv() (*RolloutWatchEvent, erro return m, nil } -func (c *rolloutServiceClient) GetNamespace(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*NamespaceInfo, error) { +func (c *rolloutServiceClient) GetNamespace(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*NamespaceInfo, error) { out := new(NamespaceInfo) err := c.cc.Invoke(ctx, "/rollout.RolloutService/GetNamespace", in, out, opts...) if err != nil { @@ -1749,7 +1749,7 @@ func (c *rolloutServiceClient) RetryRollout(ctx context.Context, in *RetryRollou return out, nil } -func (c *rolloutServiceClient) Version(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*VersionInfo, error) { +func (c *rolloutServiceClient) Version(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*VersionInfo, error) { out := new(VersionInfo) err := c.cc.Invoke(ctx, "/rollout.RolloutService/Version", in, out, opts...) if err != nil { @@ -1764,14 +1764,14 @@ type RolloutServiceServer interface { WatchRolloutInfo(*RolloutInfoQuery, RolloutService_WatchRolloutInfoServer) error ListRolloutInfos(context.Context, *RolloutInfoListQuery) (*RolloutInfoList, error) WatchRolloutInfos(*RolloutInfoListQuery, RolloutService_WatchRolloutInfosServer) error - GetNamespace(context.Context, *empty.Empty) (*NamespaceInfo, error) + GetNamespace(context.Context, *emptypb.Empty) (*NamespaceInfo, error) RestartRollout(context.Context, *RestartRolloutRequest) (*v1alpha1.Rollout, error) PromoteRollout(context.Context, *PromoteRolloutRequest) (*v1alpha1.Rollout, error) AbortRollout(context.Context, *AbortRolloutRequest) (*v1alpha1.Rollout, error) SetRolloutImage(context.Context, *SetImageRequest) (*v1alpha1.Rollout, error) UndoRollout(context.Context, *UndoRolloutRequest) (*v1alpha1.Rollout, error) RetryRollout(context.Context, *RetryRolloutRequest) (*v1alpha1.Rollout, error) - Version(context.Context, *empty.Empty) (*VersionInfo, error) + Version(context.Context, *emptypb.Empty) (*VersionInfo, error) } // UnimplementedRolloutServiceServer can be embedded to have forward compatible implementations. @@ -1790,7 +1790,7 @@ func (*UnimplementedRolloutServiceServer) ListRolloutInfos(ctx context.Context, func (*UnimplementedRolloutServiceServer) WatchRolloutInfos(req *RolloutInfoListQuery, srv RolloutService_WatchRolloutInfosServer) error { return status.Errorf(codes.Unimplemented, "method WatchRolloutInfos not implemented") } -func (*UnimplementedRolloutServiceServer) GetNamespace(ctx context.Context, req *empty.Empty) (*NamespaceInfo, error) { +func (*UnimplementedRolloutServiceServer) GetNamespace(ctx context.Context, req *emptypb.Empty) (*NamespaceInfo, error) { return nil, status.Errorf(codes.Unimplemented, "method GetNamespace not implemented") } func (*UnimplementedRolloutServiceServer) RestartRollout(ctx context.Context, req *RestartRolloutRequest) (*v1alpha1.Rollout, error) { @@ -1811,7 +1811,7 @@ func (*UnimplementedRolloutServiceServer) UndoRollout(ctx context.Context, req * func (*UnimplementedRolloutServiceServer) RetryRollout(ctx context.Context, req *RetryRolloutRequest) (*v1alpha1.Rollout, error) { return nil, status.Errorf(codes.Unimplemented, "method RetryRollout not implemented") } -func (*UnimplementedRolloutServiceServer) Version(ctx context.Context, req *empty.Empty) (*VersionInfo, error) { +func (*UnimplementedRolloutServiceServer) Version(ctx context.Context, req *emptypb.Empty) (*VersionInfo, error) { return nil, status.Errorf(codes.Unimplemented, "method Version not implemented") } @@ -1898,7 +1898,7 @@ func (x *rolloutServiceWatchRolloutInfosServer) Send(m *RolloutWatchEvent) error } func _RolloutService_GetNamespace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(empty.Empty) + in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } @@ -1910,7 +1910,7 @@ func _RolloutService_GetNamespace_Handler(srv interface{}, ctx context.Context, FullMethod: "/rollout.RolloutService/GetNamespace", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RolloutServiceServer).GetNamespace(ctx, req.(*empty.Empty)) + return srv.(RolloutServiceServer).GetNamespace(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } @@ -2024,7 +2024,7 @@ func _RolloutService_RetryRollout_Handler(srv interface{}, ctx context.Context, } func _RolloutService_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(empty.Empty) + in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } @@ -2036,7 +2036,7 @@ func _RolloutService_Version_Handler(srv interface{}, ctx context.Context, dec f FullMethod: "/rollout.RolloutService/Version", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RolloutServiceServer).Version(ctx, req.(*empty.Empty)) + return srv.(RolloutServiceServer).Version(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } diff --git a/pkg/apiclient/rollout/rollout.pb.gw.go b/pkg/apiclient/rollout/rollout.pb.gw.go index 2ab97fb555..da77cc4e6a 100644 --- a/pkg/apiclient/rollout/rollout.pb.gw.go +++ b/pkg/apiclient/rollout/rollout.pb.gw.go @@ -15,13 +15,14 @@ import ( "github.com/golang/protobuf/descriptor" "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/ptypes/empty" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/grpc-ecosystem/grpc-gateway/utilities" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" ) // Suppress "imported and not used" errors @@ -31,6 +32,7 @@ var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage +var _ = metadata.Join func request_RolloutService_GetRolloutInfo_0(ctx context.Context, marshaler runtime.Marshaler, client RolloutServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq RolloutInfoQuery @@ -244,7 +246,7 @@ func request_RolloutService_WatchRolloutInfos_0(ctx context.Context, marshaler r } func request_RolloutService_GetNamespace_0(ctx context.Context, marshaler runtime.Marshaler, client RolloutServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq empty.Empty + var protoReq emptypb.Empty var metadata runtime.ServerMetadata msg, err := client.GetNamespace(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -253,7 +255,7 @@ func request_RolloutService_GetNamespace_0(ctx context.Context, marshaler runtim } func local_request_RolloutService_GetNamespace_0(ctx context.Context, marshaler runtime.Marshaler, server RolloutServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq empty.Empty + var protoReq emptypb.Empty var metadata runtime.ServerMetadata msg, err := server.GetNamespace(ctx, &protoReq) @@ -902,7 +904,7 @@ func local_request_RolloutService_RetryRollout_0(ctx context.Context, marshaler } func request_RolloutService_Version_0(ctx context.Context, marshaler runtime.Marshaler, client RolloutServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq empty.Empty + var protoReq emptypb.Empty var metadata runtime.ServerMetadata msg, err := client.Version(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -911,7 +913,7 @@ func request_RolloutService_Version_0(ctx context.Context, marshaler runtime.Mar } func local_request_RolloutService_Version_0(ctx context.Context, marshaler runtime.Marshaler, server RolloutServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq empty.Empty + var protoReq emptypb.Empty var metadata runtime.ServerMetadata msg, err := server.Version(ctx, &protoReq) @@ -922,11 +924,14 @@ func local_request_RolloutService_Version_0(ctx context.Context, marshaler runti // RegisterRolloutServiceHandlerServer registers the http handlers for service RolloutService to "mux". // UnaryRPC :call RolloutServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterRolloutServiceHandlerFromEndpoint instead. func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server RolloutServiceServer) error { mux.Handle("GET", pattern_RolloutService_GetRolloutInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -934,6 +939,7 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve return } resp, md, err := local_request_RolloutService_GetRolloutInfo_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -954,6 +960,8 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve mux.Handle("GET", pattern_RolloutService_ListRolloutInfos_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -961,6 +969,7 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve return } resp, md, err := local_request_RolloutService_ListRolloutInfos_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -981,6 +990,8 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve mux.Handle("GET", pattern_RolloutService_GetNamespace_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -988,6 +999,7 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve return } resp, md, err := local_request_RolloutService_GetNamespace_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1001,6 +1013,8 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve mux.Handle("PUT", pattern_RolloutService_RestartRollout_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1008,6 +1022,7 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve return } resp, md, err := local_request_RolloutService_RestartRollout_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1021,6 +1036,8 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve mux.Handle("PUT", pattern_RolloutService_PromoteRollout_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1028,6 +1045,7 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve return } resp, md, err := local_request_RolloutService_PromoteRollout_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1041,6 +1059,8 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve mux.Handle("PUT", pattern_RolloutService_AbortRollout_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1048,6 +1068,7 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve return } resp, md, err := local_request_RolloutService_AbortRollout_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1061,6 +1082,8 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve mux.Handle("PUT", pattern_RolloutService_SetRolloutImage_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1068,6 +1091,7 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve return } resp, md, err := local_request_RolloutService_SetRolloutImage_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1081,6 +1105,8 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve mux.Handle("PUT", pattern_RolloutService_UndoRollout_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1088,6 +1114,7 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve return } resp, md, err := local_request_RolloutService_UndoRollout_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1101,6 +1128,8 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve mux.Handle("PUT", pattern_RolloutService_RetryRollout_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1108,6 +1137,7 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve return } resp, md, err := local_request_RolloutService_RetryRollout_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1121,6 +1151,8 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve mux.Handle("GET", pattern_RolloutService_Version_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) if err != nil { @@ -1128,6 +1160,7 @@ func RegisterRolloutServiceHandlerServer(ctx context.Context, mux *runtime.Serve return } resp, md, err := local_request_RolloutService_Version_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) diff --git a/pkg/apiclient/rollout/rollout.swagger.json b/pkg/apiclient/rollout/rollout.swagger.json index 7a52d30224..95746a92d6 100644 --- a/pkg/apiclient/rollout/rollout.swagger.json +++ b/pkg/apiclient/rollout/rollout.swagger.json @@ -743,6 +743,16 @@ "stableMetadata": { "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.PodTemplateMetadata", "title": "StableMetadata specify labels and annotations which will be attached to the stable pods for\nthe duration which they act as a canary, and will be removed after" + }, + "scaleDownDelaySeconds": { + "type": "integer", + "format": "int32", + "title": "ScaleDownDelaySeconds adds a delay before scaling down the previous ReplicaSet when the\ncanary strategy is used with traffic routing (default 30 seconds). A delay in scaling down\nthe previous ReplicaSet is needed after switching the stable service selector to point to\nthe new ReplicaSet, in order to give time for traffic providers to re-target the new pods.\nThis value is ignored with basic, replica-weighted canary without traffic routing.\n+optional" + }, + "scaleDownDelayRevisionLimit": { + "type": "integer", + "format": "int32", + "title": "ScaleDownDelayRevisionLimit limits the number of old RS that can run at one time before getting scaled down\n+optional" } }, "title": "CanaryStrategy defines parameters for a Replica Based Canary" diff --git a/pkg/apis/rollouts/v1alpha1/generated.pb.go b/pkg/apis/rollouts/v1alpha1/generated.pb.go index dbffc5c501..54319eab06 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.pb.go +++ b/pkg/apis/rollouts/v1alpha1/generated.pb.go @@ -2125,349 +2125,349 @@ func init() { } var fileDescriptor_e0e705f843545fab = []byte{ - // 5462 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x8c, 0x24, 0xd7, - 0x55, 0xb0, 0xab, 0x7b, 0x7a, 0xa6, 0xe7, 0xf4, 0xfc, 0xde, 0x9d, 0xcd, 0x76, 0xd6, 0xbb, 0xd3, - 0x9b, 0x72, 0xe4, 0xcf, 0xf9, 0x48, 0x7a, 0xe2, 0xb5, 0x0d, 0x26, 0x8e, 0x2c, 0xba, 0x67, 0x77, - 0xbd, 0x33, 0x9e, 0xd9, 0x9d, 0xbd, 0x3d, 0xeb, 0x55, 0xfc, 0x03, 0xa9, 0xe9, 0xbe, 0xd3, 0x53, - 0xbb, 0xd5, 0x55, 0x9d, 0xaa, 0xea, 0xd9, 0x1d, 0x27, 0x4a, 0xec, 0x44, 0x26, 0x06, 0x25, 0xb2, - 0xf9, 0x79, 0x41, 0x08, 0x84, 0x10, 0x0f, 0x08, 0x5e, 0x78, 0xc8, 0x0b, 0x12, 0x11, 0x51, 0x00, - 0xc9, 0x3c, 0x40, 0xc2, 0x0b, 0x0e, 0x48, 0x69, 0x70, 0x07, 0x09, 0xc1, 0x0b, 0x0a, 0x8a, 0x84, - 0xb2, 0xe2, 0x01, 0xdd, 0x9f, 0xba, 0x55, 0xb7, 0xaa, 0x66, 0xa6, 0x7b, 0xbb, 0x66, 0x89, 0x80, - 0xb7, 0xe9, 0x73, 0xce, 0x3d, 0xe7, 0xfe, 0x9c, 0x7b, 0xce, 0xb9, 0xf7, 0x9c, 0x5b, 0x03, 0x1b, - 0x6d, 0xd3, 0xdf, 0xeb, 0xed, 0x54, 0x9b, 0x4e, 0x67, 0xc5, 0x70, 0xdb, 0x4e, 0xd7, 0x75, 0x6e, - 0xb3, 0x3f, 0x3e, 0xe1, 0x3a, 0x96, 0xe5, 0xf4, 0x7c, 0x6f, 0xa5, 0x7b, 0xa7, 0xbd, 0x62, 0x74, - 0x4d, 0x6f, 0x45, 0x42, 0xf6, 0x9f, 0x34, 0xac, 0xee, 0x9e, 0xf1, 0xe4, 0x4a, 0x9b, 0xd8, 0xc4, - 0x35, 0x7c, 0xd2, 0xaa, 0x76, 0x5d, 0xc7, 0x77, 0xd0, 0xa7, 0x43, 0x6e, 0xd5, 0x80, 0x1b, 0xfb, - 0xe3, 0x17, 0x82, 0xb6, 0xd5, 0xee, 0x9d, 0x76, 0x95, 0x72, 0xab, 0x4a, 0x48, 0xc0, 0xed, 0xec, - 0x27, 0x22, 0x7d, 0x69, 0x3b, 0x6d, 0x67, 0x85, 0x31, 0xdd, 0xe9, 0xed, 0xb2, 0x5f, 0xec, 0x07, - 0xfb, 0x8b, 0x0b, 0x3b, 0xfb, 0xd8, 0x9d, 0x67, 0xbd, 0xaa, 0xe9, 0xd0, 0xbe, 0xad, 0xec, 0x18, - 0x7e, 0x73, 0x6f, 0x65, 0x3f, 0xd1, 0xa3, 0xb3, 0x7a, 0x84, 0xa8, 0xe9, 0xb8, 0x24, 0x8d, 0xe6, - 0xe9, 0x90, 0xa6, 0x63, 0x34, 0xf7, 0x4c, 0x9b, 0xb8, 0x07, 0xe1, 0xa8, 0x3b, 0xc4, 0x37, 0xd2, - 0x5a, 0xad, 0x1c, 0xd6, 0xca, 0xed, 0xd9, 0xbe, 0xd9, 0x21, 0x89, 0x06, 0x3f, 0x7d, 0x5c, 0x03, - 0xaf, 0xb9, 0x47, 0x3a, 0x46, 0xa2, 0xdd, 0x53, 0x87, 0xb5, 0xeb, 0xf9, 0xa6, 0xb5, 0x62, 0xda, - 0xbe, 0xe7, 0xbb, 0xf1, 0x46, 0xfa, 0xbf, 0x6b, 0xb0, 0x58, 0xdb, 0xa8, 0x6f, 0xbb, 0xc6, 0xee, - 0xae, 0xd9, 0xc4, 0x4e, 0xcf, 0x37, 0xed, 0x36, 0xfa, 0x18, 0x4c, 0x99, 0x76, 0xdb, 0x25, 0x9e, - 0x57, 0xd6, 0x2e, 0x68, 0x4f, 0x4c, 0xd7, 0xe7, 0xdf, 0xeb, 0x57, 0x1e, 0x19, 0xf4, 0x2b, 0x53, - 0x6b, 0x1c, 0x8c, 0x03, 0x3c, 0x7a, 0x06, 0x4a, 0x1e, 0x71, 0xf7, 0xcd, 0x26, 0xd9, 0x72, 0x5c, - 0xbf, 0x9c, 0xbb, 0xa0, 0x3d, 0x51, 0xa8, 0x9f, 0x12, 0xe4, 0xa5, 0x46, 0x88, 0xc2, 0x51, 0x3a, - 0xda, 0xcc, 0x75, 0x1c, 0x5f, 0xe0, 0xcb, 0x79, 0x26, 0x45, 0x36, 0xc3, 0x21, 0x0a, 0x47, 0xe9, - 0xd0, 0x25, 0x58, 0x30, 0x6c, 0xdb, 0xf1, 0x0d, 0xdf, 0x74, 0xec, 0x2d, 0x97, 0xec, 0x9a, 0xf7, - 0xca, 0x13, 0xac, 0x6d, 0x59, 0xb4, 0x5d, 0xa8, 0xc5, 0xf0, 0x38, 0xd1, 0x42, 0xff, 0xbb, 0x1c, - 0x94, 0x6a, 0xb6, 0x61, 0x1d, 0x78, 0xa6, 0x87, 0x7b, 0x36, 0xfa, 0x2c, 0x14, 0xe9, 0xea, 0xb5, - 0x0c, 0xdf, 0x60, 0xe3, 0x2d, 0x5d, 0xfc, 0x64, 0x95, 0x4f, 0x66, 0x35, 0x3a, 0x99, 0xa1, 0x4e, - 0x52, 0xea, 0xea, 0xfe, 0x93, 0xd5, 0xeb, 0x3b, 0xb7, 0x49, 0xd3, 0xdf, 0x24, 0xbe, 0x51, 0x47, - 0x42, 0x3e, 0x84, 0x30, 0x2c, 0xb9, 0x22, 0x07, 0x26, 0xbc, 0x2e, 0x69, 0xb2, 0xe9, 0x29, 0x5d, - 0xdc, 0xac, 0x8e, 0xa3, 0xff, 0xd5, 0x48, 0xd7, 0x1b, 0x5d, 0xd2, 0xac, 0xcf, 0x08, 0xd1, 0x13, - 0xf4, 0x17, 0x66, 0x82, 0xd0, 0x5d, 0x98, 0xf4, 0x7c, 0xc3, 0xef, 0x79, 0x6c, 0x6a, 0x4b, 0x17, - 0xaf, 0x67, 0x27, 0x92, 0xb1, 0xad, 0xcf, 0x09, 0xa1, 0x93, 0xfc, 0x37, 0x16, 0xe2, 0xf4, 0xbf, - 0xd7, 0xe0, 0x54, 0x84, 0xba, 0xe6, 0xb6, 0x7b, 0x1d, 0x62, 0xfb, 0xe8, 0x02, 0x4c, 0xd8, 0x46, - 0x87, 0x08, 0x7d, 0x92, 0x5d, 0xbe, 0x66, 0x74, 0x08, 0x66, 0x18, 0xf4, 0x18, 0x14, 0xf6, 0x0d, - 0xab, 0x47, 0xd8, 0x24, 0x4d, 0xd7, 0x67, 0x05, 0x49, 0xe1, 0x25, 0x0a, 0xc4, 0x1c, 0x87, 0xbe, - 0x00, 0xd3, 0xec, 0x8f, 0x2b, 0xae, 0xd3, 0xc9, 0x68, 0x68, 0xa2, 0x87, 0x2f, 0x05, 0x6c, 0xeb, - 0xb3, 0x83, 0x7e, 0x65, 0x5a, 0xfe, 0xc4, 0xa1, 0x40, 0xfd, 0x1f, 0x34, 0x98, 0x8f, 0x0c, 0x6e, - 0xc3, 0xf4, 0x7c, 0xf4, 0x6a, 0x42, 0x79, 0xaa, 0xc3, 0x29, 0x0f, 0x6d, 0xcd, 0x54, 0x67, 0x41, - 0x8c, 0xb4, 0x18, 0x40, 0x22, 0x8a, 0x63, 0x43, 0xc1, 0xf4, 0x49, 0xc7, 0x2b, 0xe7, 0x2e, 0xe4, - 0x9f, 0x28, 0x5d, 0x5c, 0xcb, 0x6c, 0x19, 0xc3, 0xf9, 0x5d, 0xa3, 0xfc, 0x31, 0x17, 0xa3, 0xff, - 0x56, 0x4e, 0x19, 0x21, 0xd5, 0x28, 0xe4, 0xc0, 0x54, 0x87, 0xf8, 0xae, 0xd9, 0xa4, 0xd6, 0x80, - 0xf6, 0xe2, 0xd2, 0x78, 0xbd, 0xd8, 0x64, 0xcc, 0x42, 0x9b, 0xc2, 0x7f, 0x7b, 0x38, 0x90, 0x82, - 0xf6, 0x60, 0xc2, 0x70, 0xdb, 0xc1, 0x98, 0xaf, 0x64, 0xb3, 0xbe, 0xa1, 0xce, 0xd5, 0xdc, 0xb6, - 0x87, 0x99, 0x04, 0xb4, 0x02, 0xd3, 0x3e, 0x71, 0x3b, 0xa6, 0x6d, 0xf8, 0xdc, 0x08, 0x15, 0xeb, - 0x8b, 0x82, 0x6c, 0x7a, 0x3b, 0x40, 0xe0, 0x90, 0x46, 0x7f, 0x3f, 0x07, 0x8b, 0x89, 0xcd, 0x80, - 0x9e, 0x86, 0x42, 0x77, 0xcf, 0xf0, 0x02, 0xed, 0x5e, 0x0e, 0xa6, 0x76, 0x8b, 0x02, 0xef, 0xf7, - 0x2b, 0xb3, 0x41, 0x13, 0x06, 0xc0, 0x9c, 0x98, 0x5a, 0xd9, 0x0e, 0xf1, 0x3c, 0xa3, 0x1d, 0xa8, - 0x7c, 0x64, 0x46, 0x18, 0x18, 0x07, 0x78, 0xf4, 0x55, 0x0d, 0x66, 0xf9, 0xec, 0x60, 0xe2, 0xf5, - 0x2c, 0x9f, 0x6e, 0x6b, 0x3a, 0x37, 0xeb, 0x59, 0xac, 0x04, 0x67, 0x59, 0x3f, 0x2d, 0xa4, 0xcf, - 0x46, 0xa1, 0x1e, 0x56, 0xe5, 0xa2, 0x5b, 0x30, 0xed, 0xf9, 0x86, 0xeb, 0x93, 0x56, 0xcd, 0x67, - 0xa6, 0xb7, 0x74, 0xf1, 0xff, 0x0f, 0xa7, 0xef, 0xdb, 0x66, 0x87, 0xf0, 0xbd, 0xd5, 0x08, 0x18, - 0xe0, 0x90, 0x97, 0xfe, 0xaf, 0x1a, 0x2c, 0x04, 0xd3, 0xb4, 0x4d, 0x3a, 0x5d, 0xcb, 0xf0, 0xc9, - 0x43, 0xb0, 0xcc, 0xbe, 0x62, 0x99, 0x71, 0x36, 0xfb, 0x2b, 0xe8, 0xff, 0x61, 0xe6, 0x59, 0xff, - 0x17, 0x0d, 0x96, 0xe2, 0xc4, 0x0f, 0xc1, 0x9a, 0x78, 0xaa, 0x35, 0xb9, 0x96, 0xed, 0x68, 0x0f, - 0x31, 0x29, 0x3f, 0x4c, 0x19, 0xeb, 0xff, 0x70, 0xbb, 0xa2, 0xff, 0xfe, 0x04, 0xcc, 0xd4, 0x6c, - 0xdf, 0xac, 0xed, 0xee, 0x9a, 0xb6, 0xe9, 0x1f, 0xa0, 0xaf, 0xe5, 0x60, 0xa5, 0xeb, 0x92, 0x5d, - 0xe2, 0xba, 0xa4, 0x75, 0xa9, 0xe7, 0x9a, 0x76, 0xbb, 0xd1, 0xdc, 0x23, 0xad, 0x9e, 0x65, 0xda, - 0xed, 0xb5, 0xb6, 0xed, 0x48, 0xf0, 0xe5, 0x7b, 0xa4, 0xd9, 0xa3, 0xc1, 0x8a, 0x58, 0xff, 0xce, - 0x78, 0xdd, 0xdc, 0x1a, 0x4d, 0x68, 0xfd, 0xa9, 0x41, 0xbf, 0xb2, 0x32, 0x62, 0x23, 0x3c, 0xea, - 0xd0, 0xd0, 0xdb, 0x39, 0xa8, 0xba, 0xe4, 0x73, 0x3d, 0x73, 0xf8, 0xd9, 0xe0, 0x1b, 0xd4, 0x1a, - 0x6f, 0x36, 0xf0, 0x48, 0x32, 0xeb, 0x17, 0x07, 0xfd, 0xca, 0x88, 0x6d, 0xf0, 0x88, 0xe3, 0xd2, - 0xff, 0x4c, 0x83, 0xe2, 0x08, 0x51, 0x52, 0x45, 0x8d, 0x92, 0xa6, 0x13, 0x11, 0x92, 0x9f, 0x8c, - 0x90, 0x5e, 0x18, 0x6f, 0xd2, 0x86, 0x89, 0x8c, 0xfe, 0x8d, 0x9e, 0x23, 0xe2, 0x91, 0x14, 0xda, - 0x83, 0xa5, 0xae, 0xd3, 0x0a, 0x36, 0xfd, 0x55, 0xc3, 0xdb, 0x63, 0x38, 0x31, 0xbc, 0xa7, 0x07, - 0xfd, 0xca, 0xd2, 0x56, 0x0a, 0xfe, 0x7e, 0xbf, 0x52, 0x96, 0x4c, 0x62, 0x04, 0x38, 0x95, 0x23, - 0xea, 0x42, 0x71, 0xd7, 0x24, 0x56, 0x0b, 0x93, 0x5d, 0xa1, 0x29, 0x63, 0x6e, 0xef, 0x2b, 0x82, - 0x5b, 0x7d, 0x86, 0xda, 0xd2, 0xe0, 0x17, 0x96, 0x52, 0xf4, 0x1f, 0x4f, 0xc0, 0x7c, 0xdd, 0xea, - 0x91, 0x17, 0x5c, 0x42, 0x82, 0x38, 0xa0, 0x06, 0xf3, 0x5d, 0x97, 0xec, 0x9b, 0xe4, 0x6e, 0x83, - 0x58, 0xa4, 0xe9, 0x3b, 0xae, 0x18, 0xea, 0x19, 0xb1, 0x92, 0xf3, 0x5b, 0x2a, 0x1a, 0xc7, 0xe9, - 0xd1, 0xf3, 0x30, 0x67, 0x34, 0x7d, 0x73, 0x9f, 0x48, 0x0e, 0x7c, 0xa1, 0x3f, 0x24, 0x38, 0xcc, - 0xd5, 0x14, 0x2c, 0x8e, 0x51, 0xa3, 0x57, 0xa1, 0xec, 0x35, 0x0d, 0x8b, 0xdc, 0xec, 0x0a, 0x51, - 0xab, 0x7b, 0xa4, 0x79, 0x67, 0xcb, 0x31, 0x6d, 0x5f, 0x04, 0x38, 0x17, 0x04, 0xa7, 0x72, 0xe3, - 0x10, 0x3a, 0x7c, 0x28, 0x07, 0xf4, 0xa7, 0x1a, 0x9c, 0xef, 0xba, 0x64, 0xcb, 0x75, 0x3a, 0x0e, - 0xd5, 0xde, 0x44, 0x28, 0x24, 0x42, 0x82, 0x97, 0xc6, 0xdc, 0xa6, 0x1c, 0x92, 0x3c, 0x75, 0x7c, - 0x64, 0xd0, 0xaf, 0x9c, 0xdf, 0x3a, 0xaa, 0x03, 0xf8, 0xe8, 0xfe, 0xa1, 0x6f, 0x6b, 0xb0, 0xdc, - 0x75, 0x3c, 0xff, 0x88, 0x21, 0x14, 0x4e, 0x74, 0x08, 0xfa, 0xa0, 0x5f, 0x59, 0xde, 0x3a, 0xb2, - 0x07, 0xf8, 0x98, 0x1e, 0xea, 0x5f, 0x2e, 0xc1, 0x62, 0x44, 0xf7, 0xe8, 0x81, 0xbe, 0x7d, 0x80, - 0x9e, 0x83, 0xd9, 0x40, 0x19, 0xf8, 0xa9, 0x9a, 0xeb, 0x9e, 0x8c, 0xeb, 0x6a, 0x51, 0x24, 0x56, - 0x69, 0xa9, 0xde, 0x49, 0x55, 0xe4, 0xad, 0x63, 0x7a, 0xb7, 0xa5, 0x60, 0x71, 0x8c, 0x1a, 0xad, - 0xc1, 0x29, 0x01, 0xc1, 0xa4, 0x6b, 0x99, 0x4d, 0x63, 0xd5, 0xe9, 0x09, 0x95, 0x2b, 0xd4, 0xcf, - 0x0c, 0xfa, 0x95, 0x53, 0x5b, 0x49, 0x34, 0x4e, 0x6b, 0x83, 0x36, 0x60, 0xc9, 0xe8, 0xf9, 0x8e, - 0x1c, 0xff, 0x65, 0xdb, 0xd8, 0xb1, 0x48, 0x8b, 0xa9, 0x56, 0xb1, 0x5e, 0xa6, 0x56, 0xa3, 0x96, - 0x82, 0xc7, 0xa9, 0xad, 0xd0, 0x56, 0x8c, 0x5b, 0x83, 0x34, 0x1d, 0xbb, 0xc5, 0x57, 0xb9, 0x50, - 0x3f, 0x27, 0x86, 0xa7, 0x72, 0x14, 0x34, 0x38, 0xb5, 0x25, 0xb2, 0x60, 0xae, 0x63, 0xdc, 0xbb, - 0x69, 0x1b, 0xfb, 0x86, 0x69, 0x51, 0x21, 0xe5, 0xc9, 0x63, 0x42, 0xd3, 0x9e, 0x6f, 0x5a, 0x55, - 0x7e, 0x03, 0x53, 0x5d, 0xb3, 0xfd, 0xeb, 0x6e, 0xc3, 0xa7, 0x4e, 0xa0, 0x8e, 0xe8, 0xc4, 0x6e, - 0x2a, 0xbc, 0x70, 0x8c, 0x37, 0xba, 0x0e, 0xa7, 0xd9, 0x76, 0xbc, 0xe4, 0xdc, 0xb5, 0x2f, 0x11, - 0xcb, 0x38, 0x08, 0x06, 0x30, 0xc5, 0x06, 0xf0, 0xe1, 0x41, 0xbf, 0x72, 0xba, 0x91, 0x46, 0x80, - 0xd3, 0xdb, 0x21, 0x03, 0x1e, 0x55, 0x11, 0x98, 0xec, 0x9b, 0x9e, 0xe9, 0xd8, 0x1b, 0x66, 0xc7, - 0xf4, 0xcb, 0x45, 0xc6, 0xb6, 0x32, 0xe8, 0x57, 0x1e, 0x6d, 0x1c, 0x4e, 0x86, 0x8f, 0xe2, 0x81, - 0x7e, 0x53, 0x83, 0xa5, 0xb4, 0x6d, 0x58, 0x9e, 0xce, 0xe2, 0xfe, 0x23, 0xb6, 0xb5, 0xb8, 0x46, - 0xa4, 0x1a, 0x85, 0xd4, 0x4e, 0xa0, 0x37, 0x34, 0x98, 0x31, 0x22, 0xc1, 0x59, 0x19, 0x58, 0xaf, - 0xd6, 0xc7, 0x8d, 0x86, 0x43, 0x8e, 0xf5, 0x85, 0x41, 0xbf, 0xa2, 0x04, 0x80, 0x58, 0x91, 0x88, - 0x7e, 0x5b, 0x83, 0xd3, 0xa9, 0x7b, 0xbc, 0x5c, 0x3a, 0x89, 0x19, 0x62, 0x4a, 0x92, 0x6e, 0x73, - 0xd2, 0xbb, 0x81, 0xde, 0xd5, 0xa4, 0x2b, 0xdb, 0x0c, 0xce, 0x23, 0x33, 0xac, 0x6b, 0x37, 0xc6, - 0x8c, 0x47, 0x43, 0xef, 0x1d, 0x30, 0xae, 0x9f, 0x8a, 0x78, 0xc6, 0x00, 0x88, 0xe3, 0xe2, 0xd1, - 0xd7, 0xb5, 0xc0, 0x35, 0xca, 0x1e, 0xcd, 0x9e, 0x54, 0x8f, 0x50, 0xe8, 0x69, 0x65, 0x87, 0x62, - 0xc2, 0xf5, 0x7f, 0xce, 0xc3, 0xcc, 0xaa, 0x61, 0x1b, 0xee, 0x81, 0x70, 0x2d, 0x7f, 0xa2, 0xc1, - 0xb9, 0x66, 0xcf, 0x75, 0x89, 0xed, 0x37, 0x7c, 0xd2, 0x4d, 0x3a, 0x16, 0xed, 0x44, 0x1d, 0xcb, - 0x85, 0x41, 0xbf, 0x72, 0x6e, 0xf5, 0x08, 0xf9, 0xf8, 0xc8, 0xde, 0xa1, 0xbf, 0xd6, 0x40, 0x17, - 0x04, 0x75, 0xa3, 0x79, 0xa7, 0xed, 0x3a, 0x3d, 0xbb, 0x95, 0x1c, 0x44, 0xee, 0x44, 0x07, 0xf1, - 0xf8, 0xa0, 0x5f, 0xd1, 0x57, 0x8f, 0xed, 0x05, 0x1e, 0xa2, 0xa7, 0xe8, 0x05, 0x58, 0x14, 0x54, - 0x97, 0xef, 0x75, 0x89, 0x6b, 0xd2, 0xd8, 0x54, 0xdc, 0x34, 0x7f, 0x58, 0x98, 0xfd, 0xc5, 0xd5, - 0x38, 0x01, 0x4e, 0xb6, 0xd1, 0xff, 0x68, 0x02, 0x20, 0x58, 0x69, 0xd2, 0x45, 0x3f, 0x05, 0xd3, - 0x1e, 0xf1, 0x6f, 0x11, 0xb3, 0xbd, 0xe7, 0xb3, 0x35, 0x2d, 0x88, 0x6b, 0x8d, 0x00, 0x88, 0x43, - 0x3c, 0xba, 0x03, 0x85, 0xae, 0xd1, 0xf3, 0x88, 0x98, 0xb7, 0xf5, 0x4c, 0xe6, 0x6d, 0x8b, 0x72, - 0xe4, 0xb1, 0x3f, 0xfb, 0x13, 0x73, 0x19, 0xe8, 0x2b, 0x1a, 0x00, 0x51, 0xc7, 0x5a, 0xba, 0xd8, - 0xc8, 0x44, 0x64, 0x38, 0x1d, 0x74, 0x0e, 0xea, 0x73, 0x83, 0x7e, 0x05, 0x22, 0xb3, 0x16, 0x11, - 0x8b, 0xee, 0x42, 0xd1, 0x08, 0xcc, 0xd9, 0xc4, 0x49, 0x98, 0x33, 0x16, 0x92, 0xcb, 0xf5, 0x96, - 0xc2, 0xd0, 0xdb, 0x1a, 0xcc, 0x79, 0xc4, 0x17, 0x4b, 0x45, 0xfd, 0x93, 0x88, 0xe5, 0x36, 0xc6, - 0x93, 0xdf, 0x50, 0x78, 0x72, 0xe3, 0xa0, 0xc2, 0x70, 0x4c, 0xae, 0xfe, 0x9f, 0x45, 0x98, 0x0b, - 0x54, 0x26, 0x0c, 0xcf, 0x9a, 0x1c, 0x92, 0x1e, 0x9e, 0xad, 0x46, 0x91, 0x58, 0xa5, 0xa5, 0x8d, - 0x3d, 0x9f, 0xc6, 0x03, 0x6a, 0x74, 0x26, 0x1b, 0x37, 0xa2, 0x48, 0xac, 0xd2, 0xa2, 0x0e, 0x14, - 0x3c, 0x9f, 0x74, 0x83, 0x4b, 0xc3, 0xab, 0xe3, 0xcd, 0x46, 0xb8, 0x13, 0xc2, 0x0b, 0x1f, 0xfa, - 0xcb, 0xc3, 0x5c, 0x0a, 0x7a, 0x47, 0x83, 0x39, 0x5f, 0x49, 0x28, 0x09, 0x35, 0xc8, 0x46, 0x13, - 0xd5, 0x5c, 0x15, 0x5f, 0x0d, 0x15, 0x86, 0x63, 0xe2, 0x53, 0x22, 0xb6, 0xc2, 0x09, 0x46, 0x6c, - 0x2f, 0x43, 0xb1, 0x63, 0xdc, 0x6b, 0xf4, 0xdc, 0xf6, 0x83, 0x47, 0x86, 0x4c, 0xc5, 0x37, 0x05, - 0x17, 0x2c, 0xf9, 0xa1, 0x37, 0xb5, 0xc8, 0xe6, 0x9a, 0x62, 0xcc, 0x6f, 0x65, 0xbb, 0xb9, 0xa4, - 0x41, 0x3d, 0x74, 0x9b, 0x25, 0xe2, 0xa7, 0xe2, 0x43, 0x8f, 0x9f, 0x68, 0x2c, 0xc0, 0x37, 0x88, - 0x8c, 0x05, 0xa6, 0x4f, 0x34, 0x16, 0x58, 0x55, 0x84, 0xe1, 0x98, 0x70, 0xd6, 0x1f, 0xbe, 0xe7, - 0x64, 0x7f, 0xe0, 0x44, 0xfb, 0xd3, 0x50, 0x84, 0xe1, 0x98, 0x70, 0xfd, 0x87, 0x1a, 0x9c, 0x59, - 0xb5, 0x7a, 0x9e, 0x4f, 0xdc, 0xff, 0x35, 0x77, 0xea, 0xff, 0xa1, 0xc1, 0xa3, 0x87, 0x8c, 0xf9, - 0x21, 0x5c, 0xad, 0xbf, 0xae, 0x5e, 0xad, 0xdf, 0x1c, 0xd3, 0xc6, 0xa6, 0x8f, 0xe3, 0x90, 0x1b, - 0x76, 0x1f, 0x66, 0x2f, 0x19, 0xbe, 0xd1, 0x72, 0xda, 0xfc, 0xca, 0x1b, 0x3d, 0x0f, 0x45, 0xd3, - 0xf6, 0x89, 0xbb, 0x6f, 0x58, 0xc2, 0xcb, 0xe8, 0x41, 0xd7, 0xd7, 0x04, 0xfc, 0x7e, 0xbf, 0x32, - 0x77, 0xa9, 0xe7, 0xb2, 0xb4, 0x38, 0xb7, 0x39, 0x58, 0xb6, 0x41, 0x8f, 0x41, 0xe1, 0x73, 0x3d, - 0xe2, 0x1e, 0xc4, 0x53, 0xb1, 0x37, 0x28, 0x10, 0x73, 0x9c, 0xfe, 0xb7, 0x39, 0x88, 0x44, 0x00, - 0x0f, 0x41, 0xad, 0x6c, 0x45, 0xad, 0xc6, 0xf4, 0xe9, 0x91, 0x78, 0xe6, 0xb0, 0x1c, 0xfa, 0x7e, - 0x2c, 0x87, 0x7e, 0x2d, 0x33, 0x89, 0x47, 0xa7, 0xd0, 0xdf, 0xd7, 0xe0, 0xd1, 0x90, 0x38, 0x19, - 0xd7, 0x1e, 0x7f, 0x49, 0xfc, 0x0c, 0x94, 0x8c, 0xb0, 0x99, 0x58, 0x45, 0x59, 0x5d, 0x11, 0xe1, - 0x88, 0xa3, 0x74, 0x61, 0x1a, 0x33, 0xff, 0x80, 0x69, 0xcc, 0x89, 0xa3, 0xd3, 0x98, 0xfa, 0x8f, - 0x72, 0x70, 0x3e, 0x39, 0xb2, 0x40, 0xbb, 0x31, 0xd9, 0x1d, 0x62, 0x6c, 0xcf, 0xc2, 0x8c, 0x2f, - 0x1a, 0x50, 0xa8, 0x18, 0xdc, 0x92, 0xa0, 0x9c, 0xd9, 0x8e, 0xe0, 0xb0, 0x42, 0x49, 0x5b, 0x36, - 0xf9, 0xbe, 0x6a, 0x34, 0x9d, 0x6e, 0x90, 0xef, 0x95, 0x2d, 0x57, 0x23, 0x38, 0xac, 0x50, 0xca, - 0xc4, 0xd1, 0xc4, 0x89, 0x27, 0xa4, 0x1b, 0x70, 0x3a, 0xc8, 0x1f, 0x5c, 0x71, 0xdc, 0x55, 0xa7, - 0xd3, 0xb5, 0x08, 0x4b, 0x7f, 0x14, 0x58, 0x67, 0xcf, 0x8b, 0x26, 0xa7, 0x71, 0x1a, 0x11, 0x4e, - 0x6f, 0xab, 0xbf, 0x9f, 0x87, 0x53, 0xe1, 0xb4, 0xaf, 0x3a, 0x76, 0xcb, 0x64, 0x59, 0x98, 0xe7, - 0x60, 0xc2, 0x3f, 0xe8, 0x06, 0x93, 0xfd, 0xff, 0x82, 0xee, 0x6c, 0x1f, 0x74, 0xe9, 0x6a, 0x9f, - 0x49, 0x69, 0x42, 0x51, 0x98, 0x35, 0x42, 0x1b, 0x72, 0x77, 0xf0, 0x15, 0x78, 0x5a, 0xd5, 0xe6, - 0xfb, 0xfd, 0x4a, 0x4a, 0x4d, 0x55, 0x55, 0x72, 0x52, 0x75, 0x1e, 0xdd, 0x86, 0x39, 0xcb, 0xf0, - 0xfc, 0x9b, 0xdd, 0x96, 0xe1, 0x93, 0x6d, 0xb3, 0x43, 0xc4, 0x9e, 0x1b, 0x25, 0xb7, 0x2c, 0xaf, - 0x2a, 0x37, 0x14, 0x4e, 0x38, 0xc6, 0x19, 0xed, 0x03, 0xa2, 0x90, 0x6d, 0xd7, 0xb0, 0x3d, 0x3e, - 0x2a, 0x2a, 0x6f, 0xf4, 0x5c, 0xf6, 0x59, 0x21, 0x0f, 0x6d, 0x24, 0xb8, 0xe1, 0x14, 0x09, 0xe8, - 0x71, 0x98, 0x74, 0x89, 0xe1, 0x89, 0xc5, 0x9c, 0x0e, 0xf7, 0x3f, 0x66, 0x50, 0x2c, 0xb0, 0xd1, - 0x0d, 0x35, 0x79, 0xcc, 0x86, 0xfa, 0xbe, 0x06, 0x73, 0xe1, 0x32, 0x3d, 0x04, 0x37, 0xd7, 0x51, - 0xdd, 0xdc, 0xd5, 0xac, 0x4c, 0xe2, 0x21, 0x9e, 0xed, 0x83, 0x7c, 0x74, 0x7c, 0x2c, 0x6b, 0xfc, - 0x79, 0x98, 0x0e, 0x76, 0x75, 0x90, 0x37, 0x1e, 0x33, 0xf2, 0x54, 0x22, 0x8b, 0x48, 0xf9, 0x87, - 0x10, 0x82, 0x43, 0x79, 0xd4, 0xb1, 0xb6, 0x84, 0xd3, 0x14, 0x6a, 0x2f, 0x1d, 0x6b, 0xe0, 0x4c, - 0xd3, 0x1c, 0x6b, 0xd0, 0x06, 0xdd, 0x84, 0x33, 0x5d, 0xd7, 0x61, 0x95, 0x73, 0x97, 0x88, 0xd1, - 0xb2, 0x4c, 0x9b, 0x04, 0xd7, 0xb9, 0xfc, 0xa6, 0xfc, 0xd1, 0x41, 0xbf, 0x72, 0x66, 0x2b, 0x9d, - 0x04, 0x1f, 0xd6, 0x56, 0x2d, 0x63, 0x99, 0x38, 0xbe, 0x8c, 0x05, 0xfd, 0x92, 0x3c, 0x46, 0x10, - 0xaf, 0x5c, 0x60, 0x93, 0xf8, 0x4a, 0x56, 0x4b, 0x99, 0x62, 0xd6, 0x43, 0x95, 0xaa, 0x09, 0xa1, - 0x58, 0x8a, 0xd7, 0xdf, 0x2a, 0xc0, 0x42, 0xdc, 0x37, 0x9e, 0x7c, 0x45, 0xcd, 0xaf, 0x6a, 0xb0, - 0x10, 0xac, 0x2b, 0x97, 0x49, 0x82, 0xf3, 0xf1, 0x46, 0x46, 0xea, 0xc4, 0xbd, 0xbc, 0x2c, 0x4c, - 0xdc, 0x8e, 0x49, 0xc3, 0x09, 0xf9, 0xe8, 0x35, 0x28, 0xc9, 0x63, 0xe4, 0x03, 0x95, 0xd7, 0xcc, - 0x33, 0xff, 0x1e, 0xb2, 0xc0, 0x51, 0x7e, 0xe8, 0x2d, 0x0d, 0xa0, 0x19, 0x18, 0xe0, 0x60, 0xdd, - 0x6f, 0x64, 0xb5, 0xee, 0xd2, 0xb4, 0x87, 0x61, 0x9c, 0x04, 0x79, 0x38, 0x22, 0x18, 0xfd, 0x1a, - 0x3b, 0x40, 0xca, 0xb8, 0xc3, 0x2b, 0x4f, 0xb2, 0x9e, 0x7c, 0x26, 0x6b, 0x0d, 0x0c, 0xaf, 0x15, - 0xa5, 0x93, 0x8f, 0xa0, 0x3c, 0xac, 0x74, 0x42, 0x7f, 0x0e, 0x64, 0x9a, 0x97, 0x6e, 0x28, 0x96, - 0xe8, 0xdd, 0x32, 0xfc, 0x3d, 0xa1, 0x82, 0x72, 0x43, 0x5d, 0x09, 0x10, 0x38, 0xa4, 0xd1, 0xff, - 0x5c, 0x83, 0xa5, 0x35, 0xcf, 0x37, 0x9d, 0x4b, 0xc4, 0xf3, 0xe9, 0x1e, 0xa3, 0xee, 0xb8, 0x67, - 0x91, 0x21, 0x02, 0x9a, 0x4b, 0xb0, 0x20, 0xee, 0x7a, 0x7a, 0x3b, 0x1e, 0xf1, 0x23, 0x41, 0x8d, - 0x54, 0x9d, 0xd5, 0x18, 0x1e, 0x27, 0x5a, 0x50, 0x2e, 0xe2, 0xd2, 0x27, 0xe4, 0x92, 0x57, 0xb9, - 0x34, 0x62, 0x78, 0x9c, 0x68, 0xa1, 0x7f, 0x33, 0x07, 0xa7, 0xd8, 0x30, 0x62, 0x05, 0xc1, 0xbf, - 0xa2, 0xc1, 0xdc, 0xbe, 0xe9, 0xfa, 0x3d, 0xc3, 0x8a, 0xde, 0x5e, 0x8d, 0xad, 0x3d, 0x4c, 0xd6, - 0x4b, 0x0a, 0xe3, 0xd0, 0x8d, 0xab, 0x70, 0x1c, 0xeb, 0x00, 0xed, 0xd3, 0x7c, 0x4b, 0x9d, 0xed, - 0x6c, 0x4e, 0x9c, 0x69, 0xeb, 0xc8, 0x73, 0x14, 0x31, 0x20, 0x8e, 0xcb, 0xd7, 0x5f, 0x11, 0xd3, - 0xa7, 0x76, 0x7d, 0x08, 0x25, 0xd0, 0x61, 0xd2, 0x75, 0x7a, 0xd4, 0xa5, 0x51, 0xc7, 0x3a, 0x5d, - 0x07, 0x16, 0x17, 0x30, 0x08, 0x16, 0x18, 0xfd, 0x0f, 0x35, 0x98, 0x5e, 0x77, 0x76, 0xc4, 0x19, - 0xef, 0xe7, 0x33, 0x38, 0x6f, 0x49, 0xb3, 0x2c, 0x2f, 0x12, 0x42, 0x4f, 0xff, 0xbc, 0x72, 0xda, - 0x3a, 0x17, 0xe1, 0x5d, 0x65, 0x55, 0xf4, 0x94, 0xd5, 0xba, 0xb3, 0x73, 0xe8, 0x71, 0xfc, 0x77, - 0x0b, 0x30, 0xfb, 0xa2, 0x71, 0x40, 0x6c, 0xdf, 0x10, 0x3d, 0xfe, 0x18, 0x4c, 0x19, 0xad, 0x56, - 0x5a, 0x55, 0x79, 0x8d, 0x83, 0x71, 0x80, 0x67, 0x07, 0x98, 0x2e, 0x4b, 0x09, 0x47, 0x5c, 0x6d, - 0x78, 0x80, 0x09, 0x51, 0x38, 0x4a, 0x17, 0x6e, 0xa5, 0x55, 0xc7, 0xde, 0x35, 0xdb, 0x69, 0x9b, - 0x60, 0x35, 0x86, 0xc7, 0x89, 0x16, 0x68, 0x1d, 0x90, 0xa8, 0x18, 0xab, 0x35, 0x9b, 0x4e, 0xcf, - 0xe6, 0x9b, 0x89, 0x9f, 0x6d, 0x64, 0xcc, 0xb7, 0x99, 0xa0, 0xc0, 0x29, 0xad, 0xd0, 0xab, 0x50, - 0x6e, 0x32, 0xce, 0x22, 0x02, 0x88, 0x72, 0xe4, 0x51, 0xa0, 0x2c, 0xc7, 0x58, 0x3d, 0x84, 0x0e, - 0x1f, 0xca, 0x81, 0xf6, 0xd4, 0xf3, 0x1d, 0xd7, 0x68, 0x93, 0x28, 0xdf, 0x49, 0xb5, 0xa7, 0x8d, - 0x04, 0x05, 0x4e, 0x69, 0x85, 0xbe, 0x04, 0xd3, 0xfe, 0x9e, 0x4b, 0xbc, 0x3d, 0xc7, 0x6a, 0x89, - 0x9b, 0xc5, 0x31, 0x0f, 0xbc, 0x62, 0xf5, 0xb7, 0x03, 0xae, 0x91, 0x98, 0x24, 0x00, 0xe1, 0x50, - 0x26, 0x72, 0x61, 0xd2, 0xa3, 0xa7, 0x2d, 0xaf, 0x5c, 0xcc, 0x22, 0xaa, 0x13, 0xd2, 0xd9, 0x01, - 0x2e, 0x72, 0xd4, 0x66, 0x12, 0xb0, 0x90, 0xa4, 0xff, 0x45, 0x0e, 0x66, 0xa2, 0x84, 0x43, 0xec, - 0xd4, 0xaf, 0x68, 0x30, 0xd3, 0x74, 0x6c, 0xdf, 0x75, 0x2c, 0x7e, 0x8c, 0xe4, 0x1b, 0x64, 0xcc, - 0xca, 0x6c, 0xc6, 0xea, 0x12, 0xf1, 0x0d, 0xd3, 0x8a, 0x9c, 0x48, 0x23, 0x62, 0xb0, 0x22, 0x14, - 0x7d, 0x4d, 0x83, 0xf9, 0x30, 0xe5, 0x12, 0x9e, 0x67, 0x33, 0xed, 0x88, 0xac, 0x5a, 0xba, 0xac, - 0x4a, 0xc2, 0x71, 0xd1, 0xfa, 0x0e, 0x2c, 0xc4, 0x57, 0x9b, 0x4e, 0x65, 0xd7, 0x10, 0x7b, 0x3d, - 0x1f, 0x4e, 0xe5, 0x96, 0xe1, 0x79, 0x98, 0x61, 0xd0, 0xc7, 0xa1, 0xd8, 0x31, 0xdc, 0xb6, 0x69, - 0x1b, 0x16, 0x9b, 0xc5, 0x7c, 0xc4, 0x20, 0x09, 0x38, 0x96, 0x14, 0xfa, 0x0f, 0x26, 0xa0, 0xb4, - 0x49, 0x0c, 0xaf, 0xe7, 0x12, 0x76, 0xe1, 0x74, 0xe2, 0x21, 0xa2, 0x52, 0xea, 0x9c, 0xcf, 0xae, - 0xd4, 0x19, 0xbd, 0x0c, 0xb0, 0x6b, 0xda, 0xa6, 0xb7, 0xf7, 0x80, 0x45, 0xd4, 0x2c, 0xf9, 0x76, - 0x45, 0x72, 0xc0, 0x11, 0x6e, 0xe1, 0x2b, 0x8a, 0xc2, 0x11, 0xaf, 0x28, 0xde, 0xd2, 0x22, 0xce, - 0x83, 0x07, 0x5f, 0xb7, 0xc6, 0xad, 0xbd, 0x95, 0x0b, 0x53, 0x0d, 0x9c, 0xc9, 0x65, 0xdb, 0x77, - 0x0f, 0x8e, 0xf4, 0x31, 0xdb, 0x50, 0x74, 0x89, 0xd7, 0xeb, 0xd0, 0x60, 0x77, 0x6a, 0xe4, 0x69, - 0x60, 0xf9, 0x09, 0x2c, 0xda, 0x63, 0xc9, 0xe9, 0xec, 0x73, 0x30, 0xab, 0x74, 0x01, 0x2d, 0x40, - 0xfe, 0x0e, 0x39, 0xe0, 0x7a, 0x82, 0xe9, 0x9f, 0x68, 0x49, 0xa9, 0xa2, 0x14, 0xd3, 0xf2, 0xa9, - 0xdc, 0xb3, 0x9a, 0xfe, 0xa3, 0x49, 0x98, 0x14, 0xfe, 0xea, 0x78, 0x5b, 0x10, 0xbd, 0x67, 0xcd, - 0x3d, 0xc0, 0x3d, 0xeb, 0x3a, 0xcc, 0x98, 0xb6, 0xe9, 0x9b, 0x86, 0xc5, 0x8a, 0x68, 0x84, 0xaf, - 0x7a, 0x3c, 0xd8, 0xff, 0x6b, 0x11, 0x5c, 0x0a, 0x1f, 0xa5, 0x2d, 0xba, 0x01, 0x05, 0x66, 0xcc, - 0x85, 0x3e, 0x8d, 0x9e, 0x72, 0x62, 0xe9, 0x64, 0x5e, 0x96, 0xc5, 0x39, 0xb1, 0x98, 0xb2, 0xd7, - 0x6c, 0x12, 0xcf, 0x93, 0x81, 0xbc, 0x50, 0xab, 0x30, 0xa6, 0x8c, 0xe1, 0x71, 0xa2, 0x05, 0xe5, - 0xb2, 0x6b, 0x98, 0x56, 0xcf, 0x25, 0x21, 0x97, 0x49, 0x95, 0xcb, 0x95, 0x18, 0x1e, 0x27, 0x5a, - 0xa0, 0x5d, 0x98, 0x11, 0x30, 0x5e, 0xa6, 0x34, 0xf5, 0x80, 0xa3, 0x64, 0x99, 0xa5, 0x2b, 0x11, - 0x4e, 0x58, 0xe1, 0x8b, 0x7a, 0xb0, 0x68, 0xda, 0x4d, 0xc7, 0x6e, 0x5a, 0x3d, 0xcf, 0xdc, 0x27, - 0x61, 0x4d, 0xd4, 0x83, 0x08, 0x3b, 0x3d, 0xe8, 0x57, 0x16, 0xd7, 0xe2, 0xec, 0x70, 0x52, 0x02, - 0x7a, 0x53, 0x83, 0xd3, 0x4d, 0xc7, 0xf6, 0x58, 0x55, 0xf0, 0x3e, 0xb9, 0xec, 0xba, 0x8e, 0xcb, - 0x65, 0x4f, 0x3f, 0xa0, 0x6c, 0x56, 0xf3, 0xb3, 0x9a, 0xc6, 0x12, 0xa7, 0x4b, 0x42, 0xaf, 0x43, - 0xb1, 0xeb, 0x3a, 0xfb, 0x66, 0x8b, 0xb8, 0x22, 0x7b, 0xb5, 0x91, 0x45, 0x41, 0xfe, 0x96, 0xe0, - 0x19, 0x5a, 0x82, 0x00, 0x82, 0xa5, 0x3c, 0xfd, 0x1b, 0x93, 0x30, 0xa7, 0x92, 0xa3, 0x2f, 0x02, - 0x74, 0x5d, 0xa7, 0x43, 0xfc, 0x3d, 0x22, 0x6b, 0x67, 0xae, 0x8d, 0x5b, 0x0c, 0x1f, 0xf0, 0x13, - 0x6f, 0x05, 0x98, 0x25, 0x0d, 0xa1, 0x38, 0x22, 0x11, 0xb9, 0x30, 0x75, 0x87, 0xfb, 0x34, 0xe1, - 0xe2, 0x5f, 0xcc, 0x24, 0x20, 0x11, 0x92, 0x4b, 0xd4, 0xe5, 0x08, 0x10, 0x0e, 0x04, 0xa1, 0x1d, - 0xc8, 0xdf, 0x25, 0x3b, 0xd9, 0x94, 0x6d, 0xdf, 0x22, 0xe2, 0xa8, 0x50, 0x9f, 0x1a, 0xf4, 0x2b, - 0xf9, 0x5b, 0x64, 0x07, 0x53, 0xe6, 0x74, 0x5c, 0x2d, 0x9e, 0x2d, 0x12, 0xa6, 0x62, 0xcc, 0x71, - 0x29, 0xa9, 0x27, 0x3e, 0x2e, 0x01, 0xc2, 0x81, 0x20, 0xf4, 0x3a, 0x4c, 0xdf, 0x35, 0xf6, 0xc9, - 0xae, 0xeb, 0xd8, 0xbe, 0xc8, 0xbd, 0x8f, 0x59, 0x13, 0x72, 0x2b, 0x60, 0x27, 0xe4, 0x32, 0x6f, - 0x2b, 0x81, 0x38, 0x14, 0x87, 0xf6, 0xa1, 0x68, 0x93, 0xbb, 0x98, 0x58, 0x66, 0x53, 0xa4, 0xe3, - 0xc7, 0x54, 0xeb, 0x6b, 0x82, 0x9b, 0x90, 0xcc, 0xdc, 0x50, 0x00, 0xc3, 0x52, 0x16, 0x5d, 0xcb, - 0xdb, 0xce, 0x8e, 0x30, 0x54, 0x63, 0xae, 0xa5, 0x3c, 0xf6, 0xf1, 0xb5, 0x5c, 0x77, 0x76, 0x30, - 0x65, 0xae, 0x7f, 0x73, 0x02, 0x66, 0xa2, 0xcf, 0xb5, 0x86, 0xf0, 0x59, 0x32, 0x6c, 0xca, 0x8d, - 0x12, 0x36, 0xd1, 0xa8, 0xb7, 0x13, 0xfa, 0xf8, 0xe0, 0xaa, 0x6c, 0x2d, 0xb3, 0xa8, 0x21, 0x8c, - 0x7a, 0x23, 0x40, 0x0f, 0x2b, 0x42, 0x47, 0x48, 0x35, 0xd1, 0x38, 0x88, 0xbb, 0x43, 0x5e, 0xe7, - 0x2b, 0xe3, 0x20, 0xc5, 0xc1, 0x5d, 0x04, 0x10, 0xee, 0x6a, 0xb7, 0x67, 0x31, 0xe5, 0x28, 0x84, - 0x97, 0x57, 0x0d, 0x89, 0xc1, 0x11, 0x2a, 0xf4, 0x38, 0x4c, 0x52, 0x87, 0x41, 0x5a, 0xa2, 0x00, - 0x57, 0x1e, 0x2d, 0xae, 0x30, 0x28, 0x16, 0x58, 0xf4, 0x2c, 0xf5, 0xed, 0xa1, 0x99, 0x17, 0x75, - 0xb5, 0x4b, 0xa1, 0x6f, 0x0f, 0x71, 0x58, 0xa1, 0xa4, 0x5d, 0x27, 0xd4, 0x2a, 0x33, 0xd3, 0x1f, - 0xe9, 0x3a, 0x33, 0xd5, 0x98, 0xe3, 0xd8, 0x51, 0x37, 0x66, 0xc5, 0x99, 0xd1, 0x2e, 0x44, 0x8e, - 0xba, 0x31, 0x3c, 0x4e, 0xb4, 0xd0, 0x3f, 0x0b, 0x73, 0xaa, 0x36, 0xd3, 0x29, 0xee, 0xba, 0xce, - 0xae, 0x69, 0x91, 0xf8, 0x21, 0x7d, 0x8b, 0x83, 0x71, 0x80, 0x1f, 0x2e, 0x4b, 0xfc, 0x97, 0x79, - 0x38, 0x75, 0xad, 0x6d, 0xda, 0xf7, 0x62, 0x37, 0x4a, 0x69, 0x2f, 0xb9, 0xb5, 0x51, 0x5f, 0x72, - 0x87, 0x65, 0x51, 0xe2, 0x5d, 0x7a, 0x7a, 0x59, 0x54, 0xf0, 0x68, 0x5d, 0xa5, 0x45, 0xdf, 0xd7, - 0xe0, 0x9c, 0xd1, 0xe2, 0xf1, 0x85, 0x61, 0x09, 0x68, 0x28, 0x34, 0xd0, 0x71, 0x6f, 0x4c, 0x6b, - 0x91, 0x1c, 0x7c, 0xb5, 0x76, 0x84, 0x54, 0x1e, 0x35, 0x7f, 0x54, 0x8c, 0xe0, 0xdc, 0x51, 0xa4, - 0xf8, 0xc8, 0xee, 0x9f, 0xbd, 0x0e, 0x1f, 0x39, 0x56, 0xd0, 0x48, 0xb1, 0xf1, 0xef, 0x69, 0x30, - 0xc7, 0xea, 0x0d, 0xc3, 0xb0, 0xec, 0x19, 0x99, 0xd3, 0xe2, 0x8b, 0x77, 0x5e, 0xcd, 0x69, 0xdd, - 0xef, 0x57, 0x4a, 0xbc, 0x42, 0x51, 0x4d, 0x71, 0xbd, 0x22, 0x8e, 0x56, 0x2c, 0xf3, 0x96, 0x1b, - 0x39, 0xf2, 0x97, 0x17, 0x09, 0x8d, 0x80, 0x09, 0x0e, 0xf9, 0xe9, 0xdf, 0xc8, 0xc3, 0xa9, 0x94, - 0xc2, 0x19, 0x7a, 0xea, 0x99, 0xb4, 0x8c, 0x1d, 0x62, 0x05, 0x79, 0xa3, 0xd7, 0x32, 0x2f, 0xce, - 0xa9, 0x6e, 0x30, 0xfe, 0x7c, 0x0d, 0xa5, 0x65, 0xe0, 0x40, 0x2c, 0x84, 0xa3, 0xdf, 0xd0, 0xa0, - 0x64, 0x44, 0xd4, 0x8c, 0xa7, 0xd2, 0x76, 0xb2, 0xef, 0x4c, 0x42, 0xab, 0x22, 0x25, 0x00, 0xa1, - 0x12, 0x45, 0xfb, 0x72, 0xf6, 0x67, 0xa1, 0x14, 0x19, 0xc2, 0x28, 0xda, 0x71, 0xf6, 0x79, 0x58, - 0x18, 0x4b, 0xbb, 0x3e, 0x03, 0xa3, 0xbe, 0x3b, 0xa4, 0xb6, 0xf8, 0x6e, 0xb4, 0x0c, 0x57, 0xce, - 0xb8, 0xa8, 0xc3, 0x15, 0x58, 0x7d, 0x07, 0x16, 0xe2, 0xa1, 0xdf, 0x28, 0xb7, 0x91, 0x43, 0x19, - 0xba, 0x4f, 0xc2, 0x88, 0x2f, 0x05, 0xf5, 0xbf, 0xca, 0xc1, 0x94, 0xa8, 0xbe, 0x7b, 0x08, 0xd5, - 0x33, 0x77, 0x94, 0xfb, 0xdc, 0xb5, 0x4c, 0x8a, 0x06, 0x0f, 0x2d, 0x9d, 0xf1, 0x62, 0xa5, 0x33, - 0x2f, 0x66, 0x23, 0xee, 0xe8, 0xba, 0x99, 0x77, 0x72, 0x30, 0x1f, 0xab, 0x66, 0x44, 0xbf, 0xa8, - 0x25, 0xd3, 0xc5, 0x37, 0x33, 0x2d, 0x98, 0x94, 0xb5, 0x59, 0x47, 0x67, 0x8e, 0x3d, 0xe5, 0xed, - 0xf1, 0x8d, 0xcc, 0xbe, 0xe3, 0x70, 0xe4, 0x33, 0xe4, 0x7f, 0xd2, 0xe0, 0xc3, 0x87, 0xd6, 0x77, - 0xb2, 0x37, 0x1e, 0xae, 0x8a, 0x15, 0xba, 0x97, 0x71, 0xbd, 0xb6, 0xbc, 0x47, 0x8c, 0x97, 0xfd, - 0xc7, 0xc5, 0xa3, 0xa7, 0x61, 0x86, 0xd9, 0x71, 0xba, 0x7d, 0x7c, 0xd2, 0x15, 0x9f, 0x93, 0x61, - 0x67, 0xf6, 0x46, 0x04, 0x8e, 0x15, 0x2a, 0xfd, 0x77, 0x34, 0x28, 0x1f, 0xf6, 0xa2, 0x60, 0x88, - 0x88, 0xf8, 0x67, 0x62, 0x95, 0x2c, 0x95, 0x44, 0x25, 0x4b, 0x2c, 0x26, 0x0e, 0x8a, 0x56, 0x22, - 0xe1, 0x68, 0xfe, 0x98, 0x42, 0x8d, 0xaf, 0x6b, 0x70, 0xe6, 0x10, 0xc5, 0x49, 0x54, 0x34, 0x69, - 0x0f, 0x5c, 0xd1, 0x94, 0x1b, 0xb6, 0xa2, 0x49, 0xff, 0x9b, 0x3c, 0x2c, 0x88, 0xfe, 0x84, 0xce, - 0xfc, 0x59, 0xa5, 0x1e, 0xe8, 0xa3, 0xb1, 0x7a, 0xa0, 0xa5, 0x38, 0xfd, 0xff, 0x15, 0x03, 0xfd, - 0x64, 0x15, 0x03, 0xfd, 0x38, 0x07, 0xa7, 0x53, 0x5f, 0x6b, 0xa0, 0xb7, 0x53, 0xac, 0xe0, 0xad, - 0x8c, 0x9f, 0x85, 0x0c, 0x69, 0x07, 0xc7, 0xad, 0xa0, 0xf9, 0xf5, 0x68, 0xe5, 0x0a, 0x0f, 0xd0, - 0x77, 0x4f, 0xe0, 0x81, 0xcb, 0xa8, 0x45, 0x2c, 0xbf, 0x9c, 0x87, 0x27, 0x86, 0x65, 0xf4, 0x13, - 0x5a, 0xe4, 0xe8, 0x29, 0x45, 0x8e, 0x0f, 0xc7, 0x43, 0x9d, 0x4c, 0xbd, 0xe3, 0x57, 0xf3, 0xd2, - 0xed, 0x25, 0xf5, 0x73, 0xa8, 0x6b, 0xfd, 0x29, 0x1a, 0xc5, 0x04, 0xdf, 0x12, 0x08, 0x4d, 0xe1, - 0x54, 0x83, 0x83, 0xef, 0xf7, 0x2b, 0x8b, 0xe2, 0xc9, 0x72, 0x83, 0xf8, 0x02, 0x88, 0x83, 0x46, - 0xe8, 0x09, 0x28, 0xba, 0x1c, 0x1b, 0x94, 0x75, 0x89, 0x54, 0x05, 0x87, 0x61, 0x89, 0x45, 0x5f, - 0x8a, 0x84, 0x7d, 0x13, 0x27, 0xf5, 0x60, 0xe0, 0xa8, 0x0c, 0xcc, 0x6b, 0x50, 0xf4, 0x82, 0x0f, - 0x0d, 0xf0, 0x7b, 0xb9, 0xa7, 0x86, 0xac, 0x16, 0xa4, 0xa7, 0x84, 0xe0, 0xab, 0x03, 0x7c, 0x7c, - 0xf2, 0x9b, 0x04, 0x92, 0xa5, 0xfe, 0xbe, 0x06, 0x25, 0xb1, 0x12, 0x0f, 0xa1, 0x38, 0xf1, 0xb6, - 0x5a, 0x9c, 0x78, 0x39, 0x13, 0xbb, 0x70, 0x48, 0x65, 0xe2, 0x6d, 0x98, 0x89, 0x3e, 0xc6, 0x43, - 0x2f, 0x47, 0xec, 0x9a, 0x36, 0xce, 0xa3, 0x9f, 0xc0, 0xf2, 0x85, 0x36, 0x4f, 0xff, 0x4e, 0x41, - 0xce, 0x22, 0x2b, 0x81, 0x8c, 0xea, 0x97, 0x76, 0xa4, 0x7e, 0x45, 0x97, 0x37, 0x97, 0xf9, 0xf2, - 0xa2, 0x1b, 0x50, 0x0c, 0x8c, 0x8f, 0x70, 0xd1, 0x8f, 0x45, 0xeb, 0x44, 0xa8, 0x9f, 0xa7, 0xcc, - 0x22, 0x4a, 0xc9, 0x4e, 0x0c, 0x72, 0x0d, 0xa5, 0x51, 0x94, 0x6c, 0x50, 0x0d, 0xe6, 0x3b, 0xa6, - 0x8d, 0x89, 0xd1, 0x92, 0x0f, 0xdd, 0x27, 0xf8, 0x37, 0x04, 0x82, 0x20, 0x72, 0x53, 0x45, 0xe3, - 0x38, 0x3d, 0xfa, 0x3c, 0x14, 0x3d, 0xf1, 0xe8, 0x2e, 0x9b, 0xbb, 0x66, 0x79, 0xfc, 0xe0, 0x4c, - 0xc3, 0xfe, 0x07, 0x10, 0x2c, 0x05, 0xa2, 0x0d, 0x58, 0x72, 0xc5, 0x5b, 0xf8, 0xab, 0xa6, 0xe7, - 0x3b, 0xee, 0x01, 0x4f, 0xe3, 0xf0, 0xcb, 0x45, 0xf6, 0x54, 0x1d, 0xa7, 0xe0, 0x71, 0x6a, 0x2b, - 0x1a, 0x25, 0xb0, 0x97, 0x9d, 0xfc, 0xb2, 0xb1, 0x18, 0x46, 0x09, 0x4c, 0xe9, 0x5a, 0x58, 0x60, - 0x8f, 0xaa, 0x2b, 0x2d, 0x8e, 0x51, 0x57, 0x7a, 0x0b, 0xa6, 0x5d, 0xc2, 0x42, 0xed, 0x5a, 0x90, - 0x88, 0x1a, 0x39, 0x03, 0x8e, 0x03, 0x06, 0x38, 0xe4, 0xa5, 0xff, 0xf1, 0x0c, 0xcc, 0x2a, 0x87, - 0x3a, 0x7a, 0xc6, 0x36, 0x76, 0x1c, 0x97, 0x9f, 0xe4, 0x8b, 0xe1, 0xa6, 0xab, 0x51, 0x20, 0xe6, - 0x38, 0xf4, 0x8e, 0x06, 0xf3, 0x5d, 0xe5, 0x02, 0x2a, 0xd8, 0xeb, 0x63, 0x5e, 0xe9, 0xab, 0xb7, - 0x5a, 0x91, 0xcf, 0xb5, 0xa8, 0xc2, 0x70, 0x5c, 0x3a, 0x55, 0x57, 0x51, 0x97, 0x61, 0x11, 0x97, - 0x51, 0x0b, 0x8f, 0x2b, 0x59, 0xac, 0xaa, 0x68, 0x1c, 0xa7, 0xa7, 0x93, 0xcc, 0x46, 0x37, 0xce, - 0x17, 0xd5, 0x6a, 0x01, 0x03, 0x1c, 0xf2, 0x42, 0xcf, 0xc3, 0x9c, 0x78, 0xcb, 0xbc, 0xe5, 0xb4, - 0xae, 0x1a, 0xde, 0x9e, 0x08, 0x35, 0x65, 0x68, 0xbc, 0xaa, 0x60, 0x71, 0x8c, 0x9a, 0x8d, 0x2d, - 0x7c, 0x30, 0xce, 0x18, 0x4c, 0xaa, 0x5f, 0xb3, 0x59, 0x55, 0xd1, 0x38, 0x4e, 0x8f, 0x3e, 0x1e, - 0xb1, 0x54, 0xfc, 0xba, 0x5c, 0xee, 0x9d, 0x14, 0x6b, 0x55, 0x83, 0xf9, 0x1e, 0x8b, 0xcc, 0x5b, - 0x01, 0x52, 0x68, 0xaf, 0x14, 0x78, 0x53, 0x45, 0xe3, 0x38, 0x3d, 0x7a, 0x0e, 0x66, 0x5d, 0x6a, - 0x0b, 0x24, 0x03, 0x7e, 0x87, 0x2e, 0x2f, 0x84, 0x71, 0x14, 0x89, 0x55, 0x5a, 0xf4, 0x02, 0x2c, - 0x86, 0xaf, 0x3a, 0x03, 0x06, 0xfc, 0x52, 0x5d, 0x3e, 0x18, 0xaf, 0xc5, 0x09, 0x70, 0xb2, 0x0d, - 0xfa, 0x39, 0x58, 0x88, 0xcc, 0xc4, 0x9a, 0xdd, 0x22, 0xf7, 0xd8, 0x87, 0x1d, 0x0a, 0xf5, 0x25, - 0x76, 0x31, 0x1f, 0xc3, 0xe1, 0x04, 0x35, 0xfa, 0x14, 0xcc, 0x35, 0x1d, 0xcb, 0x62, 0x16, 0x81, - 0x7f, 0x49, 0x65, 0x86, 0x67, 0x27, 0xd8, 0xba, 0x29, 0x18, 0x1c, 0xa3, 0x44, 0xeb, 0x80, 0x9c, - 0x1d, 0x8f, 0xb8, 0xfb, 0xa4, 0xf5, 0x02, 0xff, 0xdc, 0x2b, 0x75, 0x4a, 0xb3, 0x6a, 0x55, 0xd8, - 0xf5, 0x04, 0x05, 0x4e, 0x69, 0x85, 0xbe, 0xac, 0x96, 0x0c, 0xcf, 0x65, 0xf1, 0xdd, 0xb8, 0xf8, - 0x39, 0xf2, 0xd8, 0x7a, 0x61, 0x17, 0x26, 0x79, 0x91, 0x5e, 0x79, 0x3e, 0x8b, 0x97, 0xa6, 0xd1, - 0x8f, 0x36, 0x84, 0x16, 0x95, 0x43, 0xb1, 0x90, 0x84, 0xbe, 0x08, 0xd3, 0x3b, 0xc1, 0x17, 0x76, - 0xca, 0x0b, 0x59, 0x78, 0x91, 0xd8, 0xc7, 0xa2, 0xc2, 0x73, 0x92, 0x44, 0xe0, 0x50, 0x24, 0x7a, - 0x1c, 0x4a, 0x57, 0xb7, 0x6a, 0x52, 0x0b, 0x17, 0xd9, 0xea, 0x4f, 0xd0, 0x26, 0x38, 0x8a, 0xa0, - 0x3b, 0x4c, 0x7a, 0x78, 0xc4, 0x96, 0x38, 0xf4, 0x4e, 0x49, 0x87, 0x4d, 0xa9, 0x59, 0x0e, 0x04, - 0x37, 0xca, 0xa7, 0x62, 0xd4, 0x02, 0x8e, 0x25, 0x05, 0x7a, 0x0d, 0x4a, 0xc2, 0x64, 0x33, 0xdb, - 0xb4, 0xf4, 0x60, 0xe5, 0xe8, 0x38, 0x64, 0x81, 0xa3, 0xfc, 0xd0, 0x33, 0x50, 0xea, 0xb2, 0x0f, - 0x8f, 0x90, 0x2b, 0x3d, 0xcb, 0x2a, 0x9f, 0x66, 0x76, 0x53, 0x5e, 0x51, 0x6f, 0x85, 0x28, 0x1c, - 0xa5, 0xd3, 0xdf, 0x0c, 0xaf, 0xf9, 0xe4, 0xdb, 0xfa, 0x2f, 0x44, 0x57, 0x4b, 0xcb, 0xe2, 0xb3, - 0xb0, 0x89, 0xcf, 0x2b, 0x71, 0x43, 0x9b, 0xba, 0x56, 0x5d, 0xa9, 0x9f, 0x99, 0x3c, 0x4d, 0x54, - 0xbf, 0x1b, 0xc0, 0x4b, 0x81, 0x55, 0xed, 0xd4, 0xdf, 0xcf, 0xcb, 0xa3, 0x7e, 0x2c, 0xaf, 0xe6, - 0x42, 0xc1, 0xf4, 0x7c, 0xd3, 0xc9, 0xb0, 0x3e, 0x3b, 0xf6, 0xe0, 0x9e, 0xd5, 0x09, 0x31, 0x04, - 0xe6, 0xa2, 0xa8, 0x4c, 0xbb, 0x6d, 0xda, 0xf7, 0xc4, 0xf0, 0x6f, 0x64, 0x9e, 0x30, 0xe3, 0x32, - 0x19, 0x02, 0x73, 0x51, 0xe8, 0x36, 0xe4, 0x0d, 0x6b, 0x27, 0xa3, 0x4f, 0x00, 0xc7, 0x3f, 0x80, - 0xcd, 0xb3, 0xec, 0xb5, 0x8d, 0x3a, 0xa6, 0x42, 0xa8, 0x2c, 0xaf, 0x63, 0x0a, 0xdf, 0x3c, 0xa6, - 0xac, 0xc6, 0xe6, 0x5a, 0x9a, 0xac, 0xc6, 0xe6, 0x1a, 0xa6, 0x42, 0xf4, 0x77, 0x35, 0x58, 0x4c, - 0xd0, 0xc4, 0x3f, 0x97, 0xad, 0x0d, 0xff, 0xb9, 0x6c, 0xf1, 0x25, 0x84, 0x46, 0xd7, 0x32, 0x53, - 0x9f, 0x16, 0x6c, 0xc7, 0xf0, 0x38, 0xd1, 0x42, 0xff, 0x96, 0x06, 0xa5, 0x48, 0x59, 0x28, 0x0d, - 0xd5, 0x58, 0xf9, 0xac, 0xe8, 0x46, 0xf8, 0x11, 0x08, 0x76, 0xa9, 0xc0, 0x71, 0xfc, 0x7e, 0xab, - 0x1d, 0xde, 0xf2, 0x44, 0xee, 0xb7, 0x28, 0x14, 0x0b, 0x2c, 0x3d, 0x8d, 0x7b, 0x3e, 0xe9, 0xb2, - 0x85, 0x8c, 0x54, 0x89, 0xb2, 0x5b, 0x5e, 0x86, 0x61, 0xe2, 0xa8, 0xcd, 0x10, 0x29, 0xff, 0xc8, - 0x37, 0x27, 0x0c, 0x1a, 0x19, 0x32, 0x1c, 0x3a, 0x0f, 0x79, 0x62, 0xb7, 0x44, 0x80, 0x53, 0x12, - 0x24, 0xf9, 0xcb, 0x76, 0x0b, 0x53, 0xb8, 0x7e, 0x1d, 0x66, 0x1a, 0xa4, 0xe9, 0x12, 0xff, 0x45, - 0x72, 0x30, 0xdc, 0x0d, 0xcc, 0x79, 0x9e, 0xb9, 0xca, 0xa9, 0x0c, 0x69, 0x73, 0x0a, 0xd7, 0xff, - 0x40, 0x83, 0xd8, 0x27, 0x40, 0x90, 0x1e, 0x4b, 0x46, 0x41, 0x32, 0x11, 0xa5, 0x9c, 0xdc, 0x72, - 0x47, 0x9e, 0xdc, 0xd6, 0x01, 0x75, 0x0c, 0xbf, 0xb9, 0x27, 0xd6, 0x47, 0x7c, 0x6d, 0x86, 0xc7, - 0x96, 0x61, 0x11, 0x7a, 0x82, 0x02, 0xa7, 0xb4, 0xd2, 0xbf, 0x93, 0x83, 0x19, 0xe5, 0xcb, 0xab, - 0xc7, 0x0f, 0x7f, 0xf8, 0x8e, 0xa6, 0x1c, 0xd8, 0xf2, 0x23, 0x1e, 0xd8, 0xa2, 0xa7, 0xd4, 0x89, - 0x93, 0x3d, 0xa5, 0x16, 0x32, 0x39, 0xa5, 0xea, 0xdf, 0x9e, 0x80, 0x39, 0xf5, 0x3d, 0xd7, 0x10, - 0x73, 0xfa, 0xf1, 0xc4, 0x9c, 0x8e, 0x18, 0x0c, 0xe7, 0xc7, 0x0d, 0x86, 0x27, 0xc6, 0x0d, 0x86, - 0x0b, 0x0f, 0x10, 0x0c, 0x27, 0x43, 0xd9, 0xc9, 0xa1, 0x43, 0xd9, 0x4f, 0xcb, 0xbc, 0xc2, 0x94, - 0x72, 0x11, 0x17, 0xe6, 0x15, 0x90, 0xba, 0x0c, 0xab, 0x4e, 0x2b, 0x35, 0x3f, 0x53, 0x3c, 0xa6, - 0x5c, 0xc8, 0x4d, 0x4d, 0x03, 0x8c, 0x7e, 0xe4, 0xfd, 0xd0, 0xf0, 0x29, 0x00, 0xfd, 0x8d, 0x1c, - 0x84, 0x1f, 0x53, 0x65, 0x5f, 0x55, 0xf1, 0x22, 0x36, 0x4a, 0x38, 0xf0, 0xf5, 0x71, 0x3f, 0x5d, - 0x14, 0x72, 0x14, 0x79, 0xb4, 0x08, 0x04, 0x2b, 0x12, 0xff, 0x1b, 0x3e, 0xa2, 0x6a, 0xc0, 0x7c, - 0xac, 0x90, 0x2f, 0xf3, 0xbc, 0xfc, 0xb7, 0x72, 0x30, 0x2d, 0x4b, 0x21, 0xa9, 0x59, 0xef, 0xb9, - 0xc1, 0x47, 0x31, 0xa4, 0x59, 0xbf, 0x89, 0x37, 0x30, 0x85, 0xa3, 0x7b, 0x30, 0xb5, 0x47, 0x8c, - 0x16, 0x71, 0x83, 0x7b, 0x85, 0xcd, 0x8c, 0x6a, 0x30, 0xaf, 0x32, 0xae, 0xe1, 0x58, 0xf8, 0x6f, - 0x0f, 0x07, 0xe2, 0xe8, 0x61, 0xdd, 0x37, 0x3b, 0x84, 0x06, 0xb5, 0x11, 0x2b, 0x9a, 0x0f, 0x0f, - 0xeb, 0xdb, 0x0a, 0x16, 0xc7, 0xa8, 0xa9, 0x71, 0xb9, 0xed, 0x39, 0x36, 0x7b, 0xb0, 0x38, 0xa1, - 0x46, 0xf6, 0xeb, 0x8d, 0xeb, 0xd7, 0xd8, 0x7b, 0x45, 0x49, 0x41, 0xa9, 0x4d, 0x56, 0x0a, 0xe6, - 0x12, 0x71, 0xd3, 0xbe, 0x10, 0x16, 0xae, 0x73, 0x38, 0x96, 0x14, 0xfa, 0x4d, 0x98, 0x8f, 0x0d, - 0x24, 0x70, 0x8f, 0x5a, 0xba, 0x7b, 0x1c, 0xea, 0x7f, 0x39, 0xd4, 0xab, 0xef, 0x7d, 0xb0, 0xfc, - 0xc8, 0x77, 0x3f, 0x58, 0x7e, 0xe4, 0x7b, 0x1f, 0x2c, 0x3f, 0xf2, 0xc6, 0x60, 0x59, 0x7b, 0x6f, - 0xb0, 0xac, 0x7d, 0x77, 0xb0, 0xac, 0x7d, 0x6f, 0xb0, 0xac, 0xfd, 0xe3, 0x60, 0x59, 0x7b, 0xf7, - 0x07, 0xcb, 0x8f, 0xbc, 0x5c, 0x0c, 0x26, 0xf3, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x85, 0x55, - 0x6d, 0xa5, 0x84, 0x66, 0x00, 0x00, + // 5469 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x1c, 0xd7, + 0x75, 0xb0, 0x67, 0x97, 0x4b, 0x2e, 0xcf, 0xf2, 0xf7, 0x8a, 0x8a, 0x36, 0xb2, 0xc4, 0x55, 0xc6, + 0x81, 0x3f, 0xe7, 0x6b, 0xb2, 0x8c, 0x65, 0xbb, 0x75, 0xe3, 0xc0, 0xe8, 0x2e, 0x25, 0x59, 0xa4, + 0x49, 0x89, 0xba, 0x4b, 0x59, 0x88, 0x7f, 0xda, 0x0c, 0x77, 0x2f, 0x97, 0x23, 0xcd, 0xce, 0x6c, + 0x66, 0x66, 0x29, 0xd1, 0x09, 0x12, 0x3b, 0x81, 0x1b, 0xb7, 0x48, 0x60, 0xf7, 0xe7, 0xa5, 0x28, + 0x5a, 0x14, 0x45, 0x1f, 0x8a, 0xf6, 0x25, 0x0f, 0x79, 0x29, 0xd0, 0xa0, 0x41, 0xda, 0x02, 0xee, + 0x43, 0x9b, 0xf4, 0xa5, 0x4e, 0x0b, 0x64, 0x5b, 0x33, 0x05, 0x8a, 0xf6, 0xa5, 0x48, 0x11, 0xa0, + 0x88, 0x9e, 0x8a, 0xfb, 0x33, 0x77, 0xe6, 0xce, 0xcc, 0x92, 0xbb, 0xda, 0xa1, 0x1a, 0xb4, 0x7d, + 0xe3, 0xde, 0x73, 0xee, 0x39, 0xf7, 0xe7, 0xdc, 0xf3, 0x73, 0xcf, 0xb9, 0x43, 0xd8, 0x68, 0x9b, + 0xfe, 0x5e, 0x6f, 0xa7, 0xda, 0x74, 0x3a, 0x2b, 0x86, 0xdb, 0x76, 0xba, 0xae, 0x73, 0x9b, 0xfd, + 0xf1, 0x09, 0xd7, 0xb1, 0x2c, 0xa7, 0xe7, 0x7b, 0x2b, 0xdd, 0x3b, 0xed, 0x15, 0xa3, 0x6b, 0x7a, + 0x2b, 0xb2, 0x65, 0xff, 0x49, 0xc3, 0xea, 0xee, 0x19, 0x4f, 0xae, 0xb4, 0x89, 0x4d, 0x5c, 0xc3, + 0x27, 0xad, 0x6a, 0xd7, 0x75, 0x7c, 0x07, 0x7d, 0x3a, 0xa4, 0x56, 0x0d, 0xa8, 0xb1, 0x3f, 0x7e, + 0x29, 0xe8, 0x5b, 0xed, 0xde, 0x69, 0x57, 0x29, 0xb5, 0xaa, 0x6c, 0x09, 0xa8, 0x9d, 0xfd, 0x44, + 0x64, 0x2c, 0x6d, 0xa7, 0xed, 0xac, 0x30, 0xa2, 0x3b, 0xbd, 0x5d, 0xf6, 0x8b, 0xfd, 0x60, 0x7f, + 0x71, 0x66, 0x67, 0x1f, 0xbb, 0xf3, 0xac, 0x57, 0x35, 0x1d, 0x3a, 0xb6, 0x95, 0x1d, 0xc3, 0x6f, + 0xee, 0xad, 0xec, 0x27, 0x46, 0x74, 0x56, 0x8f, 0x20, 0x35, 0x1d, 0x97, 0xa4, 0xe1, 0x3c, 0x1d, + 0xe2, 0x74, 0x8c, 0xe6, 0x9e, 0x69, 0x13, 0xf7, 0x20, 0x9c, 0x75, 0x87, 0xf8, 0x46, 0x5a, 0xaf, + 0x95, 0x41, 0xbd, 0xdc, 0x9e, 0xed, 0x9b, 0x1d, 0x92, 0xe8, 0xf0, 0xb3, 0xc7, 0x75, 0xf0, 0x9a, + 0x7b, 0xa4, 0x63, 0x24, 0xfa, 0x3d, 0x35, 0xa8, 0x5f, 0xcf, 0x37, 0xad, 0x15, 0xd3, 0xf6, 0x3d, + 0xdf, 0x8d, 0x77, 0xd2, 0xff, 0x43, 0x83, 0xc5, 0xda, 0x46, 0x7d, 0xdb, 0x35, 0x76, 0x77, 0xcd, + 0x26, 0x76, 0x7a, 0xbe, 0x69, 0xb7, 0xd1, 0xc7, 0x60, 0xca, 0xb4, 0xdb, 0x2e, 0xf1, 0xbc, 0xb2, + 0x76, 0x41, 0x7b, 0x62, 0xba, 0x3e, 0xff, 0x5e, 0xbf, 0xf2, 0xc8, 0x61, 0xbf, 0x32, 0xb5, 0xc6, + 0x9b, 0x71, 0x00, 0x47, 0xcf, 0x40, 0xc9, 0x23, 0xee, 0xbe, 0xd9, 0x24, 0x5b, 0x8e, 0xeb, 0x97, + 0x73, 0x17, 0xb4, 0x27, 0x0a, 0xf5, 0x53, 0x02, 0xbd, 0xd4, 0x08, 0x41, 0x38, 0x8a, 0x47, 0xbb, + 0xb9, 0x8e, 0xe3, 0x0b, 0x78, 0x39, 0xcf, 0xb8, 0xc8, 0x6e, 0x38, 0x04, 0xe1, 0x28, 0x1e, 0xba, + 0x04, 0x0b, 0x86, 0x6d, 0x3b, 0xbe, 0xe1, 0x9b, 0x8e, 0xbd, 0xe5, 0x92, 0x5d, 0xf3, 0x5e, 0x79, + 0x82, 0xf5, 0x2d, 0x8b, 0xbe, 0x0b, 0xb5, 0x18, 0x1c, 0x27, 0x7a, 0xe8, 0x7f, 0x9f, 0x83, 0x52, + 0xcd, 0x36, 0xac, 0x03, 0xcf, 0xf4, 0x70, 0xcf, 0x46, 0x9f, 0x85, 0x22, 0xdd, 0xbd, 0x96, 0xe1, + 0x1b, 0x6c, 0xbe, 0xa5, 0x8b, 0x9f, 0xac, 0xf2, 0xc5, 0xac, 0x46, 0x17, 0x33, 0x94, 0x49, 0x8a, + 0x5d, 0xdd, 0x7f, 0xb2, 0x7a, 0x7d, 0xe7, 0x36, 0x69, 0xfa, 0x9b, 0xc4, 0x37, 0xea, 0x48, 0xf0, + 0x87, 0xb0, 0x0d, 0x4b, 0xaa, 0xc8, 0x81, 0x09, 0xaf, 0x4b, 0x9a, 0x6c, 0x79, 0x4a, 0x17, 0x37, + 0xab, 0xe3, 0xc8, 0x7f, 0x35, 0x32, 0xf4, 0x46, 0x97, 0x34, 0xeb, 0x33, 0x82, 0xf5, 0x04, 0xfd, + 0x85, 0x19, 0x23, 0x74, 0x17, 0x26, 0x3d, 0xdf, 0xf0, 0x7b, 0x1e, 0x5b, 0xda, 0xd2, 0xc5, 0xeb, + 0xd9, 0xb1, 0x64, 0x64, 0xeb, 0x73, 0x82, 0xe9, 0x24, 0xff, 0x8d, 0x05, 0x3b, 0xfd, 0x1f, 0x34, + 0x38, 0x15, 0xc1, 0xae, 0xb9, 0xed, 0x5e, 0x87, 0xd8, 0x3e, 0xba, 0x00, 0x13, 0xb6, 0xd1, 0x21, + 0x42, 0x9e, 0xe4, 0x90, 0xaf, 0x19, 0x1d, 0x82, 0x19, 0x04, 0x3d, 0x06, 0x85, 0x7d, 0xc3, 0xea, + 0x11, 0xb6, 0x48, 0xd3, 0xf5, 0x59, 0x81, 0x52, 0x78, 0x89, 0x36, 0x62, 0x0e, 0x43, 0x5f, 0x80, + 0x69, 0xf6, 0xc7, 0x15, 0xd7, 0xe9, 0x64, 0x34, 0x35, 0x31, 0xc2, 0x97, 0x02, 0xb2, 0xf5, 0xd9, + 0xc3, 0x7e, 0x65, 0x5a, 0xfe, 0xc4, 0x21, 0x43, 0xfd, 0x1f, 0x35, 0x98, 0x8f, 0x4c, 0x6e, 0xc3, + 0xf4, 0x7c, 0xf4, 0x6a, 0x42, 0x78, 0xaa, 0xc3, 0x09, 0x0f, 0xed, 0xcd, 0x44, 0x67, 0x41, 0xcc, + 0xb4, 0x18, 0xb4, 0x44, 0x04, 0xc7, 0x86, 0x82, 0xe9, 0x93, 0x8e, 0x57, 0xce, 0x5d, 0xc8, 0x3f, + 0x51, 0xba, 0xb8, 0x96, 0xd9, 0x36, 0x86, 0xeb, 0xbb, 0x46, 0xe9, 0x63, 0xce, 0x46, 0xff, 0x9d, + 0x9c, 0x32, 0x43, 0x2a, 0x51, 0xc8, 0x81, 0xa9, 0x0e, 0xf1, 0x5d, 0xb3, 0x49, 0xb5, 0x01, 0x1d, + 0xc5, 0xa5, 0xf1, 0x46, 0xb1, 0xc9, 0x88, 0x85, 0x3a, 0x85, 0xff, 0xf6, 0x70, 0xc0, 0x05, 0xed, + 0xc1, 0x84, 0xe1, 0xb6, 0x83, 0x39, 0x5f, 0xc9, 0x66, 0x7f, 0x43, 0x99, 0xab, 0xb9, 0x6d, 0x0f, + 0x33, 0x0e, 0x68, 0x05, 0xa6, 0x7d, 0xe2, 0x76, 0x4c, 0xdb, 0xf0, 0xb9, 0x12, 0x2a, 0xd6, 0x17, + 0x05, 0xda, 0xf4, 0x76, 0x00, 0xc0, 0x21, 0x8e, 0xfe, 0x7e, 0x0e, 0x16, 0x13, 0x87, 0x01, 0x3d, + 0x0d, 0x85, 0xee, 0x9e, 0xe1, 0x05, 0xd2, 0xbd, 0x1c, 0x2c, 0xed, 0x16, 0x6d, 0xbc, 0xdf, 0xaf, + 0xcc, 0x06, 0x5d, 0x58, 0x03, 0xe6, 0xc8, 0x54, 0xcb, 0x76, 0x88, 0xe7, 0x19, 0xed, 0x40, 0xe4, + 0x23, 0x2b, 0xc2, 0x9a, 0x71, 0x00, 0x47, 0x5f, 0xd5, 0x60, 0x96, 0xaf, 0x0e, 0x26, 0x5e, 0xcf, + 0xf2, 0xe9, 0xb1, 0xa6, 0x6b, 0xb3, 0x9e, 0xc5, 0x4e, 0x70, 0x92, 0xf5, 0xd3, 0x82, 0xfb, 0x6c, + 0xb4, 0xd5, 0xc3, 0x2a, 0x5f, 0x74, 0x0b, 0xa6, 0x3d, 0xdf, 0x70, 0x7d, 0xd2, 0xaa, 0xf9, 0x4c, + 0xf5, 0x96, 0x2e, 0xfe, 0xff, 0xe1, 0xe4, 0x7d, 0xdb, 0xec, 0x10, 0x7e, 0xb6, 0x1a, 0x01, 0x01, + 0x1c, 0xd2, 0xd2, 0xff, 0x4d, 0x83, 0x85, 0x60, 0x99, 0xb6, 0x49, 0xa7, 0x6b, 0x19, 0x3e, 0x79, + 0x08, 0x9a, 0xd9, 0x57, 0x34, 0x33, 0xce, 0xe6, 0x7c, 0x05, 0xe3, 0x1f, 0xa4, 0x9e, 0xf5, 0x7f, + 0xd5, 0x60, 0x29, 0x8e, 0xfc, 0x10, 0xb4, 0x89, 0xa7, 0x6a, 0x93, 0x6b, 0xd9, 0xce, 0x76, 0x80, + 0x4a, 0xf9, 0x51, 0xca, 0x5c, 0xff, 0x87, 0xeb, 0x15, 0xfd, 0x0f, 0x27, 0x60, 0xa6, 0x66, 0xfb, + 0x66, 0x6d, 0x77, 0xd7, 0xb4, 0x4d, 0xff, 0x00, 0x7d, 0x2d, 0x07, 0x2b, 0x5d, 0x97, 0xec, 0x12, + 0xd7, 0x25, 0xad, 0x4b, 0x3d, 0xd7, 0xb4, 0xdb, 0x8d, 0xe6, 0x1e, 0x69, 0xf5, 0x2c, 0xd3, 0x6e, + 0xaf, 0xb5, 0x6d, 0x47, 0x36, 0x5f, 0xbe, 0x47, 0x9a, 0x3d, 0xea, 0xac, 0x88, 0xfd, 0xef, 0x8c, + 0x37, 0xcc, 0xad, 0xd1, 0x98, 0xd6, 0x9f, 0x3a, 0xec, 0x57, 0x56, 0x46, 0xec, 0x84, 0x47, 0x9d, + 0x1a, 0x7a, 0x3b, 0x07, 0x55, 0x97, 0x7c, 0xae, 0x67, 0x0e, 0xbf, 0x1a, 0xfc, 0x80, 0x5a, 0xe3, + 0xad, 0x06, 0x1e, 0x89, 0x67, 0xfd, 0xe2, 0x61, 0xbf, 0x32, 0x62, 0x1f, 0x3c, 0xe2, 0xbc, 0xf4, + 0x3f, 0xd7, 0xa0, 0x38, 0x82, 0x97, 0x54, 0x51, 0xbd, 0xa4, 0xe9, 0x84, 0x87, 0xe4, 0x27, 0x3d, + 0xa4, 0x17, 0xc6, 0x5b, 0xb4, 0x61, 0x3c, 0xa3, 0x7f, 0xa7, 0x71, 0x44, 0xdc, 0x93, 0x42, 0x7b, + 0xb0, 0xd4, 0x75, 0x5a, 0xc1, 0xa1, 0xbf, 0x6a, 0x78, 0x7b, 0x0c, 0x26, 0xa6, 0xf7, 0xf4, 0x61, + 0xbf, 0xb2, 0xb4, 0x95, 0x02, 0xbf, 0xdf, 0xaf, 0x94, 0x25, 0x91, 0x18, 0x02, 0x4e, 0xa5, 0x88, + 0xba, 0x50, 0xdc, 0x35, 0x89, 0xd5, 0xc2, 0x64, 0x57, 0x48, 0xca, 0x98, 0xc7, 0xfb, 0x8a, 0xa0, + 0x56, 0x9f, 0xa1, 0xba, 0x34, 0xf8, 0x85, 0x25, 0x17, 0xfd, 0x27, 0x13, 0x30, 0x5f, 0xb7, 0x7a, + 0xe4, 0x05, 0x97, 0x90, 0xc0, 0x0f, 0xa8, 0xc1, 0x7c, 0xd7, 0x25, 0xfb, 0x26, 0xb9, 0xdb, 0x20, + 0x16, 0x69, 0xfa, 0x8e, 0x2b, 0xa6, 0x7a, 0x46, 0xec, 0xe4, 0xfc, 0x96, 0x0a, 0xc6, 0x71, 0x7c, + 0xf4, 0x3c, 0xcc, 0x19, 0x4d, 0xdf, 0xdc, 0x27, 0x92, 0x02, 0xdf, 0xe8, 0x0f, 0x09, 0x0a, 0x73, + 0x35, 0x05, 0x8a, 0x63, 0xd8, 0xe8, 0x55, 0x28, 0x7b, 0x4d, 0xc3, 0x22, 0x37, 0xbb, 0x82, 0xd5, + 0xea, 0x1e, 0x69, 0xde, 0xd9, 0x72, 0x4c, 0xdb, 0x17, 0x0e, 0xce, 0x05, 0x41, 0xa9, 0xdc, 0x18, + 0x80, 0x87, 0x07, 0x52, 0x40, 0x7f, 0xa6, 0xc1, 0xf9, 0xae, 0x4b, 0xb6, 0x5c, 0xa7, 0xe3, 0x50, + 0xe9, 0x4d, 0xb8, 0x42, 0xc2, 0x25, 0x78, 0x69, 0xcc, 0x63, 0xca, 0x5b, 0x92, 0x51, 0xc7, 0x47, + 0x0e, 0xfb, 0x95, 0xf3, 0x5b, 0x47, 0x0d, 0x00, 0x1f, 0x3d, 0x3e, 0xf4, 0x1d, 0x0d, 0x96, 0xbb, + 0x8e, 0xe7, 0x1f, 0x31, 0x85, 0xc2, 0x89, 0x4e, 0x41, 0x3f, 0xec, 0x57, 0x96, 0xb7, 0x8e, 0x1c, + 0x01, 0x3e, 0x66, 0x84, 0xfa, 0x97, 0x4b, 0xb0, 0x18, 0x91, 0x3d, 0x1a, 0xd0, 0xb7, 0x0f, 0xd0, + 0x73, 0x30, 0x1b, 0x08, 0x03, 0x8f, 0xaa, 0xb9, 0xec, 0x49, 0xbf, 0xae, 0x16, 0x05, 0x62, 0x15, + 0x97, 0xca, 0x9d, 0x14, 0x45, 0xde, 0x3b, 0x26, 0x77, 0x5b, 0x0a, 0x14, 0xc7, 0xb0, 0xd1, 0x1a, + 0x9c, 0x12, 0x2d, 0x98, 0x74, 0x2d, 0xb3, 0x69, 0xac, 0x3a, 0x3d, 0x21, 0x72, 0x85, 0xfa, 0x99, + 0xc3, 0x7e, 0xe5, 0xd4, 0x56, 0x12, 0x8c, 0xd3, 0xfa, 0xa0, 0x0d, 0x58, 0x32, 0x7a, 0xbe, 0x23, + 0xe7, 0x7f, 0xd9, 0x36, 0x76, 0x2c, 0xd2, 0x62, 0xa2, 0x55, 0xac, 0x97, 0xa9, 0xd6, 0xa8, 0xa5, + 0xc0, 0x71, 0x6a, 0x2f, 0xb4, 0x15, 0xa3, 0xd6, 0x20, 0x4d, 0xc7, 0x6e, 0xf1, 0x5d, 0x2e, 0xd4, + 0xcf, 0x89, 0xe9, 0xa9, 0x14, 0x05, 0x0e, 0x4e, 0xed, 0x89, 0x2c, 0x98, 0xeb, 0x18, 0xf7, 0x6e, + 0xda, 0xc6, 0xbe, 0x61, 0x5a, 0x94, 0x49, 0x79, 0xf2, 0x18, 0xd7, 0xb4, 0xe7, 0x9b, 0x56, 0x95, + 0xdf, 0xc0, 0x54, 0xd7, 0x6c, 0xff, 0xba, 0xdb, 0xf0, 0xa9, 0x11, 0xa8, 0x23, 0xba, 0xb0, 0x9b, + 0x0a, 0x2d, 0x1c, 0xa3, 0x8d, 0xae, 0xc3, 0x69, 0x76, 0x1c, 0x2f, 0x39, 0x77, 0xed, 0x4b, 0xc4, + 0x32, 0x0e, 0x82, 0x09, 0x4c, 0xb1, 0x09, 0x7c, 0xf8, 0xb0, 0x5f, 0x39, 0xdd, 0x48, 0x43, 0xc0, + 0xe9, 0xfd, 0x90, 0x01, 0x8f, 0xaa, 0x00, 0x4c, 0xf6, 0x4d, 0xcf, 0x74, 0xec, 0x0d, 0xb3, 0x63, + 0xfa, 0xe5, 0x22, 0x23, 0x5b, 0x39, 0xec, 0x57, 0x1e, 0x6d, 0x0c, 0x46, 0xc3, 0x47, 0xd1, 0x40, + 0xbf, 0xad, 0xc1, 0x52, 0xda, 0x31, 0x2c, 0x4f, 0x67, 0x71, 0xff, 0x11, 0x3b, 0x5a, 0x5c, 0x22, + 0x52, 0x95, 0x42, 0xea, 0x20, 0xd0, 0x1b, 0x1a, 0xcc, 0x18, 0x11, 0xe7, 0xac, 0x0c, 0x6c, 0x54, + 0xeb, 0xe3, 0x7a, 0xc3, 0x21, 0xc5, 0xfa, 0xc2, 0x61, 0xbf, 0xa2, 0x38, 0x80, 0x58, 0xe1, 0x88, + 0x7e, 0x57, 0x83, 0xd3, 0xa9, 0x67, 0xbc, 0x5c, 0x3a, 0x89, 0x15, 0x62, 0x42, 0x92, 0xae, 0x73, + 0xd2, 0x87, 0x81, 0xde, 0xd5, 0xa4, 0x29, 0xdb, 0x0c, 0xe2, 0x91, 0x19, 0x36, 0xb4, 0x1b, 0x63, + 0xfa, 0xa3, 0xa1, 0xf5, 0x0e, 0x08, 0xd7, 0x4f, 0x45, 0x2c, 0x63, 0xd0, 0x88, 0xe3, 0xec, 0xd1, + 0xd7, 0xb5, 0xc0, 0x34, 0xca, 0x11, 0xcd, 0x9e, 0xd4, 0x88, 0x50, 0x68, 0x69, 0xe5, 0x80, 0x62, + 0xcc, 0xf5, 0x7f, 0xc9, 0xc3, 0xcc, 0xaa, 0x61, 0x1b, 0xee, 0x81, 0x30, 0x2d, 0x7f, 0xaa, 0xc1, + 0xb9, 0x66, 0xcf, 0x75, 0x89, 0xed, 0x37, 0x7c, 0xd2, 0x4d, 0x1a, 0x16, 0xed, 0x44, 0x0d, 0xcb, + 0x85, 0xc3, 0x7e, 0xe5, 0xdc, 0xea, 0x11, 0xfc, 0xf1, 0x91, 0xa3, 0x43, 0x7f, 0xa3, 0x81, 0x2e, + 0x10, 0xea, 0x46, 0xf3, 0x4e, 0xdb, 0x75, 0x7a, 0x76, 0x2b, 0x39, 0x89, 0xdc, 0x89, 0x4e, 0xe2, + 0xf1, 0xc3, 0x7e, 0x45, 0x5f, 0x3d, 0x76, 0x14, 0x78, 0x88, 0x91, 0xa2, 0x17, 0x60, 0x51, 0x60, + 0x5d, 0xbe, 0xd7, 0x25, 0xae, 0x49, 0x7d, 0x53, 0x71, 0xd3, 0xfc, 0x61, 0xa1, 0xf6, 0x17, 0x57, + 0xe3, 0x08, 0x38, 0xd9, 0x47, 0xff, 0xc6, 0x04, 0x40, 0xb0, 0xd3, 0xa4, 0x8b, 0x7e, 0x06, 0xa6, + 0x3d, 0xe2, 0xdf, 0x22, 0x66, 0x7b, 0xcf, 0x67, 0x7b, 0x5a, 0x10, 0xd7, 0x1a, 0x41, 0x23, 0x0e, + 0xe1, 0xe8, 0x0e, 0x14, 0xba, 0x46, 0xcf, 0x23, 0x62, 0xdd, 0xd6, 0x33, 0x59, 0xb7, 0x2d, 0x4a, + 0x91, 0xfb, 0xfe, 0xec, 0x4f, 0xcc, 0x79, 0xa0, 0xaf, 0x68, 0x00, 0x44, 0x9d, 0x6b, 0xe9, 0x62, + 0x23, 0x13, 0x96, 0xe1, 0x72, 0xd0, 0x35, 0xa8, 0xcf, 0x1d, 0xf6, 0x2b, 0x10, 0x59, 0xb5, 0x08, + 0x5b, 0x74, 0x17, 0x8a, 0x46, 0xa0, 0xce, 0x26, 0x4e, 0x42, 0x9d, 0x31, 0x97, 0x5c, 0xee, 0xb7, + 0x64, 0x86, 0xde, 0xd6, 0x60, 0xce, 0x23, 0xbe, 0xd8, 0x2a, 0x6a, 0x9f, 0x84, 0x2f, 0xb7, 0x31, + 0x1e, 0xff, 0x86, 0x42, 0x93, 0x2b, 0x07, 0xb5, 0x0d, 0xc7, 0xf8, 0xea, 0xdf, 0x00, 0x98, 0x0b, + 0x44, 0x26, 0x74, 0xcf, 0x9a, 0xbc, 0x25, 0xdd, 0x3d, 0x5b, 0x8d, 0x02, 0xb1, 0x8a, 0x4b, 0x3b, + 0x7b, 0x3e, 0xf5, 0x07, 0x54, 0xef, 0x4c, 0x76, 0x6e, 0x44, 0x81, 0x58, 0xc5, 0x45, 0x1d, 0x28, + 0x78, 0x3e, 0xe9, 0x06, 0x97, 0x86, 0x57, 0xc7, 0x5b, 0x8d, 0xf0, 0x24, 0x84, 0x17, 0x3e, 0xf4, + 0x97, 0x87, 0x39, 0x17, 0xf4, 0x8e, 0x06, 0x73, 0xbe, 0x92, 0x50, 0x12, 0x62, 0x90, 0x8d, 0x24, + 0xaa, 0xb9, 0x2a, 0xbe, 0x1b, 0x6a, 0x1b, 0x8e, 0xb1, 0x4f, 0xf1, 0xd8, 0x0a, 0x27, 0xe8, 0xb1, + 0xbd, 0x0c, 0xc5, 0x8e, 0x71, 0xaf, 0xd1, 0x73, 0xdb, 0x0f, 0xee, 0x19, 0x32, 0x11, 0xdf, 0x14, + 0x54, 0xb0, 0xa4, 0x87, 0xde, 0xd4, 0x22, 0x87, 0x6b, 0x8a, 0x11, 0xbf, 0x95, 0xed, 0xe1, 0x92, + 0x0a, 0x75, 0xe0, 0x31, 0x4b, 0xf8, 0x4f, 0xc5, 0x87, 0xee, 0x3f, 0x51, 0x5f, 0x80, 0x1f, 0x10, + 0xe9, 0x0b, 0x4c, 0x9f, 0xa8, 0x2f, 0xb0, 0xaa, 0x30, 0xc3, 0x31, 0xe6, 0x6c, 0x3c, 0xfc, 0xcc, + 0xc9, 0xf1, 0xc0, 0x89, 0x8e, 0xa7, 0xa1, 0x30, 0xc3, 0x31, 0xe6, 0x83, 0x83, 0x86, 0xd2, 0xc9, + 0x04, 0x0d, 0x33, 0xe3, 0x07, 0x0d, 0xfa, 0x8f, 0x34, 0x38, 0xb3, 0x6a, 0xf5, 0x3c, 0x9f, 0xb8, + 0xff, 0x6b, 0xf2, 0x00, 0xff, 0xa9, 0xc1, 0xa3, 0x03, 0xe6, 0xfc, 0x10, 0xd2, 0x01, 0xaf, 0xab, + 0xe9, 0x80, 0x9b, 0x63, 0xda, 0x85, 0xf4, 0x79, 0x0c, 0xc8, 0x0a, 0xf8, 0x30, 0x7b, 0xc9, 0xf0, + 0x8d, 0x96, 0xd3, 0xe6, 0xd7, 0xf4, 0xe8, 0x79, 0x28, 0x9a, 0xb6, 0x4f, 0xdc, 0x7d, 0xc3, 0x12, + 0x96, 0x51, 0x0f, 0x86, 0xbe, 0x26, 0xda, 0xef, 0xf7, 0x2b, 0x73, 0x97, 0x7a, 0x2e, 0x4b, 0xe5, + 0x73, 0x3d, 0x89, 0x65, 0x1f, 0xf4, 0x18, 0x14, 0x3e, 0xd7, 0x23, 0xee, 0x41, 0x3c, 0x7d, 0x7c, + 0x83, 0x36, 0x62, 0x0e, 0xd3, 0xff, 0x2e, 0x07, 0x11, 0xaf, 0xe5, 0x21, 0x88, 0x95, 0xad, 0x88, + 0xd5, 0x98, 0x7e, 0x48, 0xc4, 0x07, 0x1b, 0x94, 0xf7, 0xdf, 0x8f, 0xe5, 0xfd, 0xaf, 0x65, 0xc6, + 0xf1, 0xe8, 0xb4, 0xff, 0xfb, 0x1a, 0x3c, 0x1a, 0x22, 0x27, 0x7d, 0xf1, 0xe3, 0x2f, 0xb6, 0x9f, + 0x81, 0x92, 0x11, 0x76, 0x13, 0xbb, 0x28, 0x2b, 0x42, 0x22, 0x14, 0x71, 0x14, 0x2f, 0x4c, 0xbd, + 0xe6, 0x1f, 0x30, 0xf5, 0x3a, 0x71, 0x74, 0xea, 0x55, 0xff, 0x71, 0x0e, 0xce, 0x27, 0x67, 0x16, + 0x48, 0x37, 0x26, 0xbb, 0x43, 0xcc, 0xed, 0x59, 0x98, 0xf1, 0x45, 0x07, 0xda, 0x2a, 0x26, 0xb7, + 0x24, 0x30, 0x67, 0xb6, 0x23, 0x30, 0xac, 0x60, 0xd2, 0x9e, 0x4d, 0x7e, 0xae, 0x1a, 0x4d, 0xa7, + 0x1b, 0xe4, 0xa8, 0x65, 0xcf, 0xd5, 0x08, 0x0c, 0x2b, 0x98, 0x32, 0xd9, 0x35, 0x71, 0xe2, 0x49, + 0xf4, 0x06, 0x9c, 0x0e, 0x72, 0x1e, 0x57, 0x1c, 0x77, 0xd5, 0xe9, 0x74, 0x2d, 0xc2, 0x52, 0x36, + 0x05, 0x36, 0xd8, 0xf3, 0xa2, 0xcb, 0x69, 0x9c, 0x86, 0x84, 0xd3, 0xfb, 0xea, 0xef, 0xe7, 0xe1, + 0x54, 0xb8, 0xec, 0xab, 0x8e, 0xdd, 0x32, 0x59, 0xe6, 0xe8, 0x39, 0x98, 0xf0, 0x0f, 0xba, 0xc1, + 0x62, 0xff, 0xbf, 0x60, 0x38, 0xdb, 0x07, 0x5d, 0xba, 0xdb, 0x67, 0x52, 0xba, 0x50, 0x10, 0x66, + 0x9d, 0xd0, 0x86, 0x3c, 0x1d, 0x7c, 0x07, 0x9e, 0x56, 0xa5, 0xf9, 0x7e, 0xbf, 0x92, 0x52, 0x07, + 0x56, 0x95, 0x94, 0x54, 0x99, 0x47, 0xb7, 0x61, 0xce, 0x32, 0x3c, 0xff, 0x66, 0xb7, 0x65, 0xf8, + 0x64, 0xdb, 0xec, 0x10, 0x71, 0xe6, 0x46, 0xc9, 0x87, 0xcb, 0xeb, 0xd5, 0x0d, 0x85, 0x12, 0x8e, + 0x51, 0x46, 0xfb, 0x80, 0x68, 0xcb, 0xb6, 0x6b, 0xd8, 0x1e, 0x9f, 0x15, 0xe5, 0x37, 0x7a, 0xfe, + 0xfd, 0xac, 0xe0, 0x87, 0x36, 0x12, 0xd4, 0x70, 0x0a, 0x07, 0xf4, 0x38, 0x4c, 0xba, 0xc4, 0xf0, + 0xc4, 0x66, 0x4e, 0x87, 0xe7, 0x1f, 0xb3, 0x56, 0x2c, 0xa0, 0xd1, 0x03, 0x35, 0x79, 0xcc, 0x81, + 0xfa, 0x81, 0x06, 0x73, 0xe1, 0x36, 0x3d, 0x04, 0x33, 0xd7, 0x51, 0xcd, 0xdc, 0xd5, 0xac, 0x54, + 0xe2, 0x00, 0xcb, 0xf6, 0x41, 0x3e, 0x3a, 0x3f, 0x96, 0xe9, 0xfe, 0x3c, 0x4c, 0x07, 0xa7, 0x3a, + 0xc8, 0x75, 0x8f, 0xe9, 0x2d, 0x2b, 0x9e, 0x45, 0xa4, 0x64, 0x45, 0x30, 0xc1, 0x21, 0x3f, 0x6a, + 0x58, 0x5b, 0xc2, 0x68, 0x0a, 0xb1, 0x97, 0x86, 0x35, 0x30, 0xa6, 0x69, 0x86, 0x35, 0xe8, 0x83, + 0x6e, 0xc2, 0x99, 0xae, 0xeb, 0xb0, 0x6a, 0xbf, 0x4b, 0xc4, 0x68, 0x59, 0xa6, 0x4d, 0x02, 0x6f, + 0x92, 0xdf, 0xee, 0x3f, 0x7a, 0xd8, 0xaf, 0x9c, 0xd9, 0x4a, 0x47, 0xc1, 0x83, 0xfa, 0xaa, 0xa5, + 0x37, 0x13, 0xc7, 0x97, 0xde, 0xa0, 0x5f, 0x91, 0xa1, 0x0f, 0xf1, 0xca, 0x05, 0xb6, 0x88, 0xaf, + 0x64, 0xb5, 0x95, 0x29, 0x6a, 0x3d, 0x14, 0xa9, 0x9a, 0x60, 0x8a, 0x25, 0x7b, 0xfd, 0xad, 0x02, + 0x2c, 0xc4, 0x6d, 0xe3, 0xc9, 0x57, 0x01, 0xfd, 0xba, 0x06, 0x0b, 0xc1, 0xbe, 0x72, 0x9e, 0x24, + 0x88, 0xe9, 0x37, 0x32, 0x12, 0x27, 0x6e, 0xe5, 0x65, 0x31, 0xe5, 0x76, 0x8c, 0x1b, 0x4e, 0xf0, + 0x47, 0xaf, 0x41, 0x49, 0x86, 0xbe, 0x0f, 0x54, 0x12, 0x34, 0xcf, 0xec, 0x7b, 0x48, 0x02, 0x47, + 0xe9, 0xa1, 0xb7, 0x34, 0x80, 0x66, 0xa0, 0x80, 0x83, 0x7d, 0xbf, 0x91, 0xd5, 0xbe, 0x4b, 0xd5, + 0x1e, 0xba, 0x71, 0xb2, 0xc9, 0xc3, 0x11, 0xc6, 0xe8, 0x37, 0x58, 0xd0, 0x2b, 0xfd, 0x0e, 0xaf, + 0x3c, 0xc9, 0x46, 0xf2, 0x99, 0xac, 0x25, 0x30, 0xbc, 0x0a, 0x95, 0x46, 0x3e, 0x02, 0xf2, 0xb0, + 0x32, 0x08, 0xfd, 0x39, 0x90, 0xa9, 0x69, 0x7a, 0xa0, 0x58, 0x72, 0x7a, 0xcb, 0xf0, 0xf7, 0x84, + 0x08, 0xca, 0x03, 0x75, 0x25, 0x00, 0xe0, 0x10, 0x47, 0xff, 0x0b, 0x0d, 0x96, 0xd6, 0x3c, 0xdf, + 0x74, 0x2e, 0x11, 0xcf, 0xa7, 0x67, 0x8c, 0x9a, 0xe3, 0x9e, 0x45, 0x86, 0x70, 0x68, 0x2e, 0xc1, + 0x82, 0xb8, 0x9f, 0xea, 0xed, 0x78, 0xc4, 0x8f, 0x38, 0x35, 0x52, 0x74, 0x56, 0x63, 0x70, 0x9c, + 0xe8, 0x41, 0xa9, 0x88, 0x8b, 0xaa, 0x90, 0x4a, 0x5e, 0xa5, 0xd2, 0x88, 0xc1, 0x71, 0xa2, 0x87, + 0xfe, 0xad, 0x1c, 0x9c, 0x62, 0xd3, 0x88, 0x15, 0x31, 0xff, 0x9a, 0x06, 0x73, 0xfb, 0xa6, 0xeb, + 0xf7, 0x0c, 0x2b, 0x7a, 0xe3, 0x36, 0xb6, 0xf4, 0x30, 0x5e, 0x2f, 0x29, 0x84, 0x43, 0x33, 0xae, + 0xb6, 0xe3, 0xd8, 0x00, 0xe8, 0x98, 0xe6, 0x5b, 0xea, 0x6a, 0x67, 0x13, 0x71, 0xa6, 0xed, 0x23, + 0xcf, 0xab, 0xc4, 0x1a, 0x71, 0x9c, 0xbf, 0xfe, 0x8a, 0x58, 0x3e, 0x75, 0xe8, 0x43, 0x08, 0x81, + 0x0e, 0x93, 0xae, 0xd3, 0xa3, 0x26, 0x8d, 0x1a, 0xd6, 0xe9, 0x3a, 0x30, 0xbf, 0x80, 0xb5, 0x60, + 0x01, 0xd1, 0xff, 0x58, 0x83, 0xe9, 0x75, 0x67, 0x47, 0xc4, 0x78, 0xbf, 0x98, 0x41, 0xbc, 0x25, + 0xd5, 0xb2, 0xbc, 0xfc, 0x08, 0x2d, 0xfd, 0xf3, 0x4a, 0xb4, 0x75, 0x2e, 0x42, 0xbb, 0xca, 0x2a, + 0xff, 0x29, 0xa9, 0x75, 0x67, 0x67, 0x60, 0x38, 0xfe, 0xfb, 0x05, 0x98, 0x7d, 0xd1, 0x38, 0x20, + 0xb6, 0x6f, 0x88, 0x11, 0x7f, 0x0c, 0xa6, 0x8c, 0x56, 0x2b, 0xad, 0x12, 0xbe, 0xc6, 0x9b, 0x71, + 0x00, 0x67, 0x01, 0x4c, 0x97, 0xa5, 0xb1, 0x23, 0xa6, 0x36, 0x0c, 0x60, 0x42, 0x10, 0x8e, 0xe2, + 0x85, 0x47, 0x69, 0xd5, 0xb1, 0x77, 0xcd, 0x76, 0xda, 0x21, 0x58, 0x8d, 0xc1, 0x71, 0xa2, 0x07, + 0x5a, 0x07, 0x24, 0xaa, 0xdc, 0x6a, 0xcd, 0xa6, 0xd3, 0xb3, 0xf9, 0x61, 0xe2, 0xb1, 0x8d, 0xf4, + 0xf9, 0x36, 0x13, 0x18, 0x38, 0xa5, 0x17, 0x7a, 0x15, 0xca, 0x4d, 0x46, 0x59, 0x78, 0x00, 0x51, + 0x8a, 0xdc, 0x0b, 0x94, 0x25, 0x24, 0xab, 0x03, 0xf0, 0xf0, 0x40, 0x0a, 0x74, 0xa4, 0x9e, 0xef, + 0xb8, 0x46, 0x9b, 0x44, 0xe9, 0x4e, 0xaa, 0x23, 0x6d, 0x24, 0x30, 0x70, 0x4a, 0x2f, 0xf4, 0x25, + 0x98, 0xf6, 0xf7, 0x5c, 0xe2, 0xed, 0x39, 0x56, 0x4b, 0xdc, 0x86, 0x8e, 0x19, 0xf0, 0x8a, 0xdd, + 0xdf, 0x0e, 0xa8, 0x46, 0x7c, 0x92, 0xa0, 0x09, 0x87, 0x3c, 0x91, 0x0b, 0x93, 0x1e, 0x8d, 0xb6, + 0xbc, 0x72, 0x31, 0x0b, 0xaf, 0x4e, 0x70, 0x67, 0x01, 0x5c, 0x24, 0xd4, 0x66, 0x1c, 0xb0, 0xe0, + 0xa4, 0xff, 0x65, 0x0e, 0x66, 0xa2, 0x88, 0x43, 0x9c, 0xd4, 0xaf, 0x68, 0x30, 0xd3, 0x74, 0x6c, + 0xdf, 0x75, 0x2c, 0x1e, 0x46, 0xf2, 0x03, 0x32, 0x66, 0x35, 0x39, 0x23, 0x75, 0x89, 0xf8, 0x86, + 0x69, 0x45, 0x22, 0xd2, 0x08, 0x1b, 0xac, 0x30, 0x45, 0x5f, 0xd3, 0x60, 0x3e, 0x4c, 0x13, 0x85, + 0xf1, 0x6c, 0xa6, 0x03, 0x91, 0x95, 0x56, 0x97, 0x55, 0x4e, 0x38, 0xce, 0x5a, 0xdf, 0x81, 0x85, + 0xf8, 0x6e, 0xd3, 0xa5, 0xec, 0x1a, 0xe2, 0xac, 0xe7, 0xc3, 0xa5, 0xdc, 0x32, 0x3c, 0x0f, 0x33, + 0x08, 0xfa, 0x38, 0x14, 0x3b, 0x86, 0xdb, 0x36, 0x6d, 0xc3, 0x62, 0xab, 0x98, 0x8f, 0x28, 0x24, + 0xd1, 0x8e, 0x25, 0x86, 0xfe, 0xc3, 0x09, 0x28, 0x6d, 0x12, 0xc3, 0xeb, 0xb9, 0x84, 0x5d, 0x38, + 0x9d, 0xb8, 0x8b, 0xa8, 0x94, 0x67, 0xe7, 0xb3, 0x2b, 0xcf, 0x46, 0x2f, 0x03, 0xec, 0x9a, 0xb6, + 0xe9, 0xed, 0x3d, 0x60, 0xe1, 0x37, 0x4b, 0x18, 0x5e, 0x91, 0x14, 0x70, 0x84, 0x5a, 0xf8, 0xf2, + 0xa3, 0x70, 0xc4, 0xcb, 0x8f, 0xb7, 0xb4, 0x88, 0xf1, 0xe0, 0xce, 0xd7, 0xad, 0x71, 0xeb, 0x85, + 0xe5, 0xc6, 0x54, 0x03, 0x63, 0x72, 0xd9, 0xf6, 0xdd, 0x83, 0x23, 0x6d, 0xcc, 0x36, 0x14, 0x5d, + 0xe2, 0xf5, 0x3a, 0xd4, 0xd9, 0x9d, 0x1a, 0x79, 0x19, 0x58, 0x4e, 0x05, 0x8b, 0xfe, 0x58, 0x52, + 0x3a, 0xfb, 0x1c, 0xcc, 0x2a, 0x43, 0x40, 0x0b, 0x90, 0xbf, 0x43, 0x0e, 0xb8, 0x9c, 0x60, 0xfa, + 0x27, 0x5a, 0x52, 0x2a, 0x3f, 0xc5, 0xb2, 0x7c, 0x2a, 0xf7, 0xac, 0xa6, 0xff, 0x78, 0x12, 0x26, + 0x85, 0xbd, 0x3a, 0x5e, 0x17, 0x44, 0xef, 0x59, 0x73, 0x0f, 0x70, 0xcf, 0xba, 0x0e, 0x33, 0xa6, + 0x6d, 0xfa, 0xa6, 0x61, 0xb1, 0x3b, 0x7c, 0x61, 0xab, 0x1e, 0x0f, 0xce, 0xff, 0x5a, 0x04, 0x96, + 0x42, 0x47, 0xe9, 0x8b, 0x6e, 0x40, 0x81, 0x29, 0x73, 0x21, 0x4f, 0xa3, 0xa7, 0xc9, 0x58, 0x0a, + 0x9c, 0x97, 0x92, 0x71, 0x4a, 0xcc, 0xa7, 0xec, 0x35, 0x9b, 0xc4, 0xf3, 0xa4, 0x23, 0x2f, 0xc4, + 0x2a, 0xf4, 0x29, 0x63, 0x70, 0x9c, 0xe8, 0x41, 0xa9, 0xec, 0x1a, 0xa6, 0xd5, 0x73, 0x49, 0x48, + 0x65, 0x52, 0xa5, 0x72, 0x25, 0x06, 0xc7, 0x89, 0x1e, 0x68, 0x17, 0x66, 0x44, 0x1b, 0xcf, 0x92, + 0x4c, 0x3d, 0xe0, 0x2c, 0x59, 0x36, 0xec, 0x4a, 0x84, 0x12, 0x56, 0xe8, 0xa2, 0x1e, 0x2c, 0x9a, + 0x76, 0xd3, 0xb1, 0x9b, 0x56, 0xcf, 0x33, 0xf7, 0x49, 0x58, 0xc7, 0xf5, 0x20, 0xcc, 0x4e, 0x1f, + 0xf6, 0x2b, 0x8b, 0x6b, 0x71, 0x72, 0x38, 0xc9, 0x01, 0xbd, 0xa9, 0xc1, 0xe9, 0xa6, 0x63, 0x7b, + 0xac, 0x92, 0x79, 0x9f, 0x5c, 0x76, 0x5d, 0xc7, 0xe5, 0xbc, 0xa7, 0x1f, 0x90, 0x37, 0xcb, 0x4b, + 0xad, 0xa6, 0x91, 0xc4, 0xe9, 0x9c, 0xd0, 0xeb, 0x50, 0xec, 0xba, 0xce, 0xbe, 0xd9, 0x22, 0xae, + 0xc8, 0xb8, 0x6d, 0x64, 0xf1, 0x88, 0x60, 0x4b, 0xd0, 0x0c, 0x35, 0x41, 0xd0, 0x82, 0x25, 0x3f, + 0xfd, 0x9b, 0x93, 0x30, 0xa7, 0xa2, 0xa3, 0x2f, 0x02, 0x74, 0x5d, 0xa7, 0x43, 0xfc, 0x3d, 0x22, + 0xeb, 0x7d, 0xae, 0x8d, 0x5b, 0xc0, 0x1f, 0xd0, 0x13, 0xef, 0x1b, 0x98, 0x26, 0x0d, 0x5b, 0x71, + 0x84, 0x23, 0x72, 0x61, 0xea, 0x0e, 0xb7, 0x69, 0xc2, 0xc4, 0xbf, 0x98, 0x89, 0x43, 0x22, 0x38, + 0x97, 0xa8, 0xc9, 0x11, 0x4d, 0x38, 0x60, 0x84, 0x76, 0x20, 0x7f, 0x97, 0xec, 0x64, 0x53, 0x6a, + 0x7e, 0x8b, 0x88, 0x50, 0xa1, 0x3e, 0x75, 0xd8, 0xaf, 0xe4, 0x6f, 0x91, 0x1d, 0x4c, 0x89, 0xd3, + 0x79, 0xb5, 0x78, 0xb6, 0x48, 0xa8, 0x8a, 0x31, 0xe7, 0xa5, 0xa4, 0x9e, 0xf8, 0xbc, 0x44, 0x13, + 0x0e, 0x18, 0xa1, 0xd7, 0x61, 0xfa, 0xae, 0xb1, 0x4f, 0x76, 0x5d, 0xc7, 0xf6, 0x45, 0xbd, 0xc0, + 0x98, 0x75, 0x2c, 0xb7, 0x02, 0x72, 0x82, 0x2f, 0xb3, 0xb6, 0xb2, 0x11, 0x87, 0xec, 0xd0, 0x3e, + 0x14, 0x6d, 0x72, 0x17, 0x13, 0xcb, 0x6c, 0x8a, 0x12, 0x82, 0x31, 0xc5, 0xfa, 0x9a, 0xa0, 0x26, + 0x38, 0x33, 0x33, 0x14, 0xb4, 0x61, 0xc9, 0x8b, 0xee, 0xe5, 0x6d, 0x67, 0x47, 0x28, 0xaa, 0x31, + 0xf7, 0x52, 0x86, 0x7d, 0x7c, 0x2f, 0xd7, 0x9d, 0x1d, 0x4c, 0x89, 0xeb, 0xdf, 0x9a, 0x80, 0x99, + 0xe8, 0x13, 0xb3, 0x21, 0x6c, 0x96, 0x74, 0x9b, 0x72, 0xa3, 0xb8, 0x4d, 0xd4, 0xeb, 0xed, 0x84, + 0x36, 0x3e, 0xb8, 0x2a, 0x5b, 0xcb, 0xcc, 0x6b, 0x08, 0xbd, 0xde, 0x48, 0xa3, 0x87, 0x15, 0xa6, + 0x23, 0xa4, 0x9a, 0xa8, 0x1f, 0xc4, 0xcd, 0x21, 0xaf, 0x4d, 0x96, 0x7e, 0x90, 0x62, 0xe0, 0x2e, + 0x02, 0x08, 0x73, 0xb5, 0xdb, 0xb3, 0x98, 0x70, 0x14, 0xc2, 0xcb, 0xab, 0x86, 0x84, 0xe0, 0x08, + 0x16, 0x7a, 0x1c, 0x26, 0xa9, 0xc1, 0x20, 0x2d, 0x51, 0x34, 0x2c, 0x43, 0x8b, 0x2b, 0xac, 0x15, + 0x0b, 0x28, 0x7a, 0x96, 0xda, 0xf6, 0x50, 0xcd, 0x8b, 0x5a, 0xe0, 0xa5, 0xd0, 0xb6, 0x87, 0x30, + 0xac, 0x60, 0xd2, 0xa1, 0x13, 0xaa, 0x95, 0x99, 0xea, 0x8f, 0x0c, 0x9d, 0xa9, 0x6a, 0xcc, 0x61, + 0x2c, 0xd4, 0x8d, 0x69, 0x71, 0xa6, 0xb4, 0x0b, 0x91, 0x50, 0x37, 0x06, 0xc7, 0x89, 0x1e, 0xfa, + 0x67, 0x61, 0x4e, 0x95, 0x66, 0xba, 0xc4, 0x5d, 0xd7, 0xd9, 0x35, 0x2d, 0x12, 0x0f, 0xd2, 0xb7, + 0x78, 0x33, 0x0e, 0xe0, 0xc3, 0x65, 0x89, 0xff, 0x2a, 0x0f, 0xa7, 0xae, 0xb5, 0x4d, 0xfb, 0x5e, + 0xec, 0x46, 0x29, 0xed, 0xf5, 0xb9, 0x36, 0xea, 0xeb, 0xf3, 0xb0, 0x94, 0x4b, 0xbc, 0xa5, 0x4f, + 0x2f, 0xe5, 0x0a, 0x1e, 0xda, 0xab, 0xb8, 0xe8, 0x07, 0x1a, 0x9c, 0x33, 0x5a, 0xdc, 0xbf, 0x30, + 0x2c, 0xd1, 0x1a, 0x32, 0x0d, 0x64, 0xdc, 0x1b, 0x53, 0x5b, 0x24, 0x27, 0x5f, 0xad, 0x1d, 0xc1, + 0x95, 0x7b, 0xcd, 0x1f, 0x15, 0x33, 0x38, 0x77, 0x14, 0x2a, 0x3e, 0x72, 0xf8, 0x67, 0xaf, 0xc3, + 0x47, 0x8e, 0x65, 0x34, 0x92, 0x6f, 0xfc, 0x07, 0x1a, 0xcc, 0xb1, 0x1a, 0xc9, 0xd0, 0x2d, 0x7b, + 0x46, 0xe6, 0xb4, 0xf8, 0xe6, 0x9d, 0x57, 0x73, 0x5a, 0xf7, 0xfb, 0x95, 0x12, 0xaf, 0xaa, 0x54, + 0x53, 0x5c, 0xaf, 0x88, 0xd0, 0x8a, 0x65, 0xde, 0x72, 0x23, 0x7b, 0xfe, 0xf2, 0x22, 0xa1, 0x11, + 0x10, 0xc1, 0x21, 0x3d, 0xfd, 0x9b, 0x79, 0x38, 0x95, 0x52, 0xec, 0x43, 0xa3, 0x9e, 0x49, 0xcb, + 0xd8, 0x21, 0x56, 0x90, 0x37, 0x7a, 0x2d, 0xf3, 0x82, 0xa2, 0xea, 0x06, 0xa3, 0xcf, 0xf7, 0x50, + 0x6a, 0x06, 0xde, 0x88, 0x05, 0x73, 0xf4, 0x5b, 0x1a, 0x94, 0x8c, 0x88, 0x98, 0xf1, 0x54, 0xda, + 0x4e, 0xf6, 0x83, 0x49, 0x48, 0x55, 0xa4, 0x04, 0x20, 0x14, 0xa2, 0xe8, 0x58, 0xce, 0xfe, 0x3c, + 0x94, 0x22, 0x53, 0x18, 0x45, 0x3a, 0xce, 0x3e, 0x0f, 0x0b, 0x63, 0x49, 0xd7, 0x67, 0x60, 0xd4, + 0xb7, 0x92, 0x54, 0x17, 0xdf, 0x8d, 0x96, 0x0e, 0xcb, 0x15, 0x17, 0xb5, 0xc3, 0x02, 0xaa, 0xef, + 0xc0, 0x42, 0xdc, 0xf5, 0x1b, 0xe5, 0x36, 0x72, 0x28, 0x45, 0xf7, 0x49, 0x18, 0xf1, 0x75, 0xa3, + 0xfe, 0xd7, 0x39, 0x98, 0x12, 0x15, 0x83, 0x0f, 0xa1, 0x7a, 0xe6, 0x8e, 0x72, 0x9f, 0xbb, 0x96, + 0x49, 0xa1, 0xe3, 0xc0, 0xd2, 0x19, 0x2f, 0x56, 0x3a, 0xf3, 0x62, 0x36, 0xec, 0x8e, 0xae, 0x9b, + 0x79, 0x27, 0x07, 0xf3, 0xb1, 0x0a, 0x4c, 0xf4, 0xcb, 0x5a, 0x32, 0x5d, 0x7c, 0x33, 0xd3, 0x22, + 0x4f, 0x59, 0x9b, 0x75, 0x74, 0xe6, 0xd8, 0x53, 0xde, 0x4b, 0xdf, 0xc8, 0xec, 0xdb, 0x13, 0x47, + 0x3e, 0x9d, 0xfe, 0x67, 0x0d, 0x3e, 0x3c, 0xb0, 0x26, 0x95, 0xbd, 0x4b, 0x71, 0x55, 0xa8, 0x90, + 0xbd, 0x8c, 0x6b, 0xcc, 0xe5, 0x3d, 0x62, 0xfc, 0xa9, 0x42, 0x9c, 0x3d, 0x7a, 0x1a, 0x66, 0x98, + 0x1e, 0xa7, 0xc7, 0xc7, 0x27, 0x5d, 0xf1, 0x09, 0x1c, 0x16, 0xb3, 0x37, 0x22, 0xed, 0x58, 0xc1, + 0xd2, 0x7f, 0x4f, 0x83, 0xf2, 0xa0, 0x57, 0x10, 0x43, 0x78, 0xc4, 0x3f, 0x17, 0xab, 0x64, 0xa9, + 0x24, 0x2a, 0x59, 0x62, 0x3e, 0x71, 0x50, 0xb4, 0x12, 0x71, 0x47, 0xf3, 0xc7, 0x14, 0x6a, 0x7c, + 0x5d, 0x83, 0x33, 0x03, 0x04, 0x27, 0x51, 0xd1, 0xa4, 0x3d, 0x70, 0x45, 0x53, 0x6e, 0xd8, 0x8a, + 0x26, 0xfd, 0x6f, 0xf3, 0xb0, 0x20, 0xc6, 0x13, 0x1a, 0xf3, 0x67, 0x95, 0x7a, 0xa0, 0x8f, 0xc6, + 0xea, 0x81, 0x96, 0xe2, 0xf8, 0xff, 0x57, 0x0c, 0xf4, 0xd3, 0x55, 0x0c, 0xf4, 0x93, 0x1c, 0x9c, + 0x4e, 0x7d, 0x61, 0x82, 0xde, 0x4e, 0xd1, 0x82, 0xb7, 0x32, 0x7e, 0xca, 0x32, 0xa4, 0x1e, 0x1c, + 0xb7, 0x82, 0xe6, 0x37, 0xa3, 0x95, 0x2b, 0xdc, 0x41, 0xdf, 0x3d, 0x81, 0x47, 0x39, 0xa3, 0x16, + 0xb1, 0xfc, 0x6a, 0x1e, 0x9e, 0x18, 0x96, 0xd0, 0x4f, 0x69, 0x91, 0xa3, 0xa7, 0x14, 0x39, 0x3e, + 0x1c, 0x0b, 0x75, 0x32, 0xf5, 0x8e, 0x5f, 0xcd, 0x4b, 0xb3, 0x97, 0x94, 0xcf, 0xa1, 0xae, 0xf5, + 0xa7, 0xa8, 0x17, 0x13, 0x7c, 0xff, 0x20, 0x54, 0x85, 0x53, 0x0d, 0xde, 0x7c, 0xbf, 0x5f, 0x59, + 0x14, 0xcf, 0xac, 0x1b, 0xc4, 0x17, 0x8d, 0x38, 0xe8, 0x84, 0x9e, 0x80, 0xa2, 0xcb, 0xa1, 0x41, + 0x59, 0x97, 0x48, 0x55, 0xf0, 0x36, 0x2c, 0xa1, 0xe8, 0x4b, 0x11, 0xb7, 0x6f, 0xe2, 0xa4, 0x1e, + 0x39, 0x1c, 0x95, 0x81, 0x79, 0x0d, 0x8a, 0x5e, 0xf0, 0x71, 0x04, 0x7e, 0x2f, 0xf7, 0xd4, 0x90, + 0xd5, 0x82, 0x34, 0x4a, 0x08, 0xbe, 0x94, 0xc0, 0xe7, 0x27, 0xbf, 0xa3, 0x20, 0x49, 0xea, 0xef, + 0x6b, 0x50, 0x12, 0x3b, 0xf1, 0x10, 0x8a, 0x13, 0x6f, 0xab, 0xc5, 0x89, 0x97, 0x33, 0xd1, 0x0b, + 0x03, 0x2a, 0x13, 0x6f, 0xc3, 0x4c, 0xf4, 0x01, 0x21, 0x7a, 0x39, 0xa2, 0xd7, 0xb4, 0x71, 0x1e, + 0x2a, 0x05, 0x9a, 0x2f, 0xd4, 0x79, 0xfa, 0x77, 0x0b, 0x72, 0x15, 0x59, 0x09, 0x64, 0x54, 0xbe, + 0xb4, 0x23, 0xe5, 0x2b, 0xba, 0xbd, 0xb9, 0xcc, 0xb7, 0x17, 0xdd, 0x80, 0x62, 0xa0, 0x7c, 0x84, + 0x89, 0x7e, 0x2c, 0x5a, 0x27, 0x42, 0xed, 0x3c, 0x25, 0x16, 0x11, 0x4a, 0x16, 0x31, 0xc8, 0x3d, + 0x94, 0x4a, 0x51, 0x92, 0x41, 0x35, 0x98, 0xef, 0x98, 0x36, 0x26, 0x46, 0x4b, 0xbe, 0xb3, 0x99, + 0xe0, 0xdf, 0x3d, 0x08, 0x9c, 0xc8, 0x4d, 0x15, 0x8c, 0xe3, 0xf8, 0xe8, 0xf3, 0x50, 0xf4, 0xc4, + 0x43, 0xc1, 0x6c, 0xee, 0x9a, 0x65, 0xf8, 0xc1, 0x89, 0x86, 0xe3, 0x0f, 0x5a, 0xb0, 0x64, 0x88, + 0x36, 0x60, 0xc9, 0x15, 0x4f, 0x71, 0xae, 0x9a, 0x9e, 0xef, 0xb8, 0x07, 0x3c, 0x8d, 0xc3, 0x2f, + 0x17, 0xd9, 0xf3, 0x7a, 0x9c, 0x02, 0xc7, 0xa9, 0xbd, 0xa8, 0x97, 0xc0, 0x5e, 0xa3, 0xf2, 0xcb, + 0xc6, 0x62, 0xe8, 0x25, 0x30, 0xa1, 0x6b, 0x61, 0x01, 0x3d, 0xaa, 0xae, 0xb4, 0x38, 0x46, 0x5d, + 0xe9, 0x2d, 0x98, 0x76, 0x09, 0x73, 0xb5, 0x6b, 0x41, 0x22, 0x6a, 0xe4, 0x0c, 0x38, 0x0e, 0x08, + 0xe0, 0x90, 0x96, 0xfe, 0x27, 0x33, 0x30, 0xab, 0x04, 0x75, 0x34, 0xc6, 0x36, 0x76, 0x1c, 0x97, + 0x47, 0xf2, 0xc5, 0xf0, 0xd0, 0xd5, 0x68, 0x23, 0xe6, 0x30, 0xf4, 0x8e, 0x06, 0xf3, 0x5d, 0xe5, + 0x02, 0x2a, 0x38, 0xeb, 0x63, 0x5e, 0xe9, 0xab, 0xb7, 0x5a, 0x91, 0x4f, 0xcc, 0xa8, 0xcc, 0x70, + 0x9c, 0x3b, 0x15, 0x57, 0x51, 0x97, 0x61, 0x11, 0x97, 0x61, 0x0b, 0x8b, 0x2b, 0x49, 0xac, 0xaa, + 0x60, 0x1c, 0xc7, 0xa7, 0x8b, 0xcc, 0x66, 0x37, 0xce, 0x57, 0xe0, 0x6a, 0x01, 0x01, 0x1c, 0xd2, + 0x42, 0xcf, 0xc3, 0x9c, 0x78, 0x7f, 0xbd, 0xe5, 0xb4, 0xae, 0x1a, 0xde, 0x9e, 0x70, 0x35, 0xa5, + 0x6b, 0xbc, 0xaa, 0x40, 0x71, 0x0c, 0x9b, 0xcd, 0x2d, 0x7c, 0xe4, 0xce, 0x08, 0x4c, 0xaa, 0x5f, + 0xe0, 0x59, 0x55, 0xc1, 0x38, 0x8e, 0x8f, 0x3e, 0x1e, 0xd1, 0x54, 0xfc, 0xba, 0x5c, 0x9e, 0x9d, + 0x14, 0x6d, 0x55, 0x83, 0xf9, 0x1e, 0xf3, 0xcc, 0x5b, 0x01, 0x50, 0x48, 0xaf, 0x64, 0x78, 0x53, + 0x05, 0xe3, 0x38, 0x3e, 0x7a, 0x0e, 0x66, 0x5d, 0xaa, 0x0b, 0x24, 0x01, 0x7e, 0x87, 0x2e, 0x2f, + 0x84, 0x71, 0x14, 0x88, 0x55, 0x5c, 0xf4, 0x02, 0x2c, 0x86, 0x2f, 0x51, 0x03, 0x02, 0xfc, 0x52, + 0x5d, 0x3e, 0x72, 0xaf, 0xc5, 0x11, 0x70, 0xb2, 0x0f, 0xfa, 0x05, 0x58, 0x88, 0xac, 0xc4, 0x9a, + 0xdd, 0x22, 0xf7, 0xc4, 0x6b, 0xc1, 0x25, 0x76, 0x31, 0x1f, 0x83, 0xe1, 0x04, 0x36, 0xfa, 0x14, + 0xcc, 0x35, 0x1d, 0xcb, 0x62, 0x1a, 0x81, 0x7f, 0xfd, 0x85, 0x3f, 0x0b, 0xe4, 0x0f, 0x28, 0x15, + 0x08, 0x8e, 0x61, 0xa2, 0x75, 0x40, 0xce, 0x8e, 0x47, 0xdc, 0x7d, 0xd2, 0x7a, 0x81, 0x7f, 0xa2, + 0x96, 0x1a, 0xa5, 0x59, 0xb5, 0x2a, 0xec, 0x7a, 0x02, 0x03, 0xa7, 0xf4, 0x42, 0x5f, 0x56, 0x4b, + 0x86, 0xe7, 0xb2, 0xf8, 0xd6, 0x5d, 0x3c, 0x8e, 0x3c, 0xb6, 0x5e, 0xd8, 0x85, 0x49, 0x5e, 0xa4, + 0x57, 0x9e, 0xcf, 0xe2, 0x75, 0x6c, 0xf4, 0x43, 0x13, 0xa1, 0x46, 0xe5, 0xad, 0x58, 0x70, 0x42, + 0x5f, 0x84, 0xe9, 0x9d, 0xe0, 0xab, 0x40, 0xe5, 0x85, 0x2c, 0xac, 0x48, 0xec, 0x03, 0x57, 0x61, + 0x9c, 0x24, 0x01, 0x38, 0x64, 0x89, 0x1e, 0x87, 0xd2, 0xd5, 0xad, 0x9a, 0x94, 0xc2, 0x45, 0xb6, + 0xfb, 0x13, 0xb4, 0x0b, 0x8e, 0x02, 0xe8, 0x09, 0x93, 0x16, 0x1e, 0xb1, 0x2d, 0x0e, 0xad, 0x53, + 0xd2, 0x60, 0x53, 0x6c, 0x96, 0x03, 0xc1, 0x8d, 0xf2, 0xa9, 0x18, 0xb6, 0x68, 0xc7, 0x12, 0x03, + 0xbd, 0x06, 0x25, 0xa1, 0xb2, 0x99, 0x6e, 0x5a, 0x7a, 0xb0, 0x72, 0x74, 0x1c, 0x92, 0xc0, 0x51, + 0x7a, 0xe8, 0x19, 0x28, 0x75, 0xd9, 0xc7, 0x52, 0xc8, 0x95, 0x9e, 0x65, 0x95, 0x4f, 0x33, 0xbd, + 0x29, 0xaf, 0xa8, 0xb7, 0x42, 0x10, 0x8e, 0xe2, 0xe9, 0x6f, 0x86, 0xd7, 0x7c, 0xf2, 0x7b, 0x00, + 0x5f, 0x88, 0xee, 0x96, 0x96, 0xc5, 0xa7, 0x6c, 0x13, 0x9f, 0x84, 0xe2, 0x8a, 0x36, 0x75, 0xaf, + 0xba, 0x52, 0x3e, 0x33, 0x79, 0x9a, 0xa8, 0x7e, 0xeb, 0x80, 0x97, 0x02, 0xab, 0xd2, 0xa9, 0xbf, + 0x9f, 0x97, 0xa1, 0x7e, 0x2c, 0xaf, 0xe6, 0x42, 0xc1, 0xf4, 0x7c, 0xd3, 0xc9, 0xb0, 0x3e, 0x3b, + 0xf6, 0x91, 0x00, 0x56, 0x27, 0xc4, 0x00, 0x98, 0xb3, 0xa2, 0x3c, 0xed, 0xb6, 0x69, 0xdf, 0x13, + 0xd3, 0xbf, 0x91, 0x79, 0xc2, 0x8c, 0xf3, 0x64, 0x00, 0xcc, 0x59, 0xa1, 0xdb, 0x90, 0x37, 0xac, + 0x9d, 0x8c, 0x3e, 0x5b, 0x1c, 0xff, 0x68, 0x37, 0xcf, 0xb2, 0xd7, 0x36, 0xea, 0x98, 0x32, 0xa1, + 0xbc, 0xbc, 0x8e, 0x29, 0x6c, 0xf3, 0x98, 0xbc, 0x1a, 0x9b, 0x6b, 0x69, 0xbc, 0x1a, 0x9b, 0x6b, + 0x98, 0x32, 0xd1, 0xdf, 0xd5, 0x60, 0x31, 0x81, 0x13, 0xff, 0xc4, 0xb7, 0x36, 0xfc, 0x27, 0xbe, + 0xc5, 0xd7, 0x1b, 0x1a, 0x5d, 0xcb, 0x4c, 0x7d, 0x5a, 0xb0, 0x1d, 0x83, 0xe3, 0x44, 0x0f, 0xfd, + 0xdb, 0x1a, 0x94, 0x22, 0x65, 0xa1, 0xd4, 0x55, 0x63, 0xe5, 0xb3, 0x62, 0x18, 0xe1, 0x87, 0x2b, + 0xd8, 0xa5, 0x02, 0x87, 0xf1, 0xfb, 0xad, 0x76, 0x78, 0xcb, 0x13, 0xb9, 0xdf, 0xa2, 0xad, 0x58, + 0x40, 0x69, 0x34, 0xee, 0xf9, 0xa4, 0xcb, 0x36, 0x32, 0x52, 0x25, 0xca, 0x6e, 0x79, 0x19, 0x84, + 0xb1, 0xa3, 0x3a, 0x43, 0xa4, 0xfc, 0x23, 0xdf, 0xc9, 0x30, 0xa8, 0x67, 0xc8, 0x60, 0xe8, 0x3c, + 0xe4, 0x89, 0xdd, 0x12, 0x0e, 0x4e, 0x49, 0xa0, 0xe4, 0x2f, 0xdb, 0x2d, 0x4c, 0xdb, 0xf5, 0xeb, + 0x30, 0xd3, 0x20, 0x4d, 0x97, 0xf8, 0x2f, 0x92, 0x83, 0xe1, 0x6e, 0x60, 0xce, 0xf3, 0xcc, 0x55, + 0x4e, 0x25, 0x48, 0xbb, 0xd3, 0x76, 0xfd, 0x8f, 0x34, 0x88, 0x7d, 0xb6, 0x04, 0xe9, 0xb1, 0x64, + 0x14, 0x24, 0x13, 0x51, 0x4a, 0xe4, 0x96, 0x3b, 0x32, 0x72, 0x5b, 0x07, 0xd4, 0x31, 0xfc, 0xe6, + 0x9e, 0xd8, 0x1f, 0xf1, 0x85, 0x1c, 0xee, 0x5b, 0x86, 0x45, 0xe8, 0x09, 0x0c, 0x9c, 0xd2, 0x4b, + 0xff, 0x6e, 0x0e, 0x66, 0x94, 0xaf, 0xc5, 0x1e, 0x3f, 0xfd, 0xe1, 0x07, 0x9a, 0x12, 0xb0, 0xe5, + 0x47, 0x0c, 0xd8, 0xa2, 0x51, 0xea, 0xc4, 0xc9, 0x46, 0xa9, 0x85, 0x4c, 0xa2, 0x54, 0xfd, 0x3b, + 0x13, 0x30, 0xa7, 0xbe, 0xe7, 0x1a, 0x62, 0x4d, 0x3f, 0x9e, 0x58, 0xd3, 0x11, 0x9d, 0xe1, 0xfc, + 0xb8, 0xce, 0xf0, 0xc4, 0xb8, 0xce, 0x70, 0xe1, 0x01, 0x9c, 0xe1, 0xa4, 0x2b, 0x3b, 0x39, 0xb4, + 0x2b, 0xfb, 0x69, 0x99, 0x57, 0x98, 0x52, 0x2e, 0xe2, 0xc2, 0xbc, 0x02, 0x52, 0xb7, 0x61, 0xd5, + 0x69, 0xa5, 0xe6, 0x67, 0x8a, 0xc7, 0x94, 0x0b, 0xb9, 0xa9, 0x69, 0x80, 0xd1, 0x43, 0xde, 0x0f, + 0x0d, 0x9f, 0x02, 0xd0, 0xdf, 0xc8, 0x41, 0xf8, 0x01, 0x58, 0xf6, 0x25, 0x18, 0x2f, 0xa2, 0xa3, + 0x84, 0x01, 0x5f, 0x1f, 0xf7, 0x73, 0x4b, 0x21, 0x45, 0x91, 0x47, 0x8b, 0xb4, 0x60, 0x85, 0xe3, + 0x7f, 0xc3, 0x87, 0x5f, 0x0d, 0x98, 0x8f, 0x15, 0xf2, 0x65, 0x9e, 0x97, 0xff, 0x76, 0x0e, 0xa6, + 0x65, 0x29, 0x24, 0x55, 0xeb, 0x3d, 0x37, 0xf8, 0x28, 0x86, 0x54, 0xeb, 0x37, 0xf1, 0x06, 0xa6, + 0xed, 0xe8, 0x1e, 0x4c, 0xed, 0x11, 0xa3, 0x45, 0xdc, 0xe0, 0x5e, 0x61, 0x33, 0xa3, 0x1a, 0xcc, + 0xab, 0x8c, 0x6a, 0x38, 0x17, 0xfe, 0xdb, 0xc3, 0x01, 0x3b, 0x1a, 0xac, 0xfb, 0x66, 0x87, 0x50, + 0xa7, 0x36, 0xa2, 0x45, 0xf3, 0x61, 0xb0, 0xbe, 0xad, 0x40, 0x71, 0x0c, 0x9b, 0x2a, 0x97, 0xdb, + 0x9e, 0x63, 0xb3, 0x07, 0x8b, 0x13, 0xaa, 0x67, 0xbf, 0xde, 0xb8, 0x7e, 0x8d, 0xbd, 0x57, 0x94, + 0x18, 0x14, 0xdb, 0x64, 0xa5, 0x60, 0x2e, 0x11, 0x37, 0xed, 0x0b, 0x61, 0xe1, 0x3a, 0x6f, 0xc7, + 0x12, 0x43, 0xbf, 0x09, 0xf3, 0xb1, 0x89, 0x04, 0xe6, 0x51, 0x4b, 0x37, 0x8f, 0x43, 0xfd, 0xff, + 0x89, 0x7a, 0xf5, 0xbd, 0x0f, 0x96, 0x1f, 0xf9, 0xde, 0x07, 0xcb, 0x8f, 0x7c, 0xff, 0x83, 0xe5, + 0x47, 0xde, 0x38, 0x5c, 0xd6, 0xde, 0x3b, 0x5c, 0xd6, 0xbe, 0x77, 0xb8, 0xac, 0x7d, 0xff, 0x70, + 0x59, 0xfb, 0xa7, 0xc3, 0x65, 0xed, 0xdd, 0x1f, 0x2e, 0x3f, 0xf2, 0x72, 0x31, 0x58, 0xcc, 0xff, + 0x0a, 0x00, 0x00, 0xff, 0xff, 0x08, 0xdf, 0xd6, 0x8d, 0x38, 0x67, 0x00, 0x00, } func (m *ALBTrafficRouting) Marshal() (dAtA []byte, err error) { @@ -3397,6 +3397,16 @@ func (m *CanaryStrategy) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.ScaleDownDelayRevisionLimit != nil { + i = encodeVarintGenerated(dAtA, i, uint64(*m.ScaleDownDelayRevisionLimit)) + i-- + dAtA[i] = 0x60 + } + if m.ScaleDownDelaySeconds != nil { + i = encodeVarintGenerated(dAtA, i, uint64(*m.ScaleDownDelaySeconds)) + i-- + dAtA[i] = 0x58 + } if m.StableMetadata != nil { { size, err := m.StableMetadata.MarshalToSizedBuffer(dAtA[:i]) @@ -6832,6 +6842,12 @@ func (m *CanaryStrategy) Size() (n int) { l = m.StableMetadata.Size() n += 1 + l + sovGenerated(uint64(l)) } + if m.ScaleDownDelaySeconds != nil { + n += 1 + sovGenerated(uint64(*m.ScaleDownDelaySeconds)) + } + if m.ScaleDownDelayRevisionLimit != nil { + n += 1 + sovGenerated(uint64(*m.ScaleDownDelayRevisionLimit)) + } return n } @@ -8156,6 +8172,8 @@ func (this *CanaryStrategy) String() string { `AntiAffinity:` + strings.Replace(this.AntiAffinity.String(), "AntiAffinity", "AntiAffinity", 1) + `,`, `CanaryMetadata:` + strings.Replace(this.CanaryMetadata.String(), "PodTemplateMetadata", "PodTemplateMetadata", 1) + `,`, `StableMetadata:` + strings.Replace(this.StableMetadata.String(), "PodTemplateMetadata", "PodTemplateMetadata", 1) + `,`, + `ScaleDownDelaySeconds:` + valueToStringGenerated(this.ScaleDownDelaySeconds) + `,`, + `ScaleDownDelayRevisionLimit:` + valueToStringGenerated(this.ScaleDownDelayRevisionLimit) + `,`, `}`, }, "") return s @@ -12079,6 +12097,46 @@ func (m *CanaryStrategy) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ScaleDownDelaySeconds", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ScaleDownDelaySeconds = &v + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ScaleDownDelayRevisionLimit", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ScaleDownDelayRevisionLimit = &v default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/pkg/apis/rollouts/v1alpha1/generated.proto b/pkg/apis/rollouts/v1alpha1/generated.proto index 4c6221626a..bf3b75437b 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.proto +++ b/pkg/apis/rollouts/v1alpha1/generated.proto @@ -348,6 +348,18 @@ message CanaryStrategy { // StableMetadata specify labels and annotations which will be attached to the stable pods for // the duration which they act as a canary, and will be removed after optional PodTemplateMetadata stableMetadata = 10; + + // ScaleDownDelaySeconds adds a delay before scaling down the previous ReplicaSet when the + // canary strategy is used with traffic routing (default 30 seconds). A delay in scaling down + // the previous ReplicaSet is needed after switching the stable service selector to point to + // the new ReplicaSet, in order to give time for traffic providers to re-target the new pods. + // This value is ignored with basic, replica-weighted canary without traffic routing. + // +optional + optional int32 scaleDownDelaySeconds = 11; + + // ScaleDownDelayRevisionLimit limits the number of old RS that can run at one time before getting scaled down + // +optional + optional int32 scaleDownDelayRevisionLimit = 12; } // ClusterAnalysisTemplate holds the template for performing canary analysis diff --git a/pkg/apis/rollouts/v1alpha1/openapi_generated.go b/pkg/apis/rollouts/v1alpha1/openapi_generated.go index 6fcd0e038c..f73213e0ff 100644 --- a/pkg/apis/rollouts/v1alpha1/openapi_generated.go +++ b/pkg/apis/rollouts/v1alpha1/openapi_generated.go @@ -939,6 +939,20 @@ func schema_pkg_apis_rollouts_v1alpha1_CanaryStrategy(ref common.ReferenceCallba Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PodTemplateMetadata"), }, }, + "scaleDownDelaySeconds": { + SchemaProps: spec.SchemaProps{ + Description: "ScaleDownDelaySeconds adds a delay before scaling down the previous ReplicaSet when the canary strategy is used with traffic routing (default 30 seconds). A delay in scaling down the previous ReplicaSet is needed after switching the stable service selector to point to the new ReplicaSet, in order to give time for traffic providers to re-target the new pods. This value is ignored with basic, replica-weighted canary without traffic routing.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "scaleDownDelayRevisionLimit": { + SchemaProps: spec.SchemaProps{ + Description: "ScaleDownDelayRevisionLimit limits the number of old RS that can run at one time before getting scaled down", + Type: []string{"integer"}, + Format: "int32", + }, + }, }, }, }, diff --git a/pkg/apis/rollouts/v1alpha1/types.go b/pkg/apis/rollouts/v1alpha1/types.go index 5159d2e591..9ab7809624 100644 --- a/pkg/apis/rollouts/v1alpha1/types.go +++ b/pkg/apis/rollouts/v1alpha1/types.go @@ -205,6 +205,17 @@ type CanaryStrategy struct { // StableMetadata specify labels and annotations which will be attached to the stable pods for // the duration which they act as a canary, and will be removed after StableMetadata *PodTemplateMetadata `json:"stableMetadata,omitempty" protobuf:"bytes,10,opt,name=stableMetadata"` + + // ScaleDownDelaySeconds adds a delay before scaling down the previous ReplicaSet when the + // canary strategy is used with traffic routing (default 30 seconds). A delay in scaling down + // the previous ReplicaSet is needed after switching the stable service selector to point to + // the new ReplicaSet, in order to give time for traffic providers to re-target the new pods. + // This value is ignored with basic, replica-weighted canary without traffic routing. + // +optional + ScaleDownDelaySeconds *int32 `json:"scaleDownDelaySeconds,omitempty" protobuf:"varint,11,opt,name=scaleDownDelaySeconds"` + // ScaleDownDelayRevisionLimit limits the number of old RS that can run at one time before getting scaled down + // +optional + ScaleDownDelayRevisionLimit *int32 `json:"scaleDownDelayRevisionLimit,omitempty" protobuf:"varint,12,opt,name=scaleDownDelayRevisionLimit"` } // ALBTrafficRouting configuration for ALB ingress controller to control traffic routing diff --git a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go index f701f002c4..3b6690f3d3 100644 --- a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go @@ -553,6 +553,16 @@ func (in *CanaryStrategy) DeepCopyInto(out *CanaryStrategy) { *out = new(PodTemplateMetadata) (*in).DeepCopyInto(*out) } + if in.ScaleDownDelaySeconds != nil { + in, out := &in.ScaleDownDelaySeconds, &out.ScaleDownDelaySeconds + *out = new(int32) + **out = **in + } + if in.ScaleDownDelayRevisionLimit != nil { + in, out := &in.ScaleDownDelayRevisionLimit, &out.ScaleDownDelayRevisionLimit + *out = new(int32) + **out = **in + } return } diff --git a/pkg/apis/rollouts/validation/validation.go b/pkg/apis/rollouts/validation/validation.go index 8ada64bb88..1bca6185eb 100644 --- a/pkg/apis/rollouts/validation/validation.go +++ b/pkg/apis/rollouts/validation/validation.go @@ -55,6 +55,8 @@ const ( // InvalidAnalysisArgsMessage indicates that arguments provided in analysis steps are refrencing un-supported metadatafield. //supported fields are "metadata.annotations", "metadata.labels", "metadata.name", "metadata.namespace", "metadata.uid" InvalidAnalysisArgsMessage = "Analyses arguments must refer to valid object metadata supported by downwardAPI" + // InvalidCanaryScaleDownDelay indicates that canary.scaleDownDelaySeconds cannot be used + InvalidCanaryScaleDownDelay = "Canary scaleDownDelaySeconds can only be used with traffic routing" ) func ValidateRollout(rollout *v1alpha1.Rollout) field.ErrorList { @@ -206,8 +208,12 @@ func ValidateRolloutStrategyCanary(rollout *v1alpha1.Rollout, fldPath *field.Pat } if canary.TrafficRouting != nil && canary.TrafficRouting.Istio != nil && len(canary.TrafficRouting.Istio.VirtualService.Routes) == 0 { allErrs = append(allErrs, field.Invalid(fldPath.Child("trafficRouting").Child("istio").Child("virtualService").Child("routes"), "[]", InvalidIstioRoutesMessage)) + } + if canary.ScaleDownDelaySeconds != nil && canary.TrafficRouting == nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("scaleDownDelaySeconds"), *canary.ScaleDownDelaySeconds, InvalidCanaryScaleDownDelay)) } + for i, step := range canary.Steps { stepFldPath := fldPath.Child("steps").Index(i) allErrs = append(allErrs, hasMultipleStepsType(step, stepFldPath)...) diff --git a/pkg/apis/rollouts/validation/validation_test.go b/pkg/apis/rollouts/validation/validation_test.go index 4fd24d0ec2..b43b3531be 100644 --- a/pkg/apis/rollouts/validation/validation_test.go +++ b/pkg/apis/rollouts/validation/validation_test.go @@ -268,3 +268,47 @@ func TestHasMultipleStepsType(t *testing.T) { allErrs = hasMultipleStepsType(step, field.NewPath("")) assert.Equal(t, InvalidStepMessage, allErrs[0].Detail) } + +func TestCanaryScaleDownDelaySeconds(t *testing.T) { + selector := &metav1.LabelSelector{ + MatchLabels: map[string]string{"key": "value"}, + } + ro := &v1alpha1.Rollout{ + Spec: v1alpha1.RolloutSpec{ + Selector: selector, + Strategy: v1alpha1.RolloutStrategy{ + Canary: &v1alpha1.CanaryStrategy{ + StableService: "stable", + CanaryService: "canary", + ScaleDownDelaySeconds: pointer.Int32Ptr(60), + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: selector.MatchLabels, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Resources: corev1.ResourceRequirements{}, + Image: "foo", + Name: "image-name", + }}, + }, + }, + }, + } + t.Run("scaleDownDelaySeconds with basic canary", func(t *testing.T) { + ro := ro.DeepCopy() + allErrs := ValidateRollout(ro) + assert.EqualError(t, allErrs[0], fmt.Sprintf("spec.strategy.scaleDownDelaySeconds: Invalid value: 60: %s", InvalidCanaryScaleDownDelay)) + }) + t.Run("scaleDownDelaySeconds with traffic weight canary", func(t *testing.T) { + ro := ro.DeepCopy() + ro.Spec.Strategy.Canary.TrafficRouting = &v1alpha1.RolloutTrafficRouting{ + SMI: &v1alpha1.SMITrafficRouting{}, + } + allErrs := ValidateRollout(ro) + assert.Empty(t, allErrs) + }) + +} diff --git a/pkg/kubectl-argo-rollouts/info/rollout_info.go b/pkg/kubectl-argo-rollouts/info/rollout_info.go index 7175a265a3..ed0b685a1e 100644 --- a/pkg/kubectl-argo-rollouts/info/rollout_info.go +++ b/pkg/kubectl-argo-rollouts/info/rollout_info.go @@ -173,10 +173,13 @@ func RolloutStatusString(ro *v1alpha1.Rollout) (string, string) { return "Progressing", "waiting for analysis to complete" } } else if ro.Spec.Strategy.Canary != nil { - if ro.Status.Replicas > ro.Status.UpdatedReplicas { - // This check should only be done for canary and not blue-green since blue-green has the - // scaleDownDelay feature which leaves the old stack of replicas running for a long time - return "Progressing", "old replicas are pending termination" + if ro.Spec.Strategy.Canary.TrafficRouting == nil { + if ro.Status.Replicas > ro.Status.UpdatedReplicas { + // This check should only be done for basic canary and not blue-green or canary with traffic routing + // since the latter two have the scaleDownDelay feature which leaves the old stack of replicas + // running for a long time + return "Progressing", "old replicas are pending termination" + } } if ro.Status.StableRS == "" || ro.Status.StableRS != ro.Status.CurrentPodHash { return "Progressing", "waiting for all steps to complete" diff --git a/rollout/analysis_test.go b/rollout/analysis_test.go index ff6b01b0d7..a09c89df1e 100644 --- a/rollout/analysis_test.go +++ b/rollout/analysis_test.go @@ -2035,7 +2035,6 @@ func TestCreatePostPromotionAnalysisRun(t *testing.T) { } ar := analysisRun(at, v1alpha1.RolloutTypePostPromotionLabel, r2) rs1 := newReplicaSetWithStatus(r1, 1, 1) - rs1.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey] = nowFn().Add(10 * time.Second).Format(time.RFC3339) rs2 := newReplicaSetWithStatus(r2, 1, 1) rs1PodHash := rs1.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] rs2PodHash := rs2.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] @@ -2141,7 +2140,6 @@ func TestPostPromotionAnalysisRunHandleInconclusive(t *testing.T) { } rs1 := newReplicaSetWithStatus(r1, 1, 1) - rs1.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey] = nowFn().Add(-10 * time.Second).Format(time.RFC3339) rs2 := newReplicaSetWithStatus(r2, 1, 1) rs1PodHash := rs1.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] rs2PodHash := rs2.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] @@ -2201,7 +2199,6 @@ func TestAbortRolloutOnErrorPostPromotionAnalysis(t *testing.T) { } rs1 := newReplicaSetWithStatus(r1, 1, 1) - rs1.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey] = nowFn().Add(10 * time.Second).Format(time.RFC3339) rs2 := newReplicaSetWithStatus(r2, 1, 1) rs1PodHash := rs1.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] rs2PodHash := rs2.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] diff --git a/rollout/bluegreen.go b/rollout/bluegreen.go index 2b831f95d6..4fb7b2beba 100644 --- a/rollout/bluegreen.go +++ b/rollout/bluegreen.go @@ -1,13 +1,13 @@ package rollout import ( + "math" "sort" "time" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/kubernetes/pkg/controller" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" "github.com/argoproj/argo-rollouts/utils/defaults" @@ -26,11 +26,7 @@ func (c *rolloutContext) rolloutBlueGreen() error { return err } - if c.reconcileBlueGreenTemplateChange() { - c.pauseContext.ClearPauseConditions() - c.pauseContext.RemoveAbort() - c.SetRestartedAt() - c.log.Infof("New pod template or template change detected") + if replicasetutil.CheckPodSpecChange(c.rollout, c.newRS) { return c.syncRolloutStatusBlueGreen(previewSvc, activeSvc) } @@ -69,7 +65,7 @@ func (c *rolloutContext) rolloutBlueGreen() error { return c.syncRolloutStatusBlueGreen(previewSvc, activeSvc) } -func (c *rolloutContext) reconcileStableReplicaSet(activeSvc *corev1.Service) error { +func (c *rolloutContext) reconcileBlueGreenStableReplicaSet(activeSvc *corev1.Service) error { if _, ok := activeSvc.Spec.Selector[v1alpha1.DefaultRolloutUniqueLabelKey]; !ok { return nil } @@ -80,20 +76,16 @@ func (c *rolloutContext) reconcileStableReplicaSet(activeSvc *corev1.Service) er } c.log.Infof("Reconciling stable ReplicaSet '%s'", activeRS.Name) - if replicasetutil.HasScaleDownDeadline(activeRS) { - // SetScaleDownDeadlineAnnotation should be removed from the new RS to ensure a new value is set - // when the active service changes to a different RS - err := c.removeScaleDownDelay(activeRS) - if err != nil { - return err - } - } _, _, err := c.scaleReplicaSetAndRecordEvent(activeRS, defaults.GetReplicasOrDefault(c.rollout.Spec.Replicas)) return err } func (c *rolloutContext) reconcileBlueGreenReplicaSets(activeSvc *corev1.Service) error { - err := c.reconcileStableReplicaSet(activeSvc) + err := c.removeScaleDownDeadlines() + if err != nil { + return err + } + err = c.reconcileBlueGreenStableReplicaSet(activeSvc) if err != nil { return err } @@ -102,25 +94,16 @@ func (c *rolloutContext) reconcileBlueGreenReplicaSets(activeSvc *corev1.Service return err } // Scale down old non-active, non-stable replicasets, if we can. - _, err = c.reconcileOldReplicaSets(controller.FilterActiveReplicaSets(c.otherRSs)) + _, err = c.reconcileOtherReplicaSets() if err != nil { return err } - if err := c.cleanupRollouts(c.otherRSs); err != nil { + if err := c.reconcileRevisionHistoryLimit(c.otherRSs); err != nil { return err } return nil } -// reconcileBlueGreenTemplateChange returns true if we detect there was a change in the pod template -// from our current pod hash, or the newRS does not yet exist -func (c *rolloutContext) reconcileBlueGreenTemplateChange() bool { - if c.newRS == nil { - return true - } - return c.rollout.Status.CurrentPodHash != c.newRS.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] -} - // isBlueGreenFastTracked returns true if we should skip the pause step because update has been fast tracked func (c *rolloutContext) isBlueGreenFastTracked(activeSvc *corev1.Service) bool { if replicasetutil.HasScaleDownDeadline(c.newRS) { @@ -215,7 +198,7 @@ func (c *rolloutContext) scaleDownOldReplicaSetsForBlueGreen(oldRSs []*appsv1.Re c.log.Infof("Cannot scale down old ReplicaSets while paused with inconclusive Analysis ") return false, nil } - if c.rollout.Spec.Strategy.BlueGreen.PostPromotionAnalysis != nil && c.rollout.Spec.Strategy.BlueGreen.ScaleDownDelaySeconds == nil && !skipPostPromotionAnalysisRun(c.rollout, c.newRS) { + if c.rollout.Spec.Strategy.BlueGreen != nil && c.rollout.Spec.Strategy.BlueGreen.PostPromotionAnalysis != nil && c.rollout.Spec.Strategy.BlueGreen.ScaleDownDelaySeconds == nil && !skipPostPromotionAnalysisRun(c.rollout, c.newRS) { currentPostAr := c.currentArs.BlueGreenPostPromotion if currentPostAr == nil || currentPostAr.Status.Phase != v1alpha1.AnalysisPhaseSuccessful { c.log.Infof("Cannot scale down old ReplicaSets while Analysis is running and no ScaleDownDelaySeconds") @@ -239,10 +222,11 @@ func (c *rolloutContext) scaleDownOldReplicaSetsForBlueGreen(oldRSs []*appsv1.Re if scaleDownAtStr, ok := targetRS.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey]; ok { annotationedRSs++ scaleDownAtTime, err := time.Parse(time.RFC3339, scaleDownAtStr) + scaleDownRevisionLimit := getScaleDownRevisionLimit(c.rollout) if err != nil { c.log.Warnf("Unable to read scaleDownAt label on rs '%s'", targetRS.Name) - } else if c.rollout.Spec.Strategy.BlueGreen.ScaleDownDelayRevisionLimit != nil && annotationedRSs > *c.rollout.Spec.Strategy.BlueGreen.ScaleDownDelayRevisionLimit { - c.log.Infof("At ScaleDownDelayRevisionLimit (%d) and scaling down the rest", *c.rollout.Spec.Strategy.BlueGreen.ScaleDownDelayRevisionLimit) + } else if annotationedRSs > scaleDownRevisionLimit { + c.log.Infof("At ScaleDownDelayRevisionLimit (%d) and scaling down the rest", scaleDownRevisionLimit) } else { now := metav1.Now() scaleDownAt := metav1.NewTime(scaleDownAtTime) @@ -271,18 +255,27 @@ func (c *rolloutContext) scaleDownOldReplicaSetsForBlueGreen(oldRSs []*appsv1.Re return hasScaled, nil } +func getScaleDownRevisionLimit(ro *v1alpha1.Rollout) int32 { + if ro.Spec.Strategy.BlueGreen != nil { + if ro.Spec.Strategy.BlueGreen.ScaleDownDelayRevisionLimit != nil { + return *ro.Spec.Strategy.BlueGreen.ScaleDownDelayRevisionLimit + } + } + if ro.Spec.Strategy.Canary != nil { + if ro.Spec.Strategy.Canary.ScaleDownDelayRevisionLimit != nil { + return *ro.Spec.Strategy.Canary.ScaleDownDelayRevisionLimit + } + } + return math.MaxInt32 +} + func (c *rolloutContext) syncRolloutStatusBlueGreen(previewSvc *corev1.Service, activeSvc *corev1.Service) error { newStatus := c.calculateBaseStatus() + newStatus.StableRS = c.rollout.Status.StableRS if replicasetutil.CheckPodSpecChange(c.rollout, c.newRS) { - c.pauseContext.ClearPauseConditions() - c.pauseContext.RemoveAbort() - c.SetRestartedAt() - newStatus.BlueGreen.PrePromotionAnalysisRunStatus = nil - newStatus.BlueGreen.PostPromotionAnalysisRunStatus = nil - newStatus.PromoteFull = false + c.resetRolloutStatus(&newStatus) } - if c.rollout.Status.PromoteFull { c.pauseContext.ClearPauseConditions() c.pauseContext.RemoveAbort() @@ -300,20 +293,10 @@ func (c *rolloutContext) syncRolloutStatusBlueGreen(previewSvc *corev1.Service, } newStatus.BlueGreen.ActiveSelector = activeSelector - newStatus.StableRS = c.rollout.Status.StableRS - if c.shouldUpdateBlueGreenStable(newStatus) { - c.log.Infof("Updating stable RS (%s -> %s)", newStatus.StableRS, newStatus.CurrentPodHash) - newStatus.StableRS = newStatus.CurrentPodHash - newStatus.PromoteFull = false - - // Now that we've marked the current RS as stable, start the scale-down countdown on the previous stable RS - previousStableRS, _ := replicasetutil.GetReplicaSetByTemplateHash(c.olderRSs, c.rollout.Status.StableRS) - if replicasetutil.GetReplicaCountForReplicaSets([]*appsv1.ReplicaSet{previousStableRS}) > 0 { - err := c.addScaleDownDelay(previousStableRS) - if err != nil { - return err - } - } + if reason := c.shouldFullPromote(newStatus); reason != "" { + c.promoteStable(&newStatus, reason) + } else { + newStatus.BlueGreen.ScaleUpPreviewCheckPoint = c.calculateScaleUpPreviewCheckPoint(newStatus) } activeRS, _ := replicasetutil.GetReplicaSetByTemplateHash(c.allRSs, newStatus.BlueGreen.ActiveSelector) @@ -331,75 +314,26 @@ func (c *rolloutContext) syncRolloutStatusBlueGreen(previewSvc *corev1.Service, // newStatus.ReadyReplicas = replicasetutil.GetReadyReplicaCountForReplicaSets(c.allRSs) } - newStatus.BlueGreen.ScaleUpPreviewCheckPoint = c.calculateScaleUpPreviewCheckPoint(newStatus.StableRS) - newStatus = c.calculateRolloutConditions(newStatus) return c.persistRolloutStatus(&newStatus) } -// shouldUpdateBlueGreenStable makes a determination if the current ReplicaSet should be marked as -// the stable ReplicaSet (for a blue-green rollout). This is true if the active selector is -// pointing at the the current RS, and there are no outstanding post-promotion analysis. -func (c *rolloutContext) shouldUpdateBlueGreenStable(newStatus v1alpha1.RolloutStatus) bool { - if c.rollout.Status.StableRS == newStatus.CurrentPodHash { - return false - } - if newStatus.BlueGreen.ActiveSelector == "" { - // corner case - initial deployments won't update the active selector until stable is set. - // We must allow current to be marked stable, so that active can be marked to current, and - // subsequently stable marked to current too. (chicken and egg problem) - return true - } - if newStatus.BlueGreen.ActiveSelector != newStatus.CurrentPodHash { - // haven't service performed cutover yet - return false - } - if newStatus.PromoteFull { - return true - } - if c.rollout.Spec.Strategy.BlueGreen.PostPromotionAnalysis != nil { - // corner case - we fast-track the StableRS to be updated to CurrentPodHash when we are - // moving to a ReplicaSet within scaleDownDelay and wish to skip analysis. - if replicasetutil.HasScaleDownDeadline(c.newRS) { - c.log.Infof("detected rollback to RS '%s' within scaleDownDelay. fast-tracking stable RS to %s", c.newRS.Name, newStatus.CurrentPodHash) - return true - } - currentPostPromotionAnalysisRun := c.currentArs.BlueGreenPostPromotion - if currentPostPromotionAnalysisRun == nil || currentPostPromotionAnalysisRun.Status.Phase != v1alpha1.AnalysisPhaseSuccessful { - // we have yet to start post-promotion analysis or post-promotion was not successful - return false - } - } - return true -} - // calculateScaleUpPreviewCheckPoint calculates the correct value of status.blueGreen.scaleUpPreviewCheckPoint // which is used by the blueGreen.previewReplicaCount feature. scaleUpPreviewCheckPoint is a single // direction trip-wire, initialized to false, and gets flipped true as soon as the preview replicas // matches scaleUpPreviewCheckPoint and prePromotionAnalysis (if used) completes. It get reset to // false when the pod template changes, or the rollout fully promotes (stableRS == newRS) -func (c *rolloutContext) calculateScaleUpPreviewCheckPoint(stableRSHash string) bool { - prevValue := c.rollout.Status.BlueGreen.ScaleUpPreviewCheckPoint +func (c *rolloutContext) calculateScaleUpPreviewCheckPoint(newStatus v1alpha1.RolloutStatus) bool { if c.rollout.Spec.Strategy.BlueGreen.PreviewReplicaCount == nil { // previewReplicaCount feature is not being used return false } - if c.rollout.Status.Abort && c.reconcileBlueGreenTemplateChange() { - if prevValue { - c.log.Infof("resetting scaleUpPreviewCheckPoint: post-abort template change detected") - } - return false - } - if c.newRS == nil || stableRSHash == "" || stableRSHash == replicasetutil.GetPodTemplateHash(c.newRS) { - if prevValue { - c.log.Infof("resetting scaleUpPreviewCheckPoint: rollout fully promoted") - } - return false - } + // Once the ScaleUpPreviewCheckPoint is set to true, the rollout should keep that value until // the newRS becomes the new stableRS or there is a template change. - if c.rollout.Status.BlueGreen.ScaleUpPreviewCheckPoint { - return c.rollout.Status.BlueGreen.ScaleUpPreviewCheckPoint + prevValue := c.rollout.Status.BlueGreen.ScaleUpPreviewCheckPoint + if prevValue { + return true } if !c.completedPrePromotionAnalysis() || !c.pauseContext.CompletedBlueGreenPause() { // do not set the checkpoint unless prePromotionAnalysis was successful and we completed our pause diff --git a/rollout/bluegreen_test.go b/rollout/bluegreen_test.go index 64a8c8d02b..894a951556 100644 --- a/rollout/bluegreen_test.go +++ b/rollout/bluegreen_test.go @@ -891,8 +891,6 @@ func TestPreviewReplicaCountHandleScaleUpPreviewCheckPoint(t *testing.T) { r1.Spec.Strategy.BlueGreen.PreviewReplicaCount = pointer.Int32Ptr(3) r1.Spec.Strategy.BlueGreen.AutoPromotionEnabled = pointer.BoolPtr(false) rs1 := newReplicaSetWithStatus(r1, 5, 5) - now := metav1.Now().Add(10 * time.Second) - rs1.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey] = now.UTC().Format(time.RFC3339) r2 := bumpVersion(r1) rs2 := newReplicaSetWithStatus(r2, 5, 5) @@ -1171,7 +1169,7 @@ func TestFastRollback(t *testing.T) { assert.Equal(t, expectedPatch, patch) } -func TestScaleDownLimit(t *testing.T) { +func TestBlueGreenScaleDownLimit(t *testing.T) { f := newFixture(t) defer f.Close() @@ -1229,10 +1227,6 @@ func TestBlueGreenAbort(t *testing.T) { rs2PodHash := rs2.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] rs1PodHash := rs1.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] - //Setting the scaleDownAt time - inTheFuture := metav1.Now().Add(10 * time.Second).UTC().Format(time.RFC3339) - rs1.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey] = inTheFuture - serviceSelector := map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: rs2PodHash} s := newService("bar", 80, serviceSelector, r2) f.kubeobjects = append(f.kubeobjects, s, rs1, rs2) diff --git a/rollout/canary.go b/rollout/canary.go index cd48d7674f..2d116d11ed 100644 --- a/rollout/canary.go +++ b/rollout/canary.go @@ -1,13 +1,12 @@ package rollout import ( - "fmt" "sort" + "time" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/kubernetes/pkg/controller" "k8s.io/utils/pointer" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" @@ -41,7 +40,7 @@ func (c *rolloutContext) rolloutCanary() error { return err } - if err := c.cleanupRollouts(c.otherRSs); err != nil { + if err := c.reconcileRevisionHistoryLimit(c.otherRSs); err != nil { return err } @@ -85,8 +84,10 @@ func (c *rolloutContext) rolloutCanary() error { return c.syncRolloutStatusCanary() } -func (c *rolloutContext) reconcileStableRS() (bool, error) { +func (c *rolloutContext) reconcileCanaryStableReplicaSet() (bool, error) { if !replicasetutil.CheckStableRSExists(c.newRS, c.stableRS) { + // we skip this because if they are equal, then it will get reconciled in reconcileNewReplicaSet() + // making this redundant c.log.Info("No StableRS exists to reconcile or matches newRS") return false, nil } @@ -137,33 +138,15 @@ func (c *rolloutContext) reconcileCanaryPause() bool { return true } -func (c *rolloutContext) reconcileOldReplicaSetsCanary(allRSs []*appsv1.ReplicaSet, oldRSs []*appsv1.ReplicaSet) (bool, error) { - oldPodsCount := replicasetutil.GetReplicaCountForReplicaSets(oldRSs) - if oldPodsCount == 0 { - // Can't scale down further - return false, nil - } - +// scaleDownOldReplicaSetsForCanary scales down old replica sets when rollout strategy is "canary". +func (c *rolloutContext) scaleDownOldReplicaSetsForCanary(oldRSs []*appsv1.ReplicaSet) (int32, error) { // Clean up unhealthy replicas first, otherwise unhealthy replicas will block rollout // and cause timeout. See https://github.com/kubernetes/kubernetes/issues/16737 - oldRSs, cleanedUpRSs, err := c.cleanupUnhealthyReplicas(oldRSs) - if err != nil { - return false, nil - } - - // Scale down old replica sets, need check replicasToKeep to ensure we can scale down - scaledDownCount, err := c.scaleDownOldReplicaSetsForCanary(allRSs, oldRSs) + oldRSs, totalScaledDown, err := c.cleanupUnhealthyReplicas(oldRSs) if err != nil { - return false, nil + return totalScaledDown, nil } - c.log.Infof("Scaled down old RSes by %d", scaledDownCount) - - return cleanedUpRSs || scaledDownCount > 0, nil -} - -// scaleDownOldReplicaSetsForCanary scales down old replica sets when rollout strategy is "canary". -func (c *rolloutContext) scaleDownOldReplicaSetsForCanary(allRSs []*appsv1.ReplicaSet, oldRSs []*appsv1.ReplicaSet) (int32, error) { - availablePodCount := replicasetutil.GetAvailableReplicaCountForReplicaSets(allRSs) + availablePodCount := replicasetutil.GetAvailableReplicaCountForReplicaSets(c.allRSs) minAvailable := defaults.GetReplicasOrDefault(c.rollout.Spec.Replicas) - replicasetutil.MaxUnavailable(c.rollout) maxScaleDown := availablePodCount - minAvailable if maxScaleDown <= 0 { @@ -172,10 +155,17 @@ func (c *rolloutContext) scaleDownOldReplicaSetsForCanary(allRSs []*appsv1.Repli } c.log.Infof("Found %d available pods, scaling down old RSes (minAvailable: %d, maxScaleDown: %d)", availablePodCount, minAvailable, maxScaleDown) - sort.Sort(controller.ReplicaSetsByCreationTimestamp(oldRSs)) + sort.Sort(sort.Reverse(replicasetutil.ReplicaSetsByRevisionNumber(oldRSs))) - totalScaledDown := int32(0) + annotationedRSs := int32(0) + rolloutReplicas := defaults.GetReplicasOrDefault(c.rollout.Spec.Replicas) for _, targetRS := range oldRSs { + if replicasetutil.IsStillReferenced(c.rollout.Status, targetRS) { + // We should technically never get here because we shouldn't be passing a replicaset list + // which includes referenced ReplicaSets. But we check just in case + c.log.Warnf("Prevented inadvertent scaleDown of RS '%s'", targetRS.Name) + continue + } if maxScaleDown <= 0 { break } @@ -183,16 +173,48 @@ func (c *rolloutContext) scaleDownOldReplicaSetsForCanary(allRSs []*appsv1.Repli // cannot scale down this ReplicaSet. continue } - // Scale down. - newReplicasCount := int32(0) - if *(targetRS.Spec.Replicas) > maxScaleDown { - newReplicasCount = *(targetRS.Spec.Replicas) - maxScaleDown + desiredReplicaCount := int32(0) + if c.rollout.Spec.Strategy.Canary.TrafficRouting == nil { + // For basic canary, we must scale down all other ReplicaSets because existence of + // those pods will cause traffic to be served by them + if *(targetRS.Spec.Replicas) > maxScaleDown { + desiredReplicaCount = *(targetRS.Spec.Replicas) - maxScaleDown + } + } else { + // For traffic shaped canary, we leave the old ReplicaSets up until scaleDownDelaySeconds + if scaleDownAtStr, ok := targetRS.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey]; ok { + annotationedRSs++ + scaleDownAtTime, err := time.Parse(time.RFC3339, scaleDownAtStr) + scaleDownRevisionLimit := getScaleDownRevisionLimit(c.rollout) + if err != nil { + c.log.Warnf("Unable to read scaleDownAt label on rs '%s'", targetRS.Name) + } else if annotationedRSs > scaleDownRevisionLimit { + c.log.Infof("At ScaleDownDelayRevisionLimit (%d) and scaling down the rest", scaleDownRevisionLimit) + } else { + now := metav1.Now() + scaleDownAt := metav1.NewTime(scaleDownAtTime) + if scaleDownAt.After(now.Time) { + c.log.Infof("RS '%s' has not reached the scaleDownTime", targetRS.Name) + remainingTime := scaleDownAt.Sub(now.Time) + if remainingTime < c.resyncPeriod { + c.enqueueRolloutAfter(c.rollout, remainingTime) + } + desiredReplicaCount = rolloutReplicas + } + } + } + + } + if *(targetRS.Spec.Replicas) == desiredReplicaCount { + // at desired account + continue } - _, _, err := c.scaleReplicaSetAndRecordEvent(targetRS, newReplicasCount) + // Scale down. + _, _, err := c.scaleReplicaSetAndRecordEvent(targetRS, desiredReplicaCount) if err != nil { return totalScaledDown, err } - scaleDownCount := *targetRS.Spec.Replicas - newReplicasCount + scaleDownCount := *targetRS.Spec.Replicas - desiredReplicaCount maxScaleDown -= scaleDownCount totalScaledDown += scaleDownCount } @@ -244,37 +266,16 @@ func (c *rolloutContext) syncRolloutStatusCanary() error { stepCount := int32(len(c.rollout.Spec.Strategy.Canary.Steps)) if replicasetutil.PodTemplateOrStepsChanged(c.rollout, c.newRS) { - newStatus.CurrentStepIndex = replicasetutil.ResetCurrentStepIndex(c.rollout) + c.resetRolloutStatus(&newStatus) if c.newRS != nil && c.rollout.Status.StableRS == replicasetutil.GetPodTemplateHash(c.newRS) { - if newStatus.CurrentStepIndex != nil { - msg := "Skipping all steps because the newRS is the stableRS." + if stepCount > 0 { + // If we get here, we detected that we've moved back to the stable ReplicaSet + msg := "Rollback to stable" c.log.Info(msg) - newStatus.CurrentStepIndex = pointer.Int32Ptr(stepCount) c.recorder.Event(c.rollout, corev1.EventTypeNormal, "SkipSteps", msg) + newStatus.CurrentStepIndex = &stepCount } } - c.pauseContext.ClearPauseConditions() - c.pauseContext.RemoveAbort() - c.SetRestartedAt() - newStatus = c.calculateRolloutConditions(newStatus) - newStatus.Canary.CurrentStepAnalysisRunStatus = nil - newStatus.Canary.CurrentBackgroundAnalysisRunStatus = nil - newStatus.PromoteFull = false - return c.persistRolloutStatus(&newStatus) - } - - if c.stableRS == nil { - msg := fmt.Sprintf("Setting StableRS to CurrentPodHash: StableRS hash: %s", newStatus.CurrentPodHash) - c.log.Info(msg) - newStatus.StableRS = newStatus.CurrentPodHash - if stepCount > 0 { - if stepCount != *currentStepIndex { - c.recorder.Event(c.rollout, corev1.EventTypeNormal, "SettingStableRS", msg) - } - newStatus.CurrentStepIndex = &stepCount - } - c.pauseContext.ClearPauseConditions() - c.pauseContext.RemoveAbort() newStatus = c.calculateRolloutConditions(newStatus) return c.persistRolloutStatus(&newStatus) } @@ -283,10 +284,19 @@ func (c *rolloutContext) syncRolloutStatusCanary() error { c.pauseContext.ClearPauseConditions() c.pauseContext.RemoveAbort() if stepCount > 0 { - currentStepIndex = pointer.Int32Ptr(stepCount) + currentStepIndex = &stepCount } } + if reason := c.shouldFullPromote(newStatus); reason != "" { + err := c.promoteStable(&newStatus, reason) + if err != nil { + return err + } + newStatus = c.calculateRolloutConditions(newStatus) + return c.persistRolloutStatus(&newStatus) + } + if c.pauseContext.IsAborted() { if stepCount > int32(0) { if newStatus.StableRS == newStatus.CurrentPodHash { @@ -299,34 +309,12 @@ func (c *rolloutContext) syncRolloutStatusCanary() error { return c.persistRolloutStatus(&newStatus) } - if stepCount == 0 || (currentStepIndex != nil && *currentStepIndex == stepCount) { - c.log.Infof("Rollout has executed all %d steps", stepCount) - if stepCount > 0 { - newStatus.CurrentStepIndex = &stepCount - } - if c.newRS != nil && c.newRS.Status.AvailableReplicas == defaults.GetReplicasOrDefault(c.rollout.Spec.Replicas) { - c.log.Info("New RS has successfully progressed") - newStatus.StableRS = newStatus.CurrentPodHash - newStatus.PromoteFull = false - } - c.pauseContext.ClearPauseConditions() - newStatus = c.calculateRolloutConditions(newStatus) - return c.persistRolloutStatus(&newStatus) - } - - completedStep := c.completedCurrentCanaryStep() - if completedStep { + if c.completedCurrentCanaryStep() { *currentStepIndex++ - newStatus.CurrentStepIndex = currentStepIndex newStatus.Canary.CurrentStepAnalysisRunStatus = nil - if int(*currentStepIndex) == len(c.rollout.Spec.Strategy.Canary.Steps) { - c.recorder.Event(c.rollout, corev1.EventTypeNormal, "SettingStableRS", "Completed all steps") - } c.log.Infof("Incrementing the Current Step Index to %d", *currentStepIndex) c.recorder.Eventf(c.rollout, corev1.EventTypeNormal, "SetStepIndex", "Set Step Index to %d", int(*currentStepIndex)) c.pauseContext.RemovePauseCondition(v1alpha1.PauseReasonCanaryPauseStep) - newStatus = c.calculateRolloutConditions(newStatus) - return c.persistRolloutStatus(&newStatus) } newStatus.CurrentStepIndex = currentStepIndex @@ -335,8 +323,11 @@ func (c *rolloutContext) syncRolloutStatusCanary() error { } func (c *rolloutContext) reconcileCanaryReplicaSets() (bool, error) { - c.log.Info("Reconciling StableRS") - scaledStableRS, err := c.reconcileStableRS() + err := c.removeScaleDownDeadlines() + if err != nil { + return false, err + } + scaledStableRS, err := c.reconcileCanaryStableReplicaSet() if err != nil { return false, err } @@ -354,13 +345,12 @@ func (c *rolloutContext) reconcileCanaryReplicaSets() (bool, error) { return true, nil } - c.log.Info("Reconciling old replica sets") - scaledDown, err := c.reconcileOldReplicaSetsCanary(c.allRSs, controller.FilterActiveReplicaSets(c.otherRSs)) + scaledDown, err := c.reconcileOtherReplicaSets() if err != nil { return false, err } if scaledDown { - c.log.Info("Not finished reconciling old replica sets") + c.log.Info("Not finished reconciling old ReplicaSets") return true, nil } return false, nil diff --git a/rollout/canary_test.go b/rollout/canary_test.go index 6ea1fdda1a..5eb37c563b 100644 --- a/rollout/canary_test.go +++ b/rollout/canary_test.go @@ -13,14 +13,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/util/intstr" + k8sinformers "k8s.io/client-go/informers" k8sfake "k8s.io/client-go/kubernetes/fake" "k8s.io/kubernetes/pkg/controller" "k8s.io/utils/pointer" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned/fake" - k8sinformers "k8s.io/client-go/informers" - "github.com/argoproj/argo-rollouts/utils/annotations" "github.com/argoproj/argo-rollouts/utils/conditions" logutil "github.com/argoproj/argo-rollouts/utils/log" @@ -1349,11 +1348,13 @@ func TestNoResumeAfterPauseDurationIfUserPaused(t *testing.T) { }, } r1 := newCanaryRollout("foo", 1, nil, steps, pointer.Int32Ptr(1), intstr.FromInt(1), intstr.FromInt(1)) - rs1 := newReplicaSetWithStatus(r1, 1, 1) + r2 := bumpVersion(r1) + rs1 := newReplicaSetWithStatus(r1, 0, 0) rs1PodHash := rs1.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] - r1 = updateCanaryRolloutStatus(r1, rs1PodHash, 1, 1, 1, true) + rs2 := newReplicaSetWithStatus(r2, 1, 1) + r2 = updateCanaryRolloutStatus(r2, rs1PodHash, 1, 1, 1, true) overAMinuteAgo := metav1.Time{Time: time.Now().Add(-63 * time.Second)} - r1.Status.PauseConditions = []v1alpha1.PauseCondition{{ + r2.Status.PauseConditions = []v1alpha1.PauseCondition{{ Reason: v1alpha1.PauseReasonCanaryPauseStep, StartTime: overAMinuteAgo, }} @@ -1361,16 +1362,17 @@ func TestNoResumeAfterPauseDurationIfUserPaused(t *testing.T) { conditions.SetRolloutCondition(&r1.Status, progressingCondition) pausedCondition, _ := newPausedCondition(true) conditions.SetRolloutCondition(&r1.Status, pausedCondition) - r1.Spec.Paused = true - f.kubeobjects = append(f.kubeobjects, rs1) - f.replicaSetLister = append(f.replicaSetLister, rs1) - f.rolloutLister = append(f.rolloutLister, r1) - f.objects = append(f.objects, r1) + r2.Spec.Paused = true + f.kubeobjects = append(f.kubeobjects, rs1, rs2) + f.replicaSetLister = append(f.replicaSetLister, rs1, rs2) + f.rolloutLister = append(f.rolloutLister, r2) + f.objects = append(f.objects, r2) - patchIndex := f.expectPatchRolloutAction(r1) - f.run(getKey(r1, t)) + _ = f.expectPatchRolloutAction(r2) // this just sets a conditions. ignore for now + patchIndex := f.expectPatchRolloutAction(r2) + f.run(getKey(r2, t)) patch := f.getPatchedRollout(patchIndex) - assert.Equal(t, calculatePatch(r1, OnlyObservedGenerationPatch), patch) + assert.Equal(t, calculatePatch(r2, OnlyObservedGenerationPatch), patch) } func TestHandleNilNewRSOnScaleAndImageChange(t *testing.T) { diff --git a/rollout/replicaset.go b/rollout/replicaset.go index 4f9ccdcc03..d3d30f0718 100644 --- a/rollout/replicaset.go +++ b/rollout/replicaset.go @@ -24,21 +24,42 @@ const ( removeScaleDownAtAnnotationsPatch = `[{ "op": "remove", "path": "/metadata/annotations/%s"}]` ) +// removeScaleDownDelay removes the `scale-down-deadline` annotation from the ReplicaSet (if it exists) func (c *rolloutContext) removeScaleDownDelay(rs *appsv1.ReplicaSet) error { ctx := context.TODO() - c.log.Infof("Removing '%s' annotation on RS '%s'", v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey, rs.Name) + if !replicasetutil.HasScaleDownDeadline(rs) { + return nil + } patch := fmt.Sprintf(removeScaleDownAtAnnotationsPatch, v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey) _, err := c.kubeclientset.AppsV1().ReplicaSets(rs.Namespace).Patch(ctx, rs.Name, patchtypes.JSONPatchType, []byte(patch), metav1.PatchOptions{}) + if err == nil { + c.log.Infof("Removed '%s' annotation from RS '%s'", v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey, rs.Name) + } return err } +// addScaleDownDelay injects the `scale-down-deadline` annotation to the ReplicaSet, or if +// scaleDownDelaySeconds is zero, removes it if it exists func (c *rolloutContext) addScaleDownDelay(rs *appsv1.ReplicaSet) error { + if rs == nil { + return nil + } ctx := context.TODO() - c.log.Infof("Adding '%s' annotation to RS '%s'", v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey, rs.Name) scaleDownDelaySeconds := time.Duration(defaults.GetScaleDownDelaySecondsOrDefault(c.rollout)) - now := metav1.Now().Add(scaleDownDelaySeconds * time.Second).UTC().Format(time.RFC3339) - patch := fmt.Sprintf(addScaleDownAtAnnotationsPatch, v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey, now) + if scaleDownDelaySeconds == 0 { + // If scaledown deadline is zero, it means we need to remove any replicasets with the delay + // This might happen if we switch from canary with traffic routing to basic canary + if replicasetutil.HasScaleDownDeadline(rs) { + return c.removeScaleDownDelay(rs) + } + return nil + } + deadline := metav1.Now().Add(scaleDownDelaySeconds * time.Second).UTC().Format(time.RFC3339) + patch := fmt.Sprintf(addScaleDownAtAnnotationsPatch, v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey, deadline) _, err := c.kubeclientset.AppsV1().ReplicaSets(rs.Namespace).Patch(ctx, rs.Name, patchtypes.JSONPatchType, []byte(patch), metav1.PatchOptions{}) + if err == nil { + c.log.Infof("Set '%s' annotation on '%s' to %s (%ds)", v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey, rs.Name, deadline, scaleDownDelaySeconds) + } return err } @@ -70,11 +91,31 @@ func (c *Controller) getReplicaSetsForRollouts(r *v1alpha1.Rollout) ([]*appsv1.R return cm.ClaimReplicaSets(rsList) } +// removeScaleDownDelays removes the scale-down-deadline annotation from the new/stable ReplicaSets, +// in the event that we moved back to an older revision that is still within its scaleDownDelay. +func (c *rolloutContext) removeScaleDownDeadlines() error { + var toRemove []*appsv1.ReplicaSet + if c.newRS != nil { + toRemove = append(toRemove, c.newRS) + } + if c.stableRS != nil { + if len(toRemove) == 0 || c.stableRS.Name != c.newRS.Name { + toRemove = append(toRemove, c.stableRS) + } + } + for _, rs := range toRemove { + err := c.removeScaleDownDelay(rs) + if err != nil { + return err + } + } + return nil +} + func (c *rolloutContext) reconcileNewReplicaSet() (bool, error) { if c.newRS == nil { return false, nil } - c.log.Infof("Reconciling new ReplicaSet '%s'", c.newRS.Name) newReplicasCount, err := replicasetutil.NewRSNewReplicas(c.rollout, c.allRSs, c.newRS) if err != nil { return false, err @@ -83,38 +124,44 @@ func (c *rolloutContext) reconcileNewReplicaSet() (bool, error) { return scaled, err } -func (c *rolloutContext) reconcileOldReplicaSets(oldRSs []*appsv1.ReplicaSet) (bool, error) { - oldPodsCount := replicasetutil.GetReplicaCountForReplicaSets(oldRSs) +// reconcileOtherReplicaSets reconciles "other" ReplicaSets. +// Other ReplicaSets are ReplicaSets are neither the new or stable (allRSs - newRS - stableRS) +func (c *rolloutContext) reconcileOtherReplicaSets() (bool, error) { + otherRSs := controller.FilterActiveReplicaSets(c.otherRSs) + oldPodsCount := replicasetutil.GetReplicaCountForReplicaSets(otherRSs) if oldPodsCount == 0 { // Can't scale down further return false, nil } - c.log.Infof("Reconciling old replica sets (count: %d)", oldPodsCount) + c.log.Infof("Reconciling %d old ReplicaSets (total pods: %d)", len(otherRSs), oldPodsCount) var err error hasScaled := false if c.rollout.Spec.Strategy.Canary != nil { - // Clean up unhealthy replicas first, otherwise unhealthy replicas will block rollout - // and cause timeout. See https://github.com/kubernetes/kubernetes/issues/16737 - oldRSs, hasScaled, err = c.cleanupUnhealthyReplicas(oldRSs) + // Scale down old replica sets, need check replicasToKeep to ensure we can scale down + scaledDownCount, err := c.scaleDownOldReplicaSetsForCanary(otherRSs) if err != nil { return false, nil } + //hasScaled = hasScaled || scaledDownCount > 0 + hasScaled = scaledDownCount > 0 } // Scale down old replica sets if c.rollout.Spec.Strategy.BlueGreen != nil { - hasScaled, err = c.scaleDownOldReplicaSetsForBlueGreen(oldRSs) + hasScaled, err = c.scaleDownOldReplicaSetsForBlueGreen(otherRSs) if err != nil { return false, nil } } - + if hasScaled { + c.log.Infof("Scaled down old RSes") + } return hasScaled, nil } // cleanupUnhealthyReplicas will scale down old replica sets with unhealthy replicas, so that all unhealthy replicas will be deleted. -func (c *rolloutContext) cleanupUnhealthyReplicas(oldRSs []*appsv1.ReplicaSet) ([]*appsv1.ReplicaSet, bool, error) { +func (c *rolloutContext) cleanupUnhealthyReplicas(oldRSs []*appsv1.ReplicaSet) ([]*appsv1.ReplicaSet, int32, error) { sort.Sort(controller.ReplicaSetsByCreationTimestamp(oldRSs)) // Safely scale down all old replica sets with unhealthy replicas. Replica set will sort the pods in the order // such that not-ready < ready, unscheduled < scheduled, and pending < running. This ensures that unhealthy replicas will @@ -134,14 +181,14 @@ func (c *rolloutContext) cleanupUnhealthyReplicas(oldRSs []*appsv1.ReplicaSet) ( scaledDownCount := *(targetRS.Spec.Replicas) - targetRS.Status.AvailableReplicas newReplicasCount := targetRS.Status.AvailableReplicas if newReplicasCount > *(targetRS.Spec.Replicas) { - return nil, false, fmt.Errorf("when cleaning up unhealthy replicas, got invalid request to scale down %s/%s %d -> %d", targetRS.Namespace, targetRS.Name, *(targetRS.Spec.Replicas), newReplicasCount) + return nil, 0, fmt.Errorf("when cleaning up unhealthy replicas, got invalid request to scale down %s/%s %d -> %d", targetRS.Namespace, targetRS.Name, *(targetRS.Spec.Replicas), newReplicasCount) } _, updatedOldRS, err := c.scaleReplicaSetAndRecordEvent(targetRS, newReplicasCount) if err != nil { - return nil, totalScaledDown > 0, err + return nil, totalScaledDown, err } totalScaledDown += scaledDownCount oldRSs[i] = updatedOldRS } - return oldRSs, totalScaledDown > 0, nil + return oldRSs, totalScaledDown, nil } diff --git a/rollout/replicaset_test.go b/rollout/replicaset_test.go index 4a50f2d152..4327419547 100644 --- a/rollout/replicaset_test.go +++ b/rollout/replicaset_test.go @@ -245,7 +245,6 @@ func TestReconcileOldReplicaSet(t *testing.T) { oldRS := rs("foo-old", test.oldReplicas, oldSelector, noTimestamp, nil) oldRS.Annotations = map[string]string{annotations.DesiredReplicasAnnotation: strconv.Itoa(test.oldReplicas)} oldRS.Status.AvailableReplicas = int32(test.readyPodsFromOldRS) - oldRSs := []*appsv1.ReplicaSet{oldRS} rollout := newBlueGreenRollout("foo", test.rolloutReplicas, nil, "", "") rollout.Spec.Selector = &metav1.LabelSelector{MatchLabels: newSelector} f := newFixture(t) @@ -260,7 +259,8 @@ func TestReconcileOldReplicaSet(t *testing.T) { close(stopCh) roCtx, err := c.newRolloutContext(rollout) assert.NoError(t, err) - scaled, err := roCtx.reconcileOldReplicaSets(oldRSs) + roCtx.otherRSs = []*appsv1.ReplicaSet{oldRS} + scaled, err := roCtx.reconcileOtherReplicaSets() if err != nil { t.Errorf("unexpected error: %v", err) return diff --git a/rollout/sync.go b/rollout/sync.go index 7f2a8337aa..0d6768e306 100644 --- a/rollout/sync.go +++ b/rollout/sync.go @@ -423,10 +423,10 @@ func (c *rolloutContext) calculateBaseStatus() v1alpha1.RolloutStatus { return newStatus } -// cleanupRollout is responsible for cleaning up a rollout ie. retains all but the latest N old replica sets +// reconcileRevisionHistoryLimit is responsible for cleaning up a rollout ie. retains all but the latest N old replica sets // where N=r.Spec.RevisionHistoryLimit. Old replica sets are older versions of the podtemplate of a rollout kept // around by default 1) for historical reasons. -func (c *rolloutContext) cleanupRollouts(oldRSs []*appsv1.ReplicaSet) error { +func (c *rolloutContext) reconcileRevisionHistoryLimit(oldRSs []*appsv1.ReplicaSet) error { ctx := context.TODO() revHistoryLimit := defaults.GetRevisionHistoryLimitOrDefault(c.rollout) @@ -763,3 +763,107 @@ func (c *rolloutContext) getReplicaFailures(allRSs []*appsv1.ReplicaSet, newRS * } return errorConditions } + +// resetRolloutStatus will reset the rollout status as if it is in a beginning of a new update +func (c *rolloutContext) resetRolloutStatus(newStatus *v1alpha1.RolloutStatus) { + c.pauseContext.ClearPauseConditions() + c.pauseContext.RemoveAbort() + c.SetRestartedAt() + newStatus.PromoteFull = false + newStatus.BlueGreen.PrePromotionAnalysisRunStatus = nil + newStatus.BlueGreen.PostPromotionAnalysisRunStatus = nil + newStatus.BlueGreen.ScaleUpPreviewCheckPoint = false + newStatus.Canary.CurrentStepAnalysisRunStatus = nil + newStatus.Canary.CurrentBackgroundAnalysisRunStatus = nil + newStatus.CurrentStepIndex = replicasetutil.ResetCurrentStepIndex(c.rollout) +} + +// shouldFullPromote returns a reason string explaining why a rollout should fully promote, marking +// the desired ReplicaSet as stable. Returns empty string if the rollout is in middle of update +func (c *rolloutContext) shouldFullPromote(newStatus v1alpha1.RolloutStatus) string { + // NOTE: the order of these checks are significant + if c.stableRS == nil { + return "Initial deploy" + } else if c.rollout.Spec.Strategy.Canary != nil { + if c.pauseContext.IsAborted() { + return "" + } + if c.newRS == nil || c.newRS.Status.AvailableReplicas != defaults.GetReplicasOrDefault(c.rollout.Spec.Replicas) { + return "" + } + if c.rollout.Status.PromoteFull { + return "Full promotion requested" + } + _, currentStepIndex := replicasetutil.GetCurrentCanaryStep(c.rollout) + stepCount := len(c.rollout.Spec.Strategy.Canary.Steps) + completedAllSteps := stepCount == 0 || (currentStepIndex != nil && *currentStepIndex == int32(stepCount)) + if completedAllSteps { + return fmt.Sprintf("Completed all %d canary steps", stepCount) + } + } else if c.rollout.Spec.Strategy.BlueGreen != nil { + if newStatus.BlueGreen.ActiveSelector == "" { + // corner case - initial deployments won't update the active selector until stable is set. + // We must allow current to be marked stable, so that active can be marked to current, and + // subsequently stable marked to current too. (chicken and egg problem) + return "Initial deploy" + } + if newStatus.BlueGreen.ActiveSelector != newStatus.CurrentPodHash { + // active selector still pointing to previous RS, don't update stable yet + return "" + } + if c.rollout.Status.PromoteFull { + return "Full promotion requested" + } + if c.pauseContext.IsAborted() { + return "" + } + if c.rollout.Spec.Strategy.BlueGreen.PostPromotionAnalysis != nil { + // corner case - we fast-track the StableRS to be updated to CurrentPodHash when we are + // moving to a ReplicaSet within scaleDownDelay and wish to skip analysis. + if replicasetutil.HasScaleDownDeadline(c.newRS) { + return fmt.Sprintf("Rollback to '%s' within scaleDownDelay", c.newRS.Name) + } + currentPostPromotionAnalysisRun := c.currentArs.BlueGreenPostPromotion + if currentPostPromotionAnalysisRun == nil || currentPostPromotionAnalysisRun.Status.Phase != v1alpha1.AnalysisPhaseSuccessful { + // we have yet to start post-promotion analysis or post-promotion was not successful + return "" + } + } + return "Completed blue-green update" + } + return "" +} + +// promoteStable will take appropriate action once we have promoted the current ReplicaSet as stable +// e.g. reset status conditions, emit Kubernetes events, start scaleDownDelay, etc... +func (c *rolloutContext) promoteStable(newStatus *v1alpha1.RolloutStatus, reason string) error { + c.pauseContext.ClearPauseConditions() + c.pauseContext.RemoveAbort() + newStatus.PromoteFull = false + newStatus.BlueGreen.ScaleUpPreviewCheckPoint = false + if c.rollout.Spec.Strategy.Canary != nil { + stepCount := int32(len(c.rollout.Spec.Strategy.Canary.Steps)) + if stepCount > 0 { + newStatus.CurrentStepIndex = &stepCount + } else { + newStatus.CurrentStepIndex = nil + } + } + previousStableHash := newStatus.StableRS + if previousStableHash != newStatus.CurrentPodHash { + // only emit this event when we switched stable + newStatus.StableRS = newStatus.CurrentPodHash + msg := fmt.Sprintf("Set stable to revision %s (%s): %s", c.rollout.Annotations[annotations.RevisionAnnotation], newStatus.CurrentPodHash, reason) + c.log.Info(msg) + c.recorder.Event(c.rollout, corev1.EventTypeNormal, "SetStable", msg) + // Now that we've marked the desired RS as stable, start the scale-down countdown on the previous stable RS + previousStableRS, _ := replicasetutil.GetReplicaSetByTemplateHash(c.olderRSs, previousStableHash) + if replicasetutil.GetReplicaCountForReplicaSets([]*appsv1.ReplicaSet{previousStableRS}) > 0 { + err := c.addScaleDownDelay(previousStableRS) + if err != nil { + return err + } + } + } + return nil +} diff --git a/rollout/sync_test.go b/rollout/sync_test.go index 423798ce8f..705bcc452c 100644 --- a/rollout/sync_test.go +++ b/rollout/sync_test.go @@ -44,7 +44,7 @@ func rs(name string, replicas int, selector map[string]string, timestamp metav1. } } -func TestCleanupRollouts(t *testing.T) { +func TestReconcileRevisionHistoryLimit(t *testing.T) { now := metav1.Now() before := metav1.Time{Time: now.Add(-time.Minute)} @@ -203,7 +203,7 @@ func TestCleanupRollouts(t *testing.T) { recorder: &record.FakeRecorder{}, }, } - err := roCtx.cleanupRollouts(test.replicaSets) + err := roCtx.reconcileRevisionHistoryLimit(test.replicaSets) assert.Nil(t, err) assert.Equal(t, len(test.expectedDeleted), len(k8sfake.Actions())) for _, action := range k8sfake.Actions() { diff --git a/rollout/trafficrouting/istio/istio.go b/rollout/trafficrouting/istio/istio.go index ef18c60f18..438ab7f746 100644 --- a/rollout/trafficrouting/istio/istio.go +++ b/rollout/trafficrouting/istio/istio.go @@ -209,7 +209,9 @@ func (r *Reconciler) UpdateHash(canaryHash, stableHash string) error { return err } if modified { - r.log.Infof("DestinationRule %s subset updated (%s: %s, %s: %s)", dRuleSpec.Name, dRuleSpec.CanarySubsetName, canaryHash, dRuleSpec.StableSubsetName, stableHash) + msg := fmt.Sprintf("DestinationRule %s subset updated (%s: %s, %s: %s)", dRuleSpec.Name, dRuleSpec.CanarySubsetName, canaryHash, dRuleSpec.StableSubsetName, stableHash) + r.log.Info(msg) + r.recorder.Event(r.rollout, corev1.EventTypeNormal, "UpdatedDestinationRule", msg) } return nil } @@ -331,10 +333,12 @@ func (r *Reconciler) SetWeight(desiredWeight int32) error { if !modified { return nil } - msg := fmt.Sprintf("Updating VirtualService `%s` to desiredWeight '%d'", vsvcName, desiredWeight) - r.log.Info(msg) - r.recorder.Event(r.rollout, corev1.EventTypeNormal, "UpdatingVirtualService", msg) _, err = client.Update(ctx, modifiedVsvc, metav1.UpdateOptions{}) + if err == nil { + msg := fmt.Sprintf("VirtualService `%s` set to desiredWeight '%d'", vsvcName, desiredWeight) + r.log.Info(msg) + r.recorder.Event(r.rollout, corev1.EventTypeNormal, "UpdatedVirtualService", msg) + } return err } diff --git a/rollout/trafficrouting_test.go b/rollout/trafficrouting_test.go index 167450e940..ee2f2db1e3 100644 --- a/rollout/trafficrouting_test.go +++ b/rollout/trafficrouting_test.go @@ -2,11 +2,13 @@ package rollout import ( "errors" + "strconv" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/dynamic/dynamicinformer" "k8s.io/client-go/dynamic/dynamiclister" @@ -373,3 +375,91 @@ func TestNewTrafficRoutingReconciler(t *testing.T) { assert.Equal(t, smi.Type, networkReconciler.Type()) } } + +// Verifies with a canary using traffic routing, we add a scaledown delay to the old ReplicaSet +// after promoting desired ReplicaSet to stable +func TestCanaryWithTrafficRoutingAddScaleDownDelay(t *testing.T) { + f := newFixture(t) + defer f.Close() + + r1 := newCanaryRollout("foo", 1, nil, nil, pointer.Int32Ptr(1), intstr.FromInt(1), intstr.FromInt(1)) + r1.Spec.Strategy.Canary.CanaryService = "canary" + r1.Spec.Strategy.Canary.StableService = "stable" + r1.Spec.Strategy.Canary.TrafficRouting = &v1alpha1.RolloutTrafficRouting{ + SMI: &v1alpha1.SMITrafficRouting{}, + } + r2 := bumpVersion(r1) + rs1 := newReplicaSetWithStatus(r1, 1, 1) + rs1PodHash := rs1.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] + rs2 := newReplicaSetWithStatus(r2, 1, 1) + r2 = updateCanaryRolloutStatus(r2, rs1PodHash, 2, 2, 2, false) + rs2PodHash := rs2.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] + r2.Status.ObservedGeneration = strconv.Itoa(int(r2.Generation)) + + canarySelector := map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: rs2PodHash} + stableSelector := map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: rs1PodHash} + canarySvc := newService("canary", 80, canarySelector, r2) + stableSvc := newService("stable", 80, stableSelector, r2) + + f.kubeobjects = append(f.kubeobjects, rs1, rs2, canarySvc, stableSvc) + f.replicaSetLister = append(f.replicaSetLister, rs1, rs2) + f.rolloutLister = append(f.rolloutLister, r2) + f.objects = append(f.objects, r2) + + rs1Patch := f.expectPatchReplicaSetAction(rs1) // adds the annotation + patchIndex := f.expectPatchRolloutAction(r2) // updates the rollout status + f.run(getKey(r2, t)) + + f.verifyPatchedReplicaSet(rs1Patch, 30) + roPatchObj := f.getPatchedRolloutAsObject(patchIndex) + assert.Equal(t, rs2PodHash, roPatchObj.Status.StableRS) + assert.Nil(t, roPatchObj.Status.CurrentStepIndex) +} + +// Verifies with a canary using traffic routing, we scale down old ReplicaSets which exceed our limit +// after promoting desired ReplicaSet to stable +func TestCanaryWithTrafficRoutingScaleDownLimit(t *testing.T) { + f := newFixture(t) + defer f.Close() + + inTheFuture := metav1.Now().Add(10 * time.Second).UTC().Format(time.RFC3339) + + r1 := newCanaryRollout("foo", 1, nil, nil, pointer.Int32Ptr(1), intstr.FromInt(1), intstr.FromInt(1)) + rs1 := newReplicaSetWithStatus(r1, 1, 1) + rs1.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey] = inTheFuture + r1.Spec.Strategy.Canary.ScaleDownDelayRevisionLimit = pointer.Int32Ptr(1) + r1.Spec.Strategy.Canary.CanaryService = "canary" + r1.Spec.Strategy.Canary.StableService = "stable" + r1.Spec.Strategy.Canary.TrafficRouting = &v1alpha1.RolloutTrafficRouting{ + SMI: &v1alpha1.SMITrafficRouting{}, + } + + r2 := bumpVersion(r1) + rs2 := newReplicaSetWithStatus(r2, 1, 1) + rs2.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey] = inTheFuture + + r3 := bumpVersion(r2) + rs3 := newReplicaSetWithStatus(r3, 1, 1) + rs3PodHash := rs3.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] + r3 = updateCanaryRolloutStatus(r3, rs3PodHash, 2, 2, 2, false) + + r3.Status.ObservedGeneration = strconv.Itoa(int(r3.Generation)) + canarySelector := map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: rs3PodHash} + stableSelector := map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: rs3PodHash} + canarySvc := newService("canary", 80, canarySelector, r3) + stableSvc := newService("stable", 80, stableSelector, r3) + + f.kubeobjects = append(f.kubeobjects, rs1, rs2, rs3, canarySvc, stableSvc) + f.replicaSetLister = append(f.replicaSetLister, rs1, rs2, rs3) + f.rolloutLister = append(f.rolloutLister, r3) + f.objects = append(f.objects, r3) + + rs1ScaleDownIndex := f.expectUpdateReplicaSetAction(rs1) // scale down ReplicaSet + _ = f.expectPatchRolloutAction(r3) // updates the rollout status + f.run(getKey(r3, t)) + + rs1Updated := f.getUpdatedReplicaSet(rs1ScaleDownIndex) + assert.Equal(t, int32(0), *rs1Updated.Spec.Replicas) + _, ok := rs1Updated.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey] + assert.False(t, ok, "annotation not removed") +} diff --git a/test/e2e/bluegreen_test.go b/test/e2e/bluegreen_test.go index b6ebadad94..9c91c6c0ef 100644 --- a/test/e2e/bluegreen_test.go +++ b/test/e2e/bluegreen_test.go @@ -165,7 +165,7 @@ func (s *BlueGreenSuite) TestBlueGreenProgressDeadlineExceededWithPause() { kind: Service apiVersion: v1 metadata: - name: rollout-bluegreen-active + name: rollout-bluegreen-with-pause spec: selector: app: rollout-bluegreen-with-pause @@ -204,7 +204,7 @@ spec: strategy: blueGreen: autoPromotionEnabled: false - activeService: rollout-bluegreen-active + activeService: rollout-bluegreen-with-pause `). When(). ApplyManifests(). @@ -226,7 +226,7 @@ func (s *BlueGreenSuite) TestBlueGreenProgressDeadlineExceededWithoutPause() { kind: Service apiVersion: v1 metadata: - name: rollout-bluegreen-active + name: rollout-bluegreen-without-pause spec: selector: app: rollout-bluegreen-without-pause @@ -265,7 +265,7 @@ spec: strategy: blueGreen: autoPromotionEnabled: true - activeService: rollout-bluegreen-active + activeService: rollout-bluegreen-without-pause `). When(). ApplyManifests(). diff --git a/test/e2e/canary_test.go b/test/e2e/canary_test.go index 1181799c4f..10048f4840 100644 --- a/test/e2e/canary_test.go +++ b/test/e2e/canary_test.go @@ -7,9 +7,11 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" corev1 "k8s.io/api/core/v1" + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" "github.com/argoproj/argo-rollouts/test/fixtures" ) @@ -85,7 +87,7 @@ func (s *CanarySuite) TestCanarySetCanaryScale() { } // TestRolloutScalingWhenPaused verifies behavior when scaling a rollout up/down when paused -func (s *FunctionalSuite) TestRolloutScalingWhenPaused() { +func (s *CanarySuite) TestRolloutScalingWhenPaused() { s.Given(). RolloutObjects(`@functional/rollout-basic.yaml`). SetSteps(` @@ -407,3 +409,67 @@ spec: WaitForRolloutStatus("Degraded"). WaitForRolloutStatus("Healthy") } + +// TestCanaryScaleDownDelay verifies canary uses a scaleDownDelay when traffic routing is used, +// and verifies the annotation is properly managed +func (s *CanarySuite) TestCanaryScaleDownDelay() { + s.Given(). + HealthyRollout(`@functional/canary-scaledowndelay.yaml`). + When(). + UpdateSpec(` +spec: + template: + metadata: + annotations: + rev: two`). // update to revision 2 + WaitForRolloutStatus("Healthy"). + Then(). + Assert(func(t *fixtures.Then) { + rs1 := t.GetReplicaSetByRevision("1") + assert.Equal(s.T(), int32(1), *rs1.Spec.Replicas) + assert.NotEmpty(s.T(), rs1.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey]) + }). + When(). + UpdateSpec(` +spec: + template: + metadata: + annotations: + rev: three`). // update to revision 3 + WaitForRolloutStatus("Healthy"). + Then(). + Assert(func(t *fixtures.Then) { + time.Sleep(time.Second) + // rs1 should be scaled down now because of scaleDownRevisionLimit + rs1 := t.GetReplicaSetByRevision("1") + assert.Equal(s.T(), int32(0), *rs1.Spec.Replicas) + assert.Empty(s.T(), rs1.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey]) + + rs2 := t.GetReplicaSetByRevision("2") + assert.Equal(s.T(), int32(1), *rs2.Spec.Replicas) + assert.NotEmpty(s.T(), rs2.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey]) + }). + When(). + UpdateSpec(` +spec: + template: + metadata: + annotations: + rev: two`). // go back to revision 2 + WaitForRolloutStatus("Healthy"). + Then(). + Assert(func(t *fixtures.Then) { + time.Sleep(time.Second) + rs1 := t.GetReplicaSetByRevision("1") + assert.Equal(s.T(), int32(0), *rs1.Spec.Replicas) + assert.Empty(s.T(), rs1.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey]) + + rs4 := t.GetReplicaSetByRevision("4") + assert.Equal(s.T(), int32(1), *rs4.Spec.Replicas) + assert.Empty(s.T(), rs4.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey]) + + rs3 := t.GetReplicaSetByRevision("3") + assert.Equal(s.T(), int32(1), *rs3.Spec.Replicas) + assert.NotEmpty(s.T(), rs3.Annotations[v1alpha1.DefaultReplicaSetScaleDownDeadlineAnnotationKey]) + }) +} diff --git a/test/e2e/functional/canary-scaledowndelay.yaml b/test/e2e/functional/canary-scaledowndelay.yaml new file mode 100644 index 0000000000..21fc6a4d42 --- /dev/null +++ b/test/e2e/functional/canary-scaledowndelay.yaml @@ -0,0 +1,91 @@ +apiVersion: v1 +kind: Service +metadata: + name: canary-scaledowndelay-root +spec: + type: NodePort + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + app: canary-scaledowndelay +--- +apiVersion: v1 +kind: Service +metadata: + name: canary-scaledowndelay-canary +spec: + type: NodePort + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + app: canary-scaledowndelay +--- +apiVersion: v1 +kind: Service +metadata: + name: canary-scaledowndelay-stable +spec: + type: NodePort + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + app: canary-scaledowndelay +--- +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: canary-scaledowndelay-ingress + annotations: + kubernetes.io/ingress.class: alb +spec: + rules: + - http: + paths: + - path: /* + backend: + serviceName: canary-scaledowndelay-root + servicePort: use-annotation +--- +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: canary-scaledowndelay +spec: + selector: + matchLabels: + app: canary-scaledowndelay + template: + metadata: + labels: + app: canary-scaledowndelay + spec: + containers: + - name: canary-scaledowndelay + image: nginx:1.19-alpine + ports: + - name: http + containerPort: 80 + protocol: TCP + resources: + requests: + memory: 16Mi + cpu: 5m + strategy: + canary: + scaleDownDelayRevisionLimit: 1 + canaryService: canary-scaledowndelay-canary + stableService: canary-scaledowndelay-stable + trafficRouting: + alb: + ingress: canary-scaledowndelay-ingress + rootService: canary-scaledowndelay-root + servicePort: 80 diff --git a/test/e2e/istio_test.go b/test/e2e/istio_test.go index 3e18eee18f..a6b9141208 100644 --- a/test/e2e/istio_test.go +++ b/test/e2e/istio_test.go @@ -63,6 +63,7 @@ func (s *IstioSuite) TestIstioHostSplit() { When(). PromoteRollout(). WaitForRolloutStatus("Healthy"). + Sleep(1*time.Second). // stable is currently set first, and then changes made to VirtualServices/DestinationRules Then(). Assert(func(t *fixtures.Then) { vsvc := t.GetVirtualService() @@ -73,7 +74,8 @@ func (s *IstioSuite) TestIstioHostSplit() { rs2 := t.GetReplicaSetByRevision("2") assert.Equal(s.T(), rs2.Spec.Template.Labels[v1alpha1.DefaultRolloutUniqueLabelKey], desired.Spec.Selector[v1alpha1.DefaultRolloutUniqueLabelKey]) assert.Equal(s.T(), rs2.Spec.Template.Labels[v1alpha1.DefaultRolloutUniqueLabelKey], stable.Spec.Selector[v1alpha1.DefaultRolloutUniqueLabelKey]) - }) + }). + ExpectRevisionPodCount("1", 1) // don't scale down old replicaset since it will be within scaleDownDelay } func (s *IstioSuite) TestIstioSubsetSplit() { @@ -111,6 +113,7 @@ func (s *IstioSuite) TestIstioSubsetSplit() { When(). PromoteRollout(). WaitForRolloutStatus("Healthy"). + Sleep(1*time.Second). // stable is currently set first, and then changes made to VirtualServices/DestinationRules Then(). Assert(func(t *fixtures.Then) { vsvc := t.GetVirtualService() @@ -122,6 +125,7 @@ func (s *IstioSuite) TestIstioSubsetSplit() { assert.Equal(s.T(), rs2.Spec.Template.Labels[v1alpha1.DefaultRolloutUniqueLabelKey], destrule.Spec.Subsets[0].Labels[v1alpha1.DefaultRolloutUniqueLabelKey]) // stable assert.Equal(s.T(), rs2.Spec.Template.Labels[v1alpha1.DefaultRolloutUniqueLabelKey], destrule.Spec.Subsets[1].Labels[v1alpha1.DefaultRolloutUniqueLabelKey]) // canary }). + ExpectRevisionPodCount("1", 1). // don't scale down old replicaset since it will be within scaleDownDelay When(). // Verify we remove the injections on the DestinationRule when a rollout no longer references it UpdateSpec(` @@ -130,7 +134,7 @@ spec: canary: trafficRouting: null `). - Sleep(2 * time.Second). + Sleep(1*time.Second). Then(). Assert(func(t *fixtures.Then) { destrule := t.GetDestinationRule() @@ -140,5 +144,6 @@ spec: assert.False(s.T(), ok) _, ok = destrule.Spec.Subsets[1].Labels[v1alpha1.DefaultRolloutUniqueLabelKey] assert.False(s.T(), ok) - }) + }). + ExpectRevisionPodCount("1", 0) // since we moved back to basic canary, we should scale down older RSs } diff --git a/ui/src/models/rollout/generated/api.ts b/ui/src/models/rollout/generated/api.ts index 65dd22e713..c85426ca12 100644 --- a/ui/src/models/rollout/generated/api.ts +++ b/ui/src/models/rollout/generated/api.ts @@ -421,6 +421,18 @@ export interface GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1CanaryStrat * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1CanaryStrategy */ stableMetadata?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1PodTemplateMetadata; + /** + * + * @type {number} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1CanaryStrategy + */ + scaleDownDelaySeconds?: number; + /** + * + * @type {number} + * @memberof GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1CanaryStrategy + */ + scaleDownDelayRevisionLimit?: number; } /** * diff --git a/utils/defaults/defaults.go b/utils/defaults/defaults.go index f46691055f..939cb3d2e1 100644 --- a/utils/defaults/defaults.go +++ b/utils/defaults/defaults.go @@ -101,15 +101,21 @@ func GetExperimentProgressDeadlineSecondsOrDefault(e *v1alpha1.Experiment) int32 } func GetScaleDownDelaySecondsOrDefault(rollout *v1alpha1.Rollout) int32 { - if rollout.Spec.Strategy.BlueGreen == nil { + if rollout.Spec.Strategy.BlueGreen != nil { + if rollout.Spec.Strategy.BlueGreen.ScaleDownDelaySeconds != nil { + return *rollout.Spec.Strategy.BlueGreen.ScaleDownDelaySeconds + } return DefaultScaleDownDelaySeconds } - - if rollout.Spec.Strategy.BlueGreen.ScaleDownDelaySeconds == nil { - return DefaultScaleDownDelaySeconds + if rollout.Spec.Strategy.Canary != nil { + if rollout.Spec.Strategy.Canary.TrafficRouting != nil { + if rollout.Spec.Strategy.Canary.ScaleDownDelaySeconds != nil { + return *rollout.Spec.Strategy.Canary.ScaleDownDelaySeconds + } + return DefaultScaleDownDelaySeconds + } } - - return *rollout.Spec.Strategy.BlueGreen.ScaleDownDelaySeconds + return 0 } func GetAutoPromotionEnabledOrDefault(rollout *v1alpha1.Rollout) bool { diff --git a/utils/defaults/defaults_test.go b/utils/defaults/defaults_test.go index bd53990839..63265aa566 100644 --- a/utils/defaults/defaults_test.go +++ b/utils/defaults/defaults_test.go @@ -136,28 +136,60 @@ func TestGetProgressDeadlineSecondsOrDefault(t *testing.T) { } func TestGetScaleDownDelaySecondsOrDefault(t *testing.T) { - scaleDownDelaySeconds := int32(60) - rolloutNonDefaultValue := &v1alpha1.Rollout{ - Spec: v1alpha1.RolloutSpec{ - Strategy: v1alpha1.RolloutStrategy{ - BlueGreen: &v1alpha1.BlueGreenStrategy{ - ScaleDownDelaySeconds: &scaleDownDelaySeconds, + { + scaleDownDelaySeconds := int32(60) + blueGreenNonDefaultValue := &v1alpha1.Rollout{ + Spec: v1alpha1.RolloutSpec{ + Strategy: v1alpha1.RolloutStrategy{ + BlueGreen: &v1alpha1.BlueGreenStrategy{ + ScaleDownDelaySeconds: &scaleDownDelaySeconds, + }, }, }, - }, + } + assert.Equal(t, scaleDownDelaySeconds, GetScaleDownDelaySecondsOrDefault(blueGreenNonDefaultValue)) } - - assert.Equal(t, scaleDownDelaySeconds, GetScaleDownDelaySecondsOrDefault(rolloutNonDefaultValue)) - rolloutNoStrategyDefaultValue := &v1alpha1.Rollout{} - assert.Equal(t, DefaultScaleDownDelaySeconds, GetScaleDownDelaySecondsOrDefault(rolloutNoStrategyDefaultValue)) - rolloutNoScaleDownDelaySeconds := &v1alpha1.Rollout{ - Spec: v1alpha1.RolloutSpec{ - Strategy: v1alpha1.RolloutStrategy{ - BlueGreen: &v1alpha1.BlueGreenStrategy{}, + { + rolloutNoStrategyDefaultValue := &v1alpha1.Rollout{} + assert.Equal(t, int32(0), GetScaleDownDelaySecondsOrDefault(rolloutNoStrategyDefaultValue)) + } + { + rolloutNoScaleDownDelaySeconds := &v1alpha1.Rollout{ + Spec: v1alpha1.RolloutSpec{ + Strategy: v1alpha1.RolloutStrategy{ + BlueGreen: &v1alpha1.BlueGreenStrategy{}, + }, }, - }, + } + assert.Equal(t, DefaultScaleDownDelaySeconds, GetScaleDownDelaySecondsOrDefault(rolloutNoScaleDownDelaySeconds)) + } + { + scaleDownDelaySeconds := int32(60) + canaryNoTrafficRouting := &v1alpha1.Rollout{ + Spec: v1alpha1.RolloutSpec{ + Strategy: v1alpha1.RolloutStrategy{ + Canary: &v1alpha1.CanaryStrategy{ + ScaleDownDelaySeconds: &scaleDownDelaySeconds, + }, + }, + }, + } + assert.Equal(t, int32(0), GetScaleDownDelaySecondsOrDefault(canaryNoTrafficRouting)) + } + { + scaleDownDelaySeconds := int32(60) + canaryWithTrafficRouting := &v1alpha1.Rollout{ + Spec: v1alpha1.RolloutSpec{ + Strategy: v1alpha1.RolloutStrategy{ + Canary: &v1alpha1.CanaryStrategy{ + ScaleDownDelaySeconds: &scaleDownDelaySeconds, + TrafficRouting: &v1alpha1.RolloutTrafficRouting{}, + }, + }, + }, + } + assert.Equal(t, scaleDownDelaySeconds, GetScaleDownDelaySecondsOrDefault(canaryWithTrafficRouting)) } - assert.Equal(t, DefaultScaleDownDelaySeconds, GetScaleDownDelaySecondsOrDefault(rolloutNoScaleDownDelaySeconds)) } func TestGetAutoPromotionEnabledOrDefault(t *testing.T) { diff --git a/utils/replicaset/replicaset.go b/utils/replicaset/replicaset.go index 9848f59968..a2689fef94 100644 --- a/utils/replicaset/replicaset.go +++ b/utils/replicaset/replicaset.go @@ -464,7 +464,7 @@ func PodTemplateOrStepsChanged(rollout *v1alpha1.Rollout, newRS *appsv1.ReplicaS // ResetCurrentStepIndex resets the index back to zero unless there are no steps func ResetCurrentStepIndex(rollout *v1alpha1.Rollout) *int32 { - if len(rollout.Spec.Strategy.Canary.Steps) > 0 { + if rollout.Spec.Strategy.Canary != nil && len(rollout.Spec.Strategy.Canary.Steps) > 0 { return pointer.Int32Ptr(0) } return nil