diff --git a/go/inventory/v1/node.go b/go/inventory/v1/node.go index cf3a023e..8f37bc37 100644 --- a/go/inventory/v1/node.go +++ b/go/inventory/v1/node.go @@ -2,7 +2,10 @@ package v1 func (nd *NodeCapabilities) Dup() NodeCapabilities { res := NodeCapabilities{ - StorageClasses: make([]string, 0, len(nd.StorageClasses)), + StorageClasses: make([]string, 0, len(nd.StorageClasses)), + InterconnectResourceName: nd.InterconnectResourceName, + InterconnectFabric: nd.InterconnectFabric, + NCCLHCAPrefix: nd.NCCLHCAPrefix, } res.StorageClasses = append(res.StorageClasses, nd.StorageClasses...) diff --git a/go/inventory/v1/node.pb.go b/go/inventory/v1/node.pb.go index 4598dff7..6c961d28 100644 --- a/go/inventory/v1/node.pb.go +++ b/go/inventory/v1/node.pb.go @@ -26,6 +26,23 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // NodeCapabilities extended list of node capabilities type NodeCapabilities struct { StorageClasses []string `protobuf:"bytes,1,rep,name=storage_classes,json=storageClasses,proto3" json:"storage_classes" yaml:"storage_classes"` + // Kubernetes extended-resource name the cluster's device plugin publishes + // for GPU interconnect HCAs (e.g. rdma/rdma_shared_device_ib for an + // InfiniBand fabric, rdma/rdma_shared_device_eth for RoCE). The + // `rdma/*` prefix is the device-plugin's own convention (Mellanox/NVIDIA) + // and stays unchanged here. Empty when the node has no GPU interconnect + // capability. Discovered by the inventory operator from k8s allocatable. + InterconnectResourceName string `protobuf:"bytes,2,opt,name=interconnect_resource_name,json=interconnectResourceName,proto3" json:"interconnect_resource_name,omitempty" yaml:"interconnect_resource_name,omitempty"` + // GPU interconnect fabric type. "infiniband" or "roce". Internal / + // informational — the SDL surface is fabric-agnostic; tenants only + // declare `interconnect: true`. Derived from + // /sys/class/infiniband//ports/1/link_layer on the host node. + InterconnectFabric string `protobuf:"bytes,3,opt,name=interconnect_fabric,json=interconnectFabric,proto3" json:"interconnect_fabric,omitempty" yaml:"interconnect_fabric,omitempty"` + // NCCL IB HCA prefix - the common device-name prefix under + // /sys/class/infiniband (e.g. "mlx5"). Same key for IB and RoCE since + // NCCL uses the IB verbs API for both. Injected by the provider as + // NCCL_IB_HCA when scheduling GPU interconnect workloads. + NCCLHCAPrefix string `protobuf:"bytes,4,opt,name=nccl_hca_prefix,json=ncclHcaPrefix,proto3" json:"nccl_hca_prefix,omitempty" yaml:"nccl_hca_prefix,omitempty"` } func (m *NodeCapabilities) Reset() { *m = NodeCapabilities{} } @@ -68,6 +85,27 @@ func (m *NodeCapabilities) GetStorageClasses() []string { return nil } +func (m *NodeCapabilities) GetInterconnectResourceName() string { + if m != nil { + return m.InterconnectResourceName + } + return "" +} + +func (m *NodeCapabilities) GetInterconnectFabric() string { + if m != nil { + return m.InterconnectFabric + } + return "" +} + +func (m *NodeCapabilities) GetNCCLHCAPrefix() string { + if m != nil { + return m.NCCLHCAPrefix + } + return "" +} + // Node reports node inventory details type Node struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name" yaml:"name"` @@ -137,30 +175,40 @@ func init() { func init() { proto.RegisterFile("akash/inventory/v1/node.proto", fileDescriptor_5f97c0fb35079221) } var fileDescriptor_5f97c0fb35079221 = []byte{ - // 364 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0x31, 0x6f, 0xe2, 0x30, - 0x1c, 0xc5, 0x63, 0x40, 0x27, 0x25, 0x20, 0x0e, 0xe5, 0x4e, 0xa7, 0x88, 0x53, 0x63, 0x6a, 0x75, - 0x60, 0x72, 0x04, 0xa8, 0x4b, 0x3b, 0x54, 0x0a, 0x3b, 0x43, 0xba, 0x75, 0xa9, 0x0c, 0x58, 0x21, - 0x02, 0x62, 0x14, 0xa7, 0x91, 0x90, 0xba, 0x76, 0xef, 0xc7, 0xe9, 0x47, 0x60, 0x64, 0xec, 0x64, - 0x55, 0x61, 0xcb, 0x98, 0x4f, 0x50, 0x25, 0x6e, 0x43, 0x42, 0xdb, 0x2d, 0x79, 0xbf, 0xe7, 0xf7, - 0xec, 0xff, 0x5f, 0x3b, 0x23, 0x4b, 0xc2, 0x17, 0x96, 0xe7, 0x47, 0xd4, 0x0f, 0x59, 0xb0, 0xb5, - 0xa2, 0x81, 0xe5, 0xb3, 0x39, 0xc5, 0x9b, 0x80, 0x85, 0x4c, 0xd7, 0x73, 0x8c, 0x0b, 0x8c, 0xa3, - 0x41, 0xf7, 0xaf, 0xcb, 0x5c, 0x96, 0x63, 0x2b, 0xfb, 0x92, 0xce, 0x2e, 0xfa, 0x26, 0x28, 0xa0, - 0x9c, 0x3d, 0x04, 0x33, 0xca, 0xa5, 0x07, 0x3d, 0x6a, 0x9d, 0x09, 0x9b, 0xd3, 0x31, 0xd9, 0x90, - 0xa9, 0xb7, 0xf2, 0x42, 0x8f, 0x72, 0x7d, 0xa1, 0xfd, 0xe6, 0x21, 0x0b, 0x88, 0x4b, 0xef, 0x67, - 0x2b, 0xc2, 0x39, 0xe5, 0x06, 0xe8, 0xd5, 0xfb, 0xaa, 0x7d, 0x13, 0x0b, 0xd8, 0xbe, 0x95, 0x68, - 0x2c, 0x49, 0x22, 0xe0, 0xa9, 0x39, 0x15, 0xf0, 0xdf, 0x96, 0xac, 0x57, 0x57, 0xe8, 0x04, 0x20, - 0xa7, 0xcd, 0x2b, 0x87, 0xd1, 0x4b, 0x4d, 0x6b, 0x64, 0xf5, 0xfa, 0x48, 0x6b, 0xf8, 0x64, 0x4d, - 0x0d, 0xd0, 0x03, 0x7d, 0xd5, 0x86, 0xb1, 0x80, 0x8d, 0x09, 0x59, 0xd3, 0x44, 0xc0, 0x5c, 0x4f, - 0x05, 0x6c, 0xca, 0xc8, 0xec, 0x0f, 0x39, 0xb9, 0xa8, 0x73, 0x4d, 0x2d, 0x9e, 0x63, 0xd4, 0x7a, - 0xa0, 0xdf, 0x1c, 0x9e, 0xe3, 0xaf, 0xd3, 0xc1, 0x59, 0x83, 0xf3, 0x69, 0xb4, 0x87, 0x3b, 0x01, - 0x95, 0x58, 0x40, 0xb5, 0x90, 0x12, 0x01, 0x8f, 0x41, 0xa9, 0x80, 0x1d, 0x59, 0x55, 0x48, 0xc8, - 0x39, 0x62, 0xfd, 0x09, 0x68, 0xad, 0x59, 0x69, 0x5a, 0x46, 0x3d, 0x2f, 0xbe, 0xf8, 0xa9, 0xb8, - 0x3c, 0x59, 0xfb, 0xfa, 0xa3, 0xbb, 0x55, 0x56, 0x13, 0x01, 0x2b, 0x89, 0xa9, 0x80, 0x7f, 0xe4, - 0x0d, 0xca, 0x2a, 0x72, 0x2a, 0x26, 0xfb, 0x72, 0x17, 0x9b, 0x60, 0x1f, 0x9b, 0xe0, 0x2d, 0x36, - 0xc1, 0xf3, 0xc1, 0x54, 0xf6, 0x07, 0x53, 0x79, 0x3d, 0x98, 0xca, 0xdd, 0xff, 0xcd, 0xd2, 0xc5, - 0x64, 0x19, 0xe2, 0x39, 0x8d, 0x2c, 0x97, 0x55, 0xf6, 0x3f, 0xfd, 0x95, 0xaf, 0x7d, 0xf4, 0x1e, - 0x00, 0x00, 0xff, 0xff, 0x60, 0xd5, 0x85, 0xca, 0x65, 0x02, 0x00, 0x00, + // 521 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xbf, 0x6b, 0xdb, 0x40, + 0x18, 0xb5, 0x12, 0x53, 0xf0, 0xe5, 0x27, 0x97, 0x52, 0x54, 0x97, 0xe8, 0xdc, 0xc3, 0x43, 0xa0, + 0x45, 0x26, 0x0e, 0x5d, 0xda, 0xa1, 0xd4, 0x82, 0x92, 0x42, 0x31, 0x45, 0xdd, 0xba, 0x88, 0xf3, + 0xf9, 0x22, 0x1f, 0xb6, 0x74, 0x42, 0xa7, 0x9a, 0xba, 0x7b, 0xf7, 0xfc, 0x2b, 0xdd, 0xba, 0x75, + 0xcd, 0x98, 0xb1, 0xd3, 0x51, 0xe4, 0x4d, 0xa3, 0xfe, 0x82, 0xe2, 0x93, 0xe3, 0x48, 0x8e, 0x0d, + 0xdd, 0x8e, 0xf7, 0xde, 0xbd, 0xf7, 0xe0, 0xfb, 0x3e, 0x70, 0x4a, 0xc6, 0x44, 0x8e, 0x3a, 0x3c, + 0x9c, 0xb2, 0x30, 0x11, 0xf1, 0xac, 0x33, 0x3d, 0xef, 0x84, 0x62, 0xc8, 0xec, 0x28, 0x16, 0x89, + 0x80, 0x50, 0xd3, 0xf6, 0x8a, 0xb6, 0xa7, 0xe7, 0xcd, 0xc7, 0xbe, 0xf0, 0x85, 0xa6, 0x3b, 0x8b, + 0x57, 0xa1, 0x6c, 0xe2, 0x0d, 0x46, 0x31, 0x93, 0xe2, 0x6b, 0x4c, 0x99, 0x2c, 0x34, 0xf8, 0x77, + 0x1d, 0x1c, 0xf7, 0xc5, 0x90, 0x39, 0x24, 0x22, 0x03, 0x3e, 0xe1, 0x09, 0x67, 0x12, 0x8e, 0xc0, + 0x91, 0x4c, 0x44, 0x4c, 0x7c, 0xe6, 0xd1, 0x09, 0x91, 0x92, 0x49, 0xd3, 0x68, 0xed, 0x9e, 0x35, + 0x7a, 0x6f, 0x53, 0x85, 0x0e, 0x3f, 0x17, 0x94, 0x53, 0x30, 0x99, 0x42, 0xeb, 0xe2, 0x5c, 0xa1, + 0x27, 0x33, 0x12, 0x4c, 0x5e, 0xe3, 0x35, 0x02, 0xbb, 0x87, 0xb2, 0xf2, 0x19, 0xfe, 0x34, 0x40, + 0x93, 0x87, 0x09, 0x8b, 0xa9, 0x08, 0x43, 0x46, 0x13, 0xef, 0xae, 0x9f, 0x17, 0x92, 0x80, 0x99, + 0x3b, 0x2d, 0xe3, 0xac, 0xd1, 0x93, 0xa9, 0x42, 0xe6, 0x87, 0x92, 0xca, 0x5d, 0x8a, 0xfa, 0x24, + 0x60, 0x99, 0x42, 0xed, 0xed, 0x0e, 0x2f, 0x45, 0xc0, 0x13, 0x16, 0x44, 0xc9, 0x2c, 0x57, 0xe8, + 0x45, 0x51, 0xea, 0x7f, 0xd4, 0xd8, 0x35, 0xf9, 0x96, 0x40, 0x78, 0x6d, 0x80, 0x93, 0x8a, 0xc7, + 0x15, 0x19, 0xc4, 0x9c, 0x9a, 0xbb, 0xba, 0xac, 0x97, 0x2a, 0x04, 0xcb, 0x65, 0xdf, 0x6b, 0x36, + 0x53, 0xe8, 0x74, 0xc3, 0xa7, 0x4a, 0xbf, 0xf6, 0x86, 0x7e, 0xeb, 0x32, 0xec, 0x42, 0xfe, 0xc0, + 0x1c, 0x7e, 0x07, 0x47, 0x21, 0xa5, 0x13, 0x6f, 0x44, 0x89, 0x17, 0xc5, 0xec, 0x8a, 0x7f, 0x33, + 0xeb, 0xba, 0x8d, 0x9b, 0x2a, 0x74, 0xd0, 0x77, 0x9c, 0x8f, 0x97, 0xce, 0xbb, 0x4f, 0x9a, 0xc8, + 0x14, 0x7a, 0xba, 0xa6, 0xad, 0x94, 0x68, 0x15, 0x25, 0xb6, 0x4a, 0xb0, 0x7b, 0xb0, 0xe0, 0x2e, + 0x29, 0x29, 0xfc, 0xf0, 0xaf, 0x1d, 0x50, 0x5f, 0x6c, 0x10, 0xbc, 0x00, 0x75, 0x3d, 0x34, 0x43, + 0x27, 0xa3, 0x54, 0xa1, 0xfa, 0x72, 0x40, 0x1a, 0xcf, 0x15, 0xda, 0x5b, 0x7a, 0x93, 0x80, 0x61, + 0x57, 0x83, 0x50, 0x82, 0xc6, 0x6a, 0x25, 0xf5, 0xb8, 0xf7, 0xba, 0xcf, 0xed, 0x87, 0x1b, 0x6e, + 0x2f, 0x12, 0xee, 0xa6, 0x20, 0x7b, 0xdd, 0x1b, 0x85, 0x6a, 0xa9, 0x42, 0x8d, 0x15, 0x94, 0x29, + 0x74, 0x6f, 0x94, 0x2b, 0x74, 0x5c, 0x44, 0xad, 0x20, 0xec, 0xde, 0xd3, 0xf0, 0x87, 0x01, 0xf6, + 0x69, 0x69, 0xe1, 0xf5, 0xe8, 0xf6, 0xba, 0xed, 0x6d, 0xc1, 0xe5, 0xe3, 0xe8, 0xbd, 0x59, 0x66, + 0xef, 0x97, 0xd1, 0x4c, 0xa1, 0x8a, 0x63, 0xae, 0xd0, 0x49, 0xd1, 0xa0, 0x8c, 0x62, 0xb7, 0x22, + 0xea, 0xbd, 0xba, 0x49, 0x2d, 0xe3, 0x36, 0xb5, 0x8c, 0xbf, 0xa9, 0x65, 0x5c, 0xcf, 0xad, 0xda, + 0xed, 0xdc, 0xaa, 0xfd, 0x99, 0x5b, 0xb5, 0x2f, 0xcf, 0xa2, 0xb1, 0x6f, 0x93, 0x71, 0x62, 0x0f, + 0xd9, 0xb4, 0xe3, 0x8b, 0xca, 0x0d, 0x0f, 0x1e, 0xe9, 0xd3, 0xbd, 0xf8, 0x17, 0x00, 0x00, 0xff, + 0xff, 0x79, 0x09, 0x7e, 0x8d, 0x29, 0x04, 0x00, 0x00, } func (m *NodeCapabilities) Marshal() (dAtA []byte, err error) { @@ -183,6 +231,27 @@ func (m *NodeCapabilities) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.NCCLHCAPrefix) > 0 { + i -= len(m.NCCLHCAPrefix) + copy(dAtA[i:], m.NCCLHCAPrefix) + i = encodeVarintNode(dAtA, i, uint64(len(m.NCCLHCAPrefix))) + i-- + dAtA[i] = 0x22 + } + if len(m.InterconnectFabric) > 0 { + i -= len(m.InterconnectFabric) + copy(dAtA[i:], m.InterconnectFabric) + i = encodeVarintNode(dAtA, i, uint64(len(m.InterconnectFabric))) + i-- + dAtA[i] = 0x1a + } + if len(m.InterconnectResourceName) > 0 { + i -= len(m.InterconnectResourceName) + copy(dAtA[i:], m.InterconnectResourceName) + i = encodeVarintNode(dAtA, i, uint64(len(m.InterconnectResourceName))) + i-- + dAtA[i] = 0x12 + } if len(m.StorageClasses) > 0 { for iNdEx := len(m.StorageClasses) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.StorageClasses[iNdEx]) @@ -268,6 +337,18 @@ func (m *NodeCapabilities) Size() (n int) { n += 1 + l + sovNode(uint64(l)) } } + l = len(m.InterconnectResourceName) + if l > 0 { + n += 1 + l + sovNode(uint64(l)) + } + l = len(m.InterconnectFabric) + if l > 0 { + n += 1 + l + sovNode(uint64(l)) + } + l = len(m.NCCLHCAPrefix) + if l > 0 { + n += 1 + l + sovNode(uint64(l)) + } return n } @@ -355,6 +436,102 @@ func (m *NodeCapabilities) Unmarshal(dAtA []byte) error { } m.StorageClasses = append(m.StorageClasses, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InterconnectResourceName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNode + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthNode + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNode + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InterconnectResourceName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InterconnectFabric", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNode + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthNode + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNode + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InterconnectFabric = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NCCLHCAPrefix", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNode + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthNode + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNode + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NCCLHCAPrefix = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipNode(dAtA[iNdEx:]) diff --git a/go/inventory/v1/node_test.go b/go/inventory/v1/node_test.go new file mode 100644 index 00000000..81cd062a --- /dev/null +++ b/go/inventory/v1/node_test.go @@ -0,0 +1,42 @@ +package v1 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNodeCapabilities_Dup_PreservesInterconnectFields(t *testing.T) { + src := NodeCapabilities{ + StorageClasses: []string{"beta3", "default"}, + InterconnectResourceName: "rdma/rdma_shared_device_ib", + InterconnectFabric: "infiniband", + NCCLHCAPrefix: "mlx5", + } + + got := src.Dup() + + require.Equal(t, src.StorageClasses, got.StorageClasses) + require.Equal(t, "rdma/rdma_shared_device_ib", got.InterconnectResourceName) + require.Equal(t, "infiniband", got.InterconnectFabric) + require.Equal(t, "mlx5", got.NCCLHCAPrefix) + + // mutating dup must not affect the source (Dup is a deep copy) + got.StorageClasses[0] = "mutated" + got.InterconnectResourceName = "rdma/rdma_shared_device_eth" + require.Equal(t, "beta3", src.StorageClasses[0]) + require.Equal(t, "rdma/rdma_shared_device_ib", src.InterconnectResourceName) +} + +func TestNodeCapabilities_Dup_ZeroValueInterconnect(t *testing.T) { + // A non-interconnect node leaves the new fields at zero value. + src := NodeCapabilities{ + StorageClasses: []string{"default"}, + } + + got := src.Dup() + + require.Empty(t, got.InterconnectResourceName) + require.Empty(t, got.InterconnectFabric) + require.Empty(t, got.NCCLHCAPrefix) +} diff --git a/go/inventory/v1/resourcepair.go b/go/inventory/v1/resourcepair.go index 9a96b5d5..021746eb 100644 --- a/go/inventory/v1/resourcepair.go +++ b/go/inventory/v1/resourcepair.go @@ -47,19 +47,51 @@ func (m *ResourcePair) LT(rhs ResourcePair) bool { return m.Allocatable.Cmp(*rhs.Allocatable) == -1 } +// IsZero reports whether the ResourcePair has been initialized. A +// zero-valued ResourcePair has nil quantity pointers and is the natural +// state for, e.g., a node that does not have GPU interconnect capacity +// (and therefore leaves `NodeResources.GPUInterconnect` untouched). +func (m *ResourcePair) IsZero() bool { + if m == nil { + return true + } + return m.Capacity == nil && m.Allocatable == nil && m.Allocated == nil && len(m.Attributes) == 0 +} + func (m *ResourcePair) Dup() ResourcePair { - capacity := m.Capacity.DeepCopy() - allocatable := m.Allocatable.DeepCopy() - allocated := m.Allocated.DeepCopy() + // A zero-valued ResourcePair (all quantity pointers nil) must round-trip + // through Dup() without panicking. Without this guard, calling Dup() + // against e.g. an unpopulated `NodeResources.GPUInterconnect` on a + // non-interconnect node nil-derefs Capacity.DeepCopy(). + if m == nil || m.IsZero() { + return ResourcePair{} + } - res := ResourcePair{ - Capacity: &capacity, - Allocatable: &allocatable, - Allocated: &allocated, - Attributes: m.Attributes.Dup(), + // Preserve the nil/non-nil shape of each quantity pointer. Returning + // `&zeroQuantity` for an originally-nil field would change protobuf + // field-presence semantics (and the JSON serialization the manifest + // version hash is computed from), so a partially-populated source + // must Dup to a structurally identical copy. + var capacity, allocatable, allocated *resource.Quantity + if m.Capacity != nil { + c := m.Capacity.DeepCopy() + capacity = &c + } + if m.Allocatable != nil { + a := m.Allocatable.DeepCopy() + allocatable = &a + } + if m.Allocated != nil { + al := m.Allocated.DeepCopy() + allocated = &al } - return res + return ResourcePair{ + Capacity: capacity, + Allocatable: allocatable, + Allocated: allocated, + Attributes: m.Attributes.Dup(), + } } func (m *ResourcePair) SubMilliNLZ(val types.ResourceValue) bool { diff --git a/go/inventory/v1/resourcepair_zero_test.go b/go/inventory/v1/resourcepair_zero_test.go new file mode 100644 index 00000000..12b4379b --- /dev/null +++ b/go/inventory/v1/resourcepair_zero_test.go @@ -0,0 +1,96 @@ +package v1 + +import ( + "testing" + + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/api/resource" +) + +// ResourcePair.Dup() previously dereferenced Capacity/Allocatable/Allocated +// unconditionally, which panics for a zero-value ResourcePair. +// Non-interconnect nodes legitimately leave NodeResources.GPUInterconnect +// at zero value (no quantity pointers populated), so NodeResources.Dup() +// ends up calling ResourcePair.Dup() on a zero value. +// +// Two assertions: +// 1. ResourcePair.Dup() returns an equivalent zero-valued ResourcePair +// without panicking when called on the nil/zero receiver. +// 2. NodeResources.Dup() round-trips a non-interconnect NodeResources +// (GPUInterconnect left at zero value) without panicking. + +func TestResourcePair_Dup_ZeroValueDoesNotPanic(t *testing.T) { + var zero ResourcePair + require.True(t, zero.IsZero(), "fresh ResourcePair must report IsZero") + + got := zero.Dup() + require.True(t, got.IsZero(), "Dup of zero ResourcePair must remain zero") +} + +func TestResourcePair_Dup_NilReceiverDoesNotPanic(t *testing.T) { + var nilRP *ResourcePair + // Calling a method on a nil pointer receiver is only safe if the method + // itself checks for nil; the new Dup() does. + got := nilRP.Dup() + require.True(t, got.IsZero()) +} + +func TestResourcePair_Dup_PopulatedRoundTrips(t *testing.T) { + rp := NewResourcePair(63, 8, 8, resource.DecimalSI) + got := rp.Dup() + + require.Equal(t, int64(63), got.Capacity.Value()) + require.Equal(t, int64(8), got.Allocatable.Value()) + require.Equal(t, int64(8), got.Allocated.Value()) + + // Mutating the dup must not poison the source — Dup() is a deep copy. + got.Allocated.Set(0) + require.Equal(t, int64(8), rp.Allocated.Value()) +} + +// Regression-pins CodeRabbit review #3: previous Dup() always returned +// non-nil pointers for Capacity/Allocatable/Allocated even when the +// source pointers were nil. That changed protobuf field-presence on the +// copy and shifted the JSON serialization (which the manifest version +// hash is computed from). A partially-nil source must Dup to a +// structurally identical copy. +func TestResourcePair_Dup_PreservesNilPointers(t *testing.T) { + q := resource.MustParse("8") + + // Capacity nil; Allocatable + Allocated populated. + src := ResourcePair{ + Allocatable: &q, + Allocated: resource.NewQuantity(0, resource.DecimalSI), + } + require.False(t, src.IsZero(), "src has non-nil pointers — not zero") + + got := src.Dup() + require.Nil(t, got.Capacity, "Dup must preserve nil Capacity") + require.NotNil(t, got.Allocatable, "non-nil Allocatable must round-trip") + require.NotNil(t, got.Allocated, "non-nil Allocated must round-trip") + require.Equal(t, int64(8), got.Allocatable.Value()) + + // Mutating the dup must not poison the source. + got.Allocatable.Set(99) + require.Equal(t, int64(8), src.Allocatable.Value(), "Dup must deep-copy") +} + +// Mirrors the realistic non-interconnect-node case the bug would surface in. +func TestNodeResources_Dup_ZeroGPUInterconnect_DoesNotPanic(t *testing.T) { + zero := NewResourcePair(0, 0, 0, resource.DecimalSI) + + // Every member is initialized except GPUInterconnect, which is left + // at zero value. + src := NodeResources{ + CPU: CPU{Quantity: NewResourcePairMilli(0, 0, 0, resource.DecimalSI)}, + Memory: Memory{Quantity: zero}, + GPU: GPU{Quantity: zero}, + EphemeralStorage: zero, + VolumesAttached: zero, + VolumesMounted: zero, + // GPUInterconnect: + } + + got := src.Dup() + require.True(t, got.GPUInterconnect.IsZero(), "zero-value GPUInterconnect must Dup as zero") +} diff --git a/go/inventory/v1/resources.go b/go/inventory/v1/resources.go index f3a6487e..38d68d2c 100644 --- a/go/inventory/v1/resources.go +++ b/go/inventory/v1/resources.go @@ -8,6 +8,7 @@ func (s *NodeResources) Dup() NodeResources { EphemeralStorage: s.EphemeralStorage.Dup(), VolumesAttached: s.VolumesAttached.Dup(), VolumesMounted: s.VolumesMounted.Dup(), + GPUInterconnect: s.GPUInterconnect.Dup(), } return res diff --git a/go/inventory/v1/resources.pb.go b/go/inventory/v1/resources.pb.go index 7bb4cfd5..259bc8b3 100644 --- a/go/inventory/v1/resources.pb.go +++ b/go/inventory/v1/resources.pb.go @@ -31,6 +31,13 @@ type NodeResources struct { EphemeralStorage ResourcePair `protobuf:"bytes,4,opt,name=ephemeral_storage,json=ephemeralStorage,proto3" json:"ephemeral_storage" yaml:"ephemeral_storage"` VolumesAttached ResourcePair `protobuf:"bytes,5,opt,name=volumes_attached,json=volumesAttached,proto3" json:"volumes_attached" yaml:"volumes_attached"` VolumesMounted ResourcePair `protobuf:"bytes,6,opt,name=volumes_mounted,json=volumesMounted,proto3" json:"volumes_mounted" yaml:"volumes_mounted"` + // GPUInterconnect reports node GPU-interconnect HCA capacity. + // Capacity/Allocatable/Allocated are populated by the inventory + // operator from k8s allocatable for whichever + // rdma/rdma_shared_device_* extended resource the cluster's device + // plugin publishes (the `rdma/*` prefix is the device plugin's + // naming convention; see NodeCapabilities.interconnect_resource_name). + GPUInterconnect ResourcePair `protobuf:"bytes,7,opt,name=gpu_interconnect,json=gpuInterconnect,proto3" json:"gpu_interconnect" yaml:"gpu_interconnect"` } func (m *NodeResources) Reset() { *m = NodeResources{} } @@ -108,6 +115,13 @@ func (m *NodeResources) GetVolumesMounted() ResourcePair { return ResourcePair{} } +func (m *NodeResources) GetGPUInterconnect() ResourcePair { + if m != nil { + return m.GPUInterconnect + } + return ResourcePair{} +} + func init() { proto.RegisterType((*NodeResources)(nil), "akash.inventory.v1.NodeResources") } @@ -117,37 +131,40 @@ func init() { } var fileDescriptor_f20a722bd8ee01b5 = []byte{ - // 475 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0xc1, 0x8a, 0xd3, 0x40, - 0x18, 0xc7, 0x1b, 0xbb, 0xdb, 0xc3, 0xc8, 0xee, 0xd6, 0x20, 0x6e, 0xa8, 0x92, 0x29, 0x03, 0xc2, - 0x7a, 0x49, 0x58, 0xc5, 0x8b, 0x37, 0x53, 0xb4, 0x88, 0xac, 0x84, 0x2c, 0xf5, 0x20, 0x48, 0x19, - 0xd3, 0x61, 0x5a, 0xda, 0x74, 0x86, 0x24, 0x13, 0xd8, 0x97, 0x10, 0xc1, 0x8b, 0xcf, 0xe0, 0x93, - 0xec, 0x71, 0xf1, 0xe4, 0x69, 0x94, 0xf6, 0x96, 0x63, 0x9e, 0x40, 0x92, 0x99, 0x2c, 0xb6, 0xcd, - 0x2e, 0x7b, 0x6b, 0xe6, 0xff, 0xff, 0x7e, 0xdf, 0x6f, 0x0a, 0x03, 0x10, 0x9e, 0xe3, 0x64, 0xea, - 0xce, 0x96, 0x19, 0x59, 0xa6, 0x2c, 0xbe, 0x70, 0xb3, 0x53, 0x37, 0x26, 0x09, 0x13, 0x71, 0x48, - 0x12, 0x87, 0xc7, 0x2c, 0x65, 0xa6, 0x59, 0x75, 0x9c, 0xeb, 0x8e, 0x93, 0x9d, 0xf6, 0x1e, 0x52, - 0x46, 0x59, 0x15, 0xbb, 0xe5, 0x2f, 0xd5, 0xec, 0x3d, 0x69, 0xa0, 0x85, 0x5c, 0xdc, 0x92, 0xd2, - 0xeb, 0x14, 0x36, 0xa4, 0x11, 0x89, 0xca, 0x7d, 0xaa, 0xf0, 0xf4, 0x16, 0x55, 0x8e, 0x67, 0xb1, - 0xaa, 0xa1, 0x5f, 0xfb, 0xe0, 0xe0, 0x03, 0x9b, 0x90, 0xa0, 0xbe, 0x85, 0xf9, 0x1e, 0xb4, 0x43, - 0x2e, 0x2c, 0xa3, 0x6f, 0x9c, 0xdc, 0x7f, 0x7e, 0xec, 0xec, 0xde, 0xc6, 0x19, 0xf8, 0x23, 0xaf, - 0x7f, 0x29, 0x61, 0x6b, 0x25, 0x61, 0x7b, 0xe0, 0x8f, 0x72, 0x09, 0xcb, 0x91, 0x42, 0x42, 0x70, - 0x81, 0xa3, 0xc5, 0x2b, 0x14, 0x72, 0x81, 0x82, 0xf2, 0xc8, 0xfc, 0x0c, 0x3a, 0xca, 0xca, 0xba, - 0x57, 0xf1, 0x7a, 0x4d, 0xbc, 0xb3, 0xaa, 0xe1, 0x3d, 0xd3, 0xc8, 0x8e, 0xfa, 0xce, 0x25, 0xd4, - 0xb3, 0x85, 0x84, 0x07, 0x0a, 0xac, 0xbe, 0x51, 0xa0, 0x03, 0xf3, 0x1c, 0xb4, 0x29, 0x17, 0x56, - 0xfb, 0x66, 0xd7, 0xa1, 0x3f, 0xf2, 0x4e, 0x6a, 0xd7, 0xa1, 0x72, 0xa5, 0xff, 0xbb, 0x52, 0x2e, - 0xd0, 0xcf, 0x3f, 0x70, 0x6f, 0xe8, 0x8f, 0x92, 0xa0, 0x8c, 0xcc, 0x1f, 0x06, 0x78, 0x40, 0xf8, - 0x94, 0x44, 0x24, 0xc6, 0x8b, 0x71, 0x92, 0xb2, 0x18, 0x53, 0x62, 0xed, 0x55, 0x3b, 0xfa, 0x4d, - 0x3b, 0xea, 0xff, 0xce, 0xc7, 0xb3, 0xd8, 0x7b, 0xa7, 0x97, 0x75, 0xdf, 0xd4, 0x88, 0x73, 0x45, - 0xc8, 0x25, 0xdc, 0xc5, 0x16, 0x12, 0x5a, 0xca, 0x63, 0x27, 0x42, 0x41, 0x97, 0x6c, 0x21, 0xcc, - 0xef, 0x06, 0xe8, 0x66, 0x6c, 0x21, 0x22, 0x92, 0x8c, 0x71, 0x9a, 0xe2, 0x70, 0x4a, 0x26, 0xd6, - 0xfe, 0x1d, 0xcd, 0xde, 0x6a, 0xb3, 0xa3, 0x8f, 0x8a, 0xf0, 0x5a, 0x03, 0x72, 0x09, 0x77, 0xa0, - 0x85, 0x84, 0xc7, 0xca, 0x6b, 0x3b, 0x41, 0xc1, 0x51, 0xb6, 0x39, 0x6f, 0x7e, 0x35, 0x40, 0x7d, - 0x36, 0x8e, 0x98, 0x58, 0xa6, 0x64, 0x62, 0x75, 0xee, 0x28, 0x35, 0xd0, 0x52, 0x87, 0x5a, 0xea, - 0x4c, 0xcd, 0xe7, 0x12, 0x6e, 0x23, 0x0b, 0x09, 0x1f, 0x6d, 0x2a, 0xe9, 0x00, 0x05, 0x87, 0xd9, - 0xc6, 0xb0, 0xf7, 0xf2, 0x72, 0x65, 0x1b, 0x57, 0x2b, 0xdb, 0xf8, 0xbb, 0xb2, 0x8d, 0x6f, 0x6b, - 0xbb, 0x75, 0xb5, 0xb6, 0x5b, 0xbf, 0xd7, 0x76, 0xeb, 0xd3, 0x63, 0x3e, 0xa7, 0x0e, 0x9e, 0xa7, - 0xce, 0x84, 0x64, 0x2e, 0x65, 0x1b, 0xcf, 0xe3, 0x4b, 0xa7, 0x7a, 0x12, 0x2f, 0xfe, 0x05, 0x00, - 0x00, 0xff, 0xff, 0xca, 0x7c, 0xf6, 0x39, 0xe6, 0x03, 0x00, 0x00, + // 514 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0xc1, 0x8a, 0xd3, 0x40, + 0x18, 0xc7, 0x1b, 0xbb, 0x56, 0x88, 0xec, 0xb6, 0x06, 0x71, 0x43, 0x95, 0x4c, 0x19, 0x10, 0xd6, + 0x4b, 0xc2, 0x2a, 0x5e, 0xbc, 0x99, 0xa2, 0x65, 0x91, 0x95, 0x90, 0x25, 0x1e, 0x04, 0x29, 0x63, + 0x3a, 0x4c, 0x4b, 0x9b, 0xcc, 0x90, 0x64, 0x02, 0xfb, 0x12, 0x22, 0x78, 0xf1, 0x19, 0x7c, 0x92, + 0x3d, 0xee, 0xd1, 0xd3, 0x28, 0xed, 0x45, 0x72, 0xcc, 0x13, 0x48, 0x32, 0xc9, 0xda, 0xa6, 0x75, + 0xd9, 0xbd, 0x25, 0xf3, 0xff, 0xbe, 0xdf, 0xf7, 0x9b, 0x0f, 0x46, 0x85, 0x68, 0x8e, 0xe2, 0xa9, + 0x35, 0x0b, 0x53, 0x1c, 0x26, 0x34, 0x3a, 0xb7, 0xd2, 0x63, 0x2b, 0xc2, 0x31, 0xe5, 0x91, 0x8f, + 0x63, 0x93, 0x45, 0x34, 0xa1, 0x9a, 0x56, 0xd6, 0x98, 0x57, 0x35, 0x66, 0x7a, 0xdc, 0x7f, 0x48, + 0x28, 0xa1, 0x65, 0x6c, 0x15, 0x5f, 0xb2, 0xb2, 0xff, 0x64, 0x07, 0xcd, 0x67, 0xfc, 0x9a, 0x94, + 0x5c, 0xa5, 0x60, 0x47, 0x1a, 0xe0, 0xa0, 0x98, 0x27, 0x0b, 0x9e, 0x5e, 0xa3, 0xca, 0xd0, 0x2c, + 0x92, 0x65, 0xf0, 0x4f, 0x47, 0xdd, 0x7f, 0x4f, 0x27, 0xd8, 0xad, 0x6f, 0xa1, 0xbd, 0x53, 0xdb, + 0x3e, 0xe3, 0xba, 0x32, 0x50, 0x8e, 0xee, 0x3f, 0x3f, 0x34, 0xb7, 0x6f, 0x63, 0x0e, 0x1d, 0xcf, + 0x1e, 0x5c, 0x08, 0xd0, 0x5a, 0x0a, 0xd0, 0x1e, 0x3a, 0x5e, 0x26, 0x40, 0xd1, 0x92, 0x0b, 0xa0, + 0x9e, 0xa3, 0x60, 0xf1, 0x0a, 0xfa, 0x8c, 0x43, 0xb7, 0x38, 0xd2, 0x3e, 0xa9, 0x1d, 0x69, 0xa5, + 0xdf, 0x29, 0x79, 0xfd, 0x5d, 0xbc, 0xd3, 0xb2, 0xc2, 0x7e, 0x56, 0x21, 0x3b, 0xf2, 0x3f, 0x13, + 0xa0, 0xea, 0xcd, 0x05, 0xd8, 0x97, 0x60, 0xf9, 0x0f, 0xdd, 0x2a, 0xd0, 0xce, 0xd4, 0x36, 0x61, + 0x5c, 0x6f, 0xff, 0xdf, 0x75, 0xe4, 0x78, 0xf6, 0x51, 0xed, 0x3a, 0x92, 0xae, 0x64, 0xdd, 0x95, + 0x30, 0x0e, 0x7f, 0xfc, 0x02, 0x7b, 0x23, 0xc7, 0x8b, 0xdd, 0x22, 0xd2, 0xbe, 0x2b, 0xea, 0x03, + 0xcc, 0xa6, 0x38, 0xc0, 0x11, 0x5a, 0x8c, 0xe3, 0x84, 0x46, 0x88, 0x60, 0x7d, 0xaf, 0x9c, 0x31, + 0xd8, 0x35, 0xa3, 0xde, 0x9d, 0x83, 0x66, 0x91, 0x7d, 0x52, 0x0d, 0xeb, 0xbd, 0xa9, 0x11, 0x67, + 0x92, 0x90, 0x09, 0xb0, 0x8d, 0xcd, 0x05, 0xd0, 0xa5, 0xc7, 0x56, 0x04, 0xdd, 0x1e, 0x6e, 0x20, + 0xb4, 0x6f, 0x8a, 0xda, 0x4b, 0xe9, 0x82, 0x07, 0x38, 0x1e, 0xa3, 0x24, 0x41, 0xfe, 0x14, 0x4f, + 0xf4, 0xbb, 0x37, 0x34, 0x7b, 0x5b, 0x99, 0x75, 0x3f, 0x48, 0xc2, 0xeb, 0x0a, 0x90, 0x09, 0xb0, + 0x05, 0xcd, 0x05, 0x38, 0x94, 0x5e, 0xcd, 0x04, 0xba, 0xdd, 0x74, 0xb3, 0x5f, 0xfb, 0xa2, 0xa8, + 0xf5, 0xd9, 0x38, 0xa0, 0x3c, 0x4c, 0xf0, 0x44, 0xef, 0xdc, 0x50, 0x6a, 0x58, 0x49, 0x1d, 0x54, + 0x52, 0xa7, 0xb2, 0x3f, 0x13, 0xa0, 0x89, 0xcc, 0x05, 0x78, 0xb4, 0xa9, 0x54, 0x05, 0xd0, 0x3d, + 0x48, 0x37, 0x9a, 0xcb, 0x35, 0x11, 0xc6, 0xc7, 0xb3, 0x30, 0xc1, 0x91, 0x4f, 0xc3, 0x10, 0xfb, + 0x89, 0x7e, 0xef, 0xb6, 0x6b, 0x1a, 0x39, 0xde, 0xc9, 0x1a, 0xa0, 0x58, 0x53, 0x13, 0xfa, 0x6f, + 0x4d, 0xcd, 0x04, 0xba, 0x5d, 0xc2, 0xf8, 0x7a, 0xbf, 0xfd, 0xf2, 0x62, 0x69, 0x28, 0x97, 0x4b, + 0x43, 0xf9, 0xbd, 0x34, 0x94, 0xaf, 0x2b, 0xa3, 0x75, 0xb9, 0x32, 0x5a, 0x3f, 0x57, 0x46, 0xeb, + 0xe3, 0x63, 0x36, 0x27, 0x26, 0x9a, 0x27, 0xe6, 0x04, 0xa7, 0x16, 0xa1, 0x1b, 0x8f, 0xf6, 0x73, + 0xa7, 0x7c, 0xa8, 0x2f, 0xfe, 0x06, 0x00, 0x00, 0xff, 0xff, 0x10, 0xed, 0x3d, 0xce, 0x7c, 0x04, + 0x00, 0x00, } func (m *NodeResources) Marshal() (dAtA []byte, err error) { @@ -170,6 +187,16 @@ func (m *NodeResources) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.GPUInterconnect.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintResources(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a { size, err := m.VolumesMounted.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -262,6 +289,8 @@ func (m *NodeResources) Size() (n int) { n += 1 + l + sovResources(uint64(l)) l = m.VolumesMounted.Size() n += 1 + l + sovResources(uint64(l)) + l = m.GPUInterconnect.Size() + n += 1 + l + sovResources(uint64(l)) return n } @@ -498,6 +527,39 @@ func (m *NodeResources) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GPUInterconnect", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.GPUInterconnect.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipResources(dAtA[iNdEx:]) diff --git a/go/inventory/v1/resources_test.go b/go/inventory/v1/resources_test.go new file mode 100644 index 00000000..a306884a --- /dev/null +++ b/go/inventory/v1/resources_test.go @@ -0,0 +1,33 @@ +package v1 + +import ( + "testing" + + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/api/resource" +) + +func TestNodeResources_Dup_PreservesGPUInterconnect(t *testing.T) { + // NodeResources.Dup() reads each member's Dup() so every member must be + // initialized to a non-zero ResourcePair to avoid nil-quantity panics. + zeroPair := NewResourcePair(0, 0, 0, resource.DecimalSI) + src := NodeResources{ + CPU: CPU{Quantity: NewResourcePairMilli(0, 0, 0, resource.DecimalSI)}, + Memory: Memory{Quantity: zeroPair}, + GPU: GPU{Quantity: zeroPair}, + EphemeralStorage: zeroPair, + VolumesAttached: zeroPair, + VolumesMounted: zeroPair, + GPUInterconnect: NewResourcePair(63, 8, 8, resource.DecimalSI), + } + + got := src.Dup() + + require.Equal(t, int64(63), got.GPUInterconnect.Capacity.Value()) + require.Equal(t, int64(8), got.GPUInterconnect.Allocatable.Value()) + require.Equal(t, int64(8), got.GPUInterconnect.Allocated.Value()) + + // Source unaffected when mutating dup + got.GPUInterconnect.Allocated.Set(0) + require.Equal(t, int64(8), src.GPUInterconnect.Allocated.Value()) +} diff --git a/go/manifest/v2beta3/service.pb.go b/go/manifest/v2beta3/service.pb.go index 25b7e89e..35de67f3 100644 --- a/go/manifest/v2beta3/service.pb.go +++ b/go/manifest/v2beta3/service.pb.go @@ -272,6 +272,19 @@ type Service struct { Expose ServiceExposes `protobuf:"bytes,8,rep,name=expose,proto3,castrepeated=ServiceExposes" json:"expose" yaml:"expose"` Params *ServiceParams `protobuf:"bytes,9,opt,name=params,proto3" json:"params,omitempty" yaml:"params,omitempty"` Credentials *ImageCredentials `protobuf:"bytes,10,opt,name=credentials,proto3" json:"credentials" yaml:"credentials"` + // InterconnectGroup carries the SDL gpu.attributes.interconnect_group + // peer-group label. Lifted from Resources.GPU.Attributes by the + // manifest builder so the off-chain workload builder can label pods + // for per-group anti-affinity. Services sharing the same value form + // one NCCL peer group; the provider schedules them on distinct nodes. + // Empty when the service is not part of any GPU interconnect group. + // + // JSON / YAML tags carry `omitempty`: the on-chain manifest `version` + // is a SHA hash of the JSON-serialized off-chain manifest, so any + // field that always serializes (even at zero value) would shift the + // hash for every non-interconnect SDL and break send-manifest + // validation on existing leases. + InterconnectGroup string `protobuf:"bytes,11,opt,name=interconnect_group,json=interconnectGroup,proto3" json:"interconnectGroup,omitempty" yaml:"interconnectGroup,omitempty"` } func (m *Service) Reset() { *m = Service{} } @@ -376,6 +389,13 @@ func (m *Service) GetCredentials() *ImageCredentials { return nil } +func (m *Service) GetInterconnectGroup() string { + if m != nil { + return m.InterconnectGroup + } + return "" +} + func init() { proto.RegisterType((*StorageParams)(nil), "akash.manifest.v2beta3.StorageParams") proto.RegisterType((*ServicePermissions)(nil), "akash.manifest.v2beta3.ServicePermissions") @@ -389,58 +409,61 @@ func init() { } var fileDescriptor_6d5964c4976d68e5 = []byte{ - // 812 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0x3b, 0x6f, 0x2b, 0x45, - 0x14, 0xf6, 0x62, 0xc7, 0x8f, 0x31, 0x0e, 0xd1, 0x88, 0xcb, 0xdd, 0x1b, 0xc1, 0x8e, 0x19, 0x71, - 0x85, 0xb9, 0x89, 0x76, 0x95, 0x04, 0x09, 0x89, 0x87, 0x10, 0x8b, 0x28, 0xa8, 0x88, 0x26, 0xa2, - 0xa1, 0x41, 0x63, 0x7b, 0x70, 0x56, 0xf1, 0x3e, 0xb4, 0xb3, 0x36, 0xa4, 0xa3, 0x45, 0x08, 0x89, - 0xdf, 0x01, 0x7f, 0x24, 0x65, 0xca, 0x54, 0x03, 0x38, 0x9d, 0xcb, 0x2d, 0xa8, 0xd1, 0x3c, 0x36, - 0xb3, 0x26, 0x0e, 0x11, 0xba, 0x55, 0x72, 0xbe, 0xf3, 0x9d, 0x73, 0x66, 0xce, 0x7e, 0xf3, 0x19, - 0xbc, 0x43, 0x2f, 0x28, 0x3f, 0x0f, 0x62, 0x9a, 0x44, 0xdf, 0x31, 0x5e, 0x04, 0xcb, 0xe3, 0x31, - 0x2b, 0xe8, 0x49, 0xc0, 0x59, 0xbe, 0x8c, 0x26, 0xcc, 0xcf, 0xf2, 0xb4, 0x48, 0xe1, 0x1b, 0x8a, - 0xe5, 0x57, 0x2c, 0xdf, 0xb0, 0xf6, 0x5f, 0x9f, 0xa5, 0xb3, 0x54, 0x51, 0x02, 0xf9, 0x9f, 0x66, - 0xef, 0xbf, 0xf8, 0xef, 0x9e, 0xec, 0x87, 0x2c, 0xe5, 0xa6, 0xf3, 0xfe, 0xa1, 0xe6, 0x8e, 0x29, - 0x67, 0x41, 0xce, 0x78, 0xba, 0xc8, 0x27, 0x8c, 0x07, 0xcb, 0x23, 0x59, 0xf1, 0xbe, 0x45, 0x34, - 0x1b, 0xff, 0xee, 0x80, 0xc1, 0x59, 0x91, 0xe6, 0x74, 0xc6, 0x4e, 0x69, 0x4e, 0x63, 0x0e, 0x0f, - 0x40, 0x2b, 0xa1, 0x31, 0x73, 0x9d, 0xa1, 0x33, 0xea, 0x85, 0x4f, 0xd7, 0x02, 0xa9, 0xb8, 0x14, - 0xa8, 0x7f, 0x49, 0xe3, 0xf9, 0x87, 0x58, 0x46, 0x98, 0x28, 0x10, 0x06, 0x60, 0x27, 0x4e, 0x17, - 0x49, 0xe1, 0xbe, 0xa2, 0xd8, 0xcf, 0xd6, 0x02, 0x69, 0xa0, 0x14, 0xe8, 0x55, 0x4d, 0x57, 0x21, - 0x26, 0x1a, 0x86, 0x1f, 0x83, 0x5e, 0xce, 0xe8, 0xf4, 0xdb, 0x34, 0x99, 0x5f, 0xba, 0xcd, 0xa1, - 0x33, 0xea, 0x86, 0x68, 0x2d, 0x50, 0x57, 0x82, 0x5f, 0x25, 0xf3, 0xcb, 0x52, 0xa0, 0xd7, 0x74, - 0x5d, 0x85, 0x60, 0x72, 0x97, 0xc4, 0x5f, 0x03, 0x78, 0xa6, 0xaf, 0x7c, 0xca, 0xf2, 0x38, 0xe2, - 0x3c, 0x4a, 0x13, 0x0e, 0x3f, 0x05, 0x2d, 0xc9, 0x70, 0x9d, 0x61, 0x73, 0xd4, 0x0b, 0x0f, 0xd6, - 0x02, 0xed, 0xca, 0xf8, 0x30, 0x8d, 0xa3, 0x82, 0xc5, 0x59, 0x21, 0x9b, 0x3e, 0xb1, 0x4d, 0x2d, - 0x8e, 0x89, 0x2a, 0xc4, 0xbf, 0x34, 0xc1, 0xa0, 0xea, 0xab, 0x97, 0x30, 0x06, 0x1d, 0xae, 0xb7, - 0xa2, 0xba, 0xf6, 0x8f, 0x9f, 0xfb, 0xdb, 0x3f, 0x98, 0xbf, 0xb1, 0xbc, 0xf0, 0xed, 0x2b, 0x81, - 0x1a, 0x6b, 0x81, 0xaa, 0xea, 0x52, 0xa0, 0x5d, 0x3d, 0xd9, 0x00, 0x98, 0x54, 0x29, 0xf8, 0x93, - 0x03, 0xfa, 0x93, 0x9c, 0x4d, 0x59, 0x52, 0x44, 0x74, 0xce, 0x5d, 0x30, 0x74, 0x46, 0xfd, 0xe3, - 0xd1, 0x43, 0x83, 0xbe, 0x8c, 0xe9, 0x8c, 0x7d, 0x6e, 0xf9, 0xe1, 0x27, 0x57, 0x02, 0x39, 0x6b, - 0x81, 0x9e, 0xd4, 0x9a, 0x6c, 0xdc, 0xf9, 0x4d, 0x3d, 0x79, 0x6b, 0x1a, 0x93, 0xfa, 0x6c, 0xf8, - 0xb3, 0x03, 0xfa, 0x99, 0x5d, 0xa9, 0xdb, 0x57, 0x67, 0x79, 0xf1, 0xe0, 0xa5, 0xef, 0x7d, 0x04, - 0x7b, 0x9a, 0x5a, 0x9b, 0x6d, 0xa7, 0xd9, 0x9a, 0xc6, 0xa4, 0x3e, 0x1d, 0xaf, 0x1d, 0xb0, 0xf7, - 0xef, 0xeb, 0x4a, 0x5d, 0x9e, 0xa7, 0xbc, 0xa8, 0xeb, 0x52, 0xc6, 0x56, 0x97, 0x32, 0xc2, 0x44, - 0x81, 0x52, 0x97, 0x2c, 0xa6, 0xd1, 0xbc, 0xae, 0x4b, 0x05, 0x58, 0x5d, 0xaa, 0x10, 0x13, 0x0d, - 0xc3, 0x8f, 0x40, 0x77, 0xc1, 0x59, 0xae, 0x94, 0xdf, 0x54, 0x35, 0x4a, 0x96, 0x15, 0x66, 0x65, - 0x59, 0x21, 0x98, 0xdc, 0x25, 0x65, 0x71, 0x46, 0x39, 0xff, 0x3e, 0xcd, 0xa7, 0x6e, 0xcb, 0x16, - 0x57, 0x98, 0x2d, 0xae, 0x10, 0x4c, 0xee, 0x92, 0xf8, 0xef, 0x1d, 0xd0, 0x31, 0xfb, 0xfc, 0xdf, - 0x6f, 0x2f, 0x92, 0x4b, 0xaa, 0xdf, 0x51, 0x01, 0xf6, 0x8e, 0x2a, 0xc4, 0x44, 0xc3, 0xf0, 0x03, - 0xd0, 0x99, 0xa4, 0x71, 0x4c, 0x93, 0xa9, 0xdb, 0x54, 0x4f, 0xe5, 0x2d, 0xa9, 0x54, 0x03, 0x59, - 0xa5, 0x1a, 0x00, 0x93, 0x2a, 0x25, 0x8f, 0x45, 0xf3, 0x19, 0x77, 0x5b, 0xaa, 0x4a, 0x1d, 0x4b, - 0xc6, 0xf6, 0x58, 0x32, 0xc2, 0x44, 0x81, 0xf0, 0x00, 0x34, 0x59, 0xb2, 0x74, 0x77, 0x14, 0xf7, - 0x99, 0x51, 0x85, 0x84, 0x4a, 0x81, 0x80, 0x59, 0x7d, 0xb2, 0xc4, 0x44, 0x42, 0x70, 0x2e, 0xed, - 0xc0, 0x38, 0x92, 0xdb, 0x56, 0xa2, 0x7b, 0xd7, 0x88, 0x4e, 0x1a, 0x98, 0x6f, 0xed, 0xca, 0x18, - 0x98, 0x4f, 0x2a, 0x24, 0x7c, 0x6e, 0xde, 0x9a, 0xed, 0x50, 0x0a, 0xb4, 0x57, 0xbd, 0x73, 0x03, - 0x61, 0x62, 0xd3, 0x72, 0x63, 0x13, 0xe5, 0x56, 0x9d, 0xa1, 0x33, 0x1a, 0xe8, 0x8d, 0x4d, 0x36, - 0xdd, 0x6a, 0x62, 0xdc, 0x4a, 0xfd, 0x85, 0x19, 0x68, 0x6b, 0x6f, 0x75, 0xbb, 0x8f, 0xb8, 0x80, - 0xfe, 0x80, 0x5f, 0x28, 0x72, 0x78, 0x64, 0x4e, 0x66, 0x8a, 0x4b, 0x81, 0x06, 0xe6, 0xe2, 0x2a, - 0xc6, 0xbf, 0xfd, 0x81, 0x76, 0x37, 0x2a, 0x38, 0x31, 0x54, 0x98, 0x83, 0x76, 0xa6, 0xac, 0xc4, - 0xed, 0xa9, 0x6d, 0x3c, 0x36, 0xd1, 0xf8, 0xce, 0x89, 0xd9, 0xf3, 0x9e, 0x2e, 0xde, 0x78, 0x78, - 0x4f, 0x2b, 0xed, 0x6d, 0x66, 0x30, 0x31, 0x93, 0xe0, 0xe2, 0xe5, 0x7c, 0xe8, 0x3d, 0x33, 0xbb, - 0xde, 0xa4, 0x14, 0x08, 0xde, 0x73, 0x9f, 0x4d, 0xcf, 0x09, 0x3f, 0xbb, 0xf9, 0xcb, 0x6b, 0xfc, - 0xb8, 0xf2, 0x9c, 0xab, 0x95, 0xe7, 0x5c, 0xaf, 0x3c, 0xe7, 0xcf, 0x95, 0xe7, 0xfc, 0x7a, 0xeb, - 0x35, 0xae, 0x6f, 0xbd, 0xc6, 0xcd, 0xad, 0xd7, 0xf8, 0x06, 0x65, 0x17, 0x33, 0x9f, 0x5e, 0x14, - 0xfe, 0x94, 0x2d, 0x83, 0x59, 0x7a, 0xef, 0xf7, 0x6f, 0xdc, 0x56, 0x3f, 0x62, 0x27, 0xff, 0x04, - 0x00, 0x00, 0xff, 0xff, 0xf4, 0x8d, 0xfc, 0x75, 0x74, 0x07, 0x00, 0x00, + // 864 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xcf, 0x6f, 0x1b, 0x45, + 0x14, 0xf6, 0xe0, 0x34, 0x8e, 0xc7, 0x24, 0xa4, 0x23, 0x4a, 0xb7, 0x01, 0x3c, 0x66, 0x44, 0x85, + 0x69, 0xaa, 0xb5, 0x9a, 0x20, 0x21, 0xf1, 0x43, 0x88, 0x45, 0x08, 0xf5, 0x44, 0x35, 0x15, 0x97, + 0x5e, 0xaa, 0xc9, 0x7a, 0x70, 0x57, 0xf1, 0xee, 0xac, 0x76, 0xd6, 0x86, 0xdc, 0x10, 0xb7, 0x0a, + 0x21, 0xf1, 0x77, 0xc0, 0x3f, 0x92, 0x63, 0x8f, 0x3d, 0x0d, 0xe0, 0xdc, 0xf6, 0xb8, 0x7f, 0x01, + 0x9a, 0x1f, 0x9b, 0x59, 0xe7, 0x07, 0x15, 0xe2, 0x94, 0xbc, 0xef, 0x7d, 0xef, 0xcd, 0xbc, 0xb7, + 0xdf, 0x7c, 0x86, 0xef, 0xb3, 0x63, 0x26, 0x9f, 0x4d, 0x52, 0x96, 0x25, 0xdf, 0x73, 0x59, 0x4e, + 0x96, 0x07, 0x47, 0xbc, 0x64, 0x87, 0x13, 0xc9, 0x8b, 0x65, 0x12, 0xf3, 0x30, 0x2f, 0x44, 0x29, + 0xd0, 0x5b, 0x86, 0x15, 0x36, 0xac, 0xd0, 0xb1, 0xf6, 0xde, 0x9c, 0x89, 0x99, 0x30, 0x94, 0x89, + 0xfe, 0xcf, 0xb2, 0xf7, 0xee, 0xfd, 0x7b, 0x4f, 0xfe, 0x63, 0x2e, 0xa4, 0xeb, 0xbc, 0x77, 0xdf, + 0x72, 0x8f, 0x98, 0xe4, 0x93, 0x82, 0x4b, 0xb1, 0x28, 0x62, 0x2e, 0x27, 0xcb, 0x07, 0xba, 0xe2, + 0x23, 0x8f, 0x58, 0x36, 0xf9, 0x03, 0xc0, 0xed, 0xc7, 0xa5, 0x28, 0xd8, 0x8c, 0x3f, 0x62, 0x05, + 0x4b, 0x25, 0xda, 0x87, 0x1b, 0x19, 0x4b, 0x79, 0x00, 0x46, 0x60, 0xdc, 0x8f, 0x6e, 0x57, 0x0a, + 0x9b, 0xb8, 0x56, 0x78, 0x70, 0xc2, 0xd2, 0xf9, 0x27, 0x44, 0x47, 0x84, 0x1a, 0x10, 0x4d, 0xe0, + 0x8d, 0x54, 0x2c, 0xb2, 0x32, 0x78, 0xcd, 0xb0, 0xef, 0x54, 0x0a, 0x5b, 0xa0, 0x56, 0xf8, 0x75, + 0x4b, 0x37, 0x21, 0xa1, 0x16, 0x46, 0x9f, 0xc1, 0x7e, 0xc1, 0xd9, 0xf4, 0xa9, 0xc8, 0xe6, 0x27, + 0x41, 0x77, 0x04, 0xc6, 0x5b, 0x11, 0xae, 0x14, 0xde, 0xd2, 0xe0, 0xb7, 0xd9, 0xfc, 0xa4, 0x56, + 0xf8, 0x0d, 0x5b, 0xd7, 0x20, 0x84, 0x9e, 0x27, 0xc9, 0x77, 0x10, 0x3d, 0xb6, 0x23, 0x3f, 0xe2, + 0x45, 0x9a, 0x48, 0x99, 0x88, 0x4c, 0xa2, 0x2f, 0xe0, 0x86, 0x66, 0x04, 0x60, 0xd4, 0x1d, 0xf7, + 0xa3, 0xfd, 0x4a, 0xe1, 0x1d, 0x1d, 0xdf, 0x17, 0x69, 0x52, 0xf2, 0x34, 0x2f, 0x75, 0xd3, 0x5b, + 0xbe, 0xa9, 0xc7, 0x09, 0x35, 0x85, 0xe4, 0xd7, 0x2e, 0xdc, 0x6e, 0xfa, 0xda, 0x25, 0x1c, 0xc1, + 0x9e, 0xb4, 0x5b, 0x31, 0x5d, 0x07, 0x07, 0x77, 0xc3, 0xab, 0x3f, 0x58, 0xb8, 0xb6, 0xbc, 0xe8, + 0xbd, 0x53, 0x85, 0x3b, 0x95, 0xc2, 0x4d, 0x75, 0xad, 0xf0, 0x8e, 0x3d, 0xd9, 0x01, 0x84, 0x36, + 0x29, 0xf4, 0x1c, 0xc0, 0x41, 0x5c, 0xf0, 0x29, 0xcf, 0xca, 0x84, 0xcd, 0x65, 0x00, 0x47, 0x60, + 0x3c, 0x38, 0x18, 0x5f, 0x77, 0xd0, 0xc3, 0x94, 0xcd, 0xf8, 0x57, 0x9e, 0x1f, 0x7d, 0x7e, 0xaa, + 0x30, 0xa8, 0x14, 0xbe, 0xd5, 0x6a, 0xb2, 0x36, 0xf3, 0x3b, 0xf6, 0xe4, 0x2b, 0xd3, 0x84, 0xb6, + 0xcf, 0x46, 0xbf, 0x00, 0x38, 0xc8, 0xfd, 0x4a, 0x83, 0x81, 0xb9, 0xcb, 0xbd, 0x6b, 0x87, 0xbe, + 0xf4, 0x11, 0xfc, 0x6d, 0x5a, 0x6d, 0xae, 0xba, 0xcd, 0x95, 0x69, 0x42, 0xdb, 0xa7, 0x93, 0x0a, + 0xc0, 0xdd, 0x8b, 0xe3, 0x6a, 0x5d, 0x3e, 0x13, 0xb2, 0x6c, 0xeb, 0x52, 0xc7, 0x5e, 0x97, 0x3a, + 0x22, 0xd4, 0x80, 0x5a, 0x97, 0x3c, 0x65, 0xc9, 0xbc, 0xad, 0x4b, 0x03, 0x78, 0x5d, 0x9a, 0x90, + 0x50, 0x0b, 0xa3, 0x4f, 0xe1, 0xd6, 0x42, 0xf2, 0xc2, 0x28, 0xbf, 0x6b, 0x6a, 0x8c, 0x2c, 0x1b, + 0xcc, 0xcb, 0xb2, 0x41, 0x08, 0x3d, 0x4f, 0xea, 0xe2, 0x9c, 0x49, 0xf9, 0x83, 0x28, 0xa6, 0xc1, + 0x86, 0x2f, 0x6e, 0x30, 0x5f, 0xdc, 0x20, 0x84, 0x9e, 0x27, 0xc9, 0xcf, 0x3d, 0xd8, 0x73, 0xfb, + 0xfc, 0xcf, 0x6f, 0x2f, 0xd1, 0x4b, 0x6a, 0xcf, 0x68, 0x00, 0x3f, 0xa3, 0x09, 0x09, 0xb5, 0x30, + 0xfa, 0x18, 0xf6, 0x62, 0x91, 0xa6, 0x2c, 0x9b, 0x06, 0x5d, 0xf3, 0x54, 0xde, 0xd5, 0x4a, 0x75, + 0x90, 0x57, 0xaa, 0x03, 0x08, 0x6d, 0x52, 0xfa, 0x5a, 0xac, 0x98, 0xc9, 0x60, 0xc3, 0x54, 0x99, + 0x6b, 0xe9, 0xd8, 0x5f, 0x4b, 0x47, 0x84, 0x1a, 0x10, 0xed, 0xc3, 0x2e, 0xcf, 0x96, 0xc1, 0x0d, + 0xc3, 0xbd, 0xe3, 0x54, 0xa1, 0xa1, 0x5a, 0x61, 0xe8, 0x56, 0x9f, 0x2d, 0x09, 0xd5, 0x10, 0x9a, + 0x6b, 0x3b, 0x70, 0x8e, 0x14, 0x6c, 0x1a, 0xd1, 0x7d, 0xe0, 0x44, 0xa7, 0x0d, 0x2c, 0xf4, 0x76, + 0xe5, 0x0c, 0x2c, 0xa4, 0x0d, 0x12, 0xdd, 0x75, 0x6f, 0xcd, 0x77, 0xa8, 0x15, 0xde, 0x6d, 0xde, + 0xb9, 0x83, 0x08, 0xf5, 0x69, 0xbd, 0xb1, 0xd8, 0xb8, 0x55, 0x6f, 0x04, 0xc6, 0xdb, 0x76, 0x63, + 0xf1, 0xba, 0x5b, 0xc5, 0xce, 0xad, 0xcc, 0x5f, 0x94, 0xc3, 0x4d, 0xeb, 0xad, 0xc1, 0xd6, 0x2b, + 0x5c, 0xc0, 0x7e, 0xc0, 0xaf, 0x0d, 0x39, 0x7a, 0xe0, 0x6e, 0xe6, 0x8a, 0x6b, 0x85, 0xb7, 0xdd, + 0xe0, 0x26, 0x26, 0xbf, 0xff, 0x89, 0x77, 0xd6, 0x2a, 0x24, 0x75, 0x54, 0x54, 0xc0, 0xcd, 0xdc, + 0x58, 0x49, 0xd0, 0x37, 0xdb, 0x78, 0xd5, 0x89, 0xce, 0x77, 0x0e, 0xdd, 0x9e, 0x77, 0x6d, 0xf1, + 0xda, 0xc3, 0xbb, 0xdd, 0x68, 0x6f, 0x3d, 0x43, 0xa8, 0x3b, 0x09, 0x2d, 0xfe, 0x9f, 0x0f, 0x7d, + 0xe8, 0xce, 0x6e, 0x37, 0xa9, 0x15, 0x46, 0x97, 0xdc, 0xe7, 0x82, 0xe7, 0x3c, 0x07, 0x10, 0x25, + 0x59, 0xc9, 0x8b, 0x58, 0x64, 0x19, 0x8f, 0xcb, 0xa7, 0xb3, 0x42, 0x2c, 0x72, 0x63, 0x3d, 0xfd, + 0xe8, 0xc9, 0x4a, 0xe1, 0x9b, 0x0f, 0x5b, 0xd9, 0x6f, 0x74, 0xb2, 0x52, 0xf8, 0xed, 0xe4, 0x22, + 0xb8, 0x36, 0x2c, 0x71, 0xc2, 0xbf, 0x9e, 0x44, 0xe8, 0xcd, 0x4b, 0xd9, 0xe8, 0xcb, 0x97, 0x7f, + 0x0f, 0x3b, 0x3f, 0xad, 0x86, 0xe0, 0x74, 0x35, 0x04, 0x2f, 0x56, 0x43, 0xf0, 0xd7, 0x6a, 0x08, + 0x7e, 0x3b, 0x1b, 0x76, 0x5e, 0x9c, 0x0d, 0x3b, 0x2f, 0xcf, 0x86, 0x9d, 0x27, 0x38, 0x3f, 0x9e, + 0x85, 0xec, 0xb8, 0x0c, 0xa7, 0x7c, 0x39, 0x99, 0x89, 0x4b, 0xbf, 0xc5, 0x47, 0x9b, 0xe6, 0x07, + 0xf5, 0xf0, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x30, 0xb7, 0xa2, 0x87, 0x00, 0x08, 0x00, 0x00, } func (m *StorageParams) Marshal() (dAtA []byte, err error) { @@ -654,6 +677,13 @@ func (m *Service) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.InterconnectGroup) > 0 { + i -= len(m.InterconnectGroup) + copy(dAtA[i:], m.InterconnectGroup) + i = encodeVarintService(dAtA, i, uint64(len(m.InterconnectGroup))) + i-- + dAtA[i] = 0x5a + } if m.Credentials != nil { { size, err := m.Credentials.MarshalToSizedBuffer(dAtA[:i]) @@ -896,6 +926,10 @@ func (m *Service) Size() (n int) { l = m.Credentials.Size() n += 1 + l + sovService(uint64(l)) } + l = len(m.InterconnectGroup) + if l > 0 { + n += 1 + l + sovService(uint64(l)) + } return n } @@ -977,6 +1011,7 @@ func (this *Service) String() string { `Expose:` + repeatedStringForExpose + `,`, `Params:` + strings.Replace(this.Params.String(), "ServiceParams", "ServiceParams", 1) + `,`, `Credentials:` + strings.Replace(this.Credentials.String(), "ImageCredentials", "ImageCredentials", 1) + `,`, + `InterconnectGroup:` + fmt.Sprintf("%v", this.InterconnectGroup) + `,`, `}`, }, "") return s @@ -1886,6 +1921,38 @@ func (m *Service) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InterconnectGroup", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InterconnectGroup = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) diff --git a/go/node/deployment/v1beta4/interconnect_commit_audit_test.go b/go/node/deployment/v1beta4/interconnect_commit_audit_test.go new file mode 100644 index 00000000..eac50fd9 --- /dev/null +++ b/go/node/deployment/v1beta4/interconnect_commit_audit_test.go @@ -0,0 +1,192 @@ +package v1beta4 + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + attr "pkg.akt.dev/go/node/types/attributes/v1" + rtypes "pkg.akt.dev/go/node/types/resources/v1beta4" +) + +// CS-6: the provider reservation/commit path on the provider side rebuilds +// GroupSpec instances from parts and feeds the rebuilt value into the +// inventory.Adjust path. The provider's interconnect-detection logic reads +// +// - GroupSpec.Requirements.Attributes (carrying capabilities/gpu-interconnect=true) +// - Resources.GPU.Attributes (carrying interconnect=true per resource) +// +// from whatever ResourceGroup-typed value lands on its `reservation.Resources()` +// call. If anything in the chain SDK's Dup / accessor chain drops either of +// those attribute slices, the provider silently treats the order as +// non-interconnect. These tests pin down preservation for every concrete +// ResourceGroup the provider may hold: +// +// - *Group (the bid-engine path stores a *dtypes.Group) +// - Group (value form, dereferenced) +// - *GroupSpec (used by tests and by some legacy adapter paths) +// - GroupSpec (value form returned by Group.GroupSpec) +// +// We assert that a representative Group carrying capabilities/gpu-interconnect=true on +// Requirements and interconnect=true on the first resource's GPU.Attributes survives: +// +// 1. (Group).Dup()-equivalent — via GroupSpec.Dup(). +// 2. Accessing the value via the ResourceGroup interface (GetResourceUnits). +// 3. Direct field reads of Requirements.Attributes from any of the four +// concrete shapes. + +func interconnectSampleGroupSpec() GroupSpec { + return GroupSpec{ + Name: "ib", + Requirements: attr.PlacementRequirements{ + Attributes: attr.Attributes{ + {Key: "capabilities/gpu-interconnect", Value: "true"}, + {Key: "capabilities/gpu-interconnect/fabric/infiniband", Value: "true"}, + }, + }, + Resources: ResourceUnits{ + { + Resources: rtypes.Resources{ + ID: 1, + CPU: &rtypes.CPU{ + Units: rtypes.NewResourceValue(1000), + }, + Memory: &rtypes.Memory{ + Quantity: rtypes.NewResourceValue(1024 * 1024 * 1024), + }, + GPU: &rtypes.GPU{ + Units: rtypes.NewResourceValue(8), + Attributes: attr.Attributes{ + {Key: "interconnect", Value: "true"}, + {Key: "vendor/nvidia/model/a100", Value: "true"}, + }, + }, + Storage: rtypes.Volumes{}, + }, + Count: 1, + Price: sdk.NewDecCoin("uact", sdkmath.NewInt(1)), + }, + }, + } +} + +func assertInterconnectSignalsPresent(t *testing.T, where string, reqs attr.Attributes, gpu attr.Attributes) { + t.Helper() + + hasPlacementInterconnect := false + for _, a := range reqs { + if a.Key == "capabilities/gpu-interconnect" && a.Value == "true" { + hasPlacementInterconnect = true + } + } + require.True(t, hasPlacementInterconnect, "%s: Requirements lost capabilities/gpu-interconnect=true", where) + + hasGPUInterconnect := false + for _, a := range gpu { + if a.Key == "interconnect" && a.Value == "true" { + hasGPUInterconnect = true + } + } + require.True(t, hasGPUInterconnect, "%s: per-resource GPU.Attributes lost interconnect=true", where) +} + +// TestCS6_InterconnectSignalsSurviveDup is the canonical regression test: drive the +// representative GroupSpec through Dup() and assert both attribute slices +// survive verbatim. If a future change to Resources.Dup() / Requirements.Dup() +// drops attributes, this fails loudly. +func TestCS6_InterconnectSignalsSurviveDup(t *testing.T) { + src := interconnectSampleGroupSpec() + dup := src.Dup() + + require.NotSame(t, &src, &dup) + assertInterconnectSignalsPresent(t, "GroupSpec.Dup result", + dup.Requirements.Attributes, + dup.Resources[0].Resources.GPU.Attributes, + ) + + // And mutating the dup must not poison the source. + dup.Requirements.Attributes[0].Value = "false" + dup.Resources[0].Resources.GPU.Attributes[0].Value = "false" + assertInterconnectSignalsPresent(t, "source after mutating dup", + src.Requirements.Attributes, + src.Resources[0].Resources.GPU.Attributes, + ) +} + +// TestCS6_InterconnectSignalsAcrossConcreteTypes exercises the four concrete +// ResourceGroup-shaped values the provider's reservation/commit path can +// hold. Each row asserts that the interconnect signals are reachable via the +// type-specific accessors the provider uses. +func TestCS6_InterconnectSignalsAcrossConcreteTypes(t *testing.T) { + specVal := interconnectSampleGroupSpec() + specPtr := &specVal + + groupVal := Group{ + GroupSpec: specVal, + } + groupPtr := &groupVal + + tests := []struct { + name string + // readers translate the type-specific access pattern the provider + // uses for that input shape into the two attribute slices we care + // about. The provider's helpers do exactly this. + readReqs func() attr.Attributes + readGPU func() attr.Attributes + }{ + { + name: "*GroupSpec", + readReqs: func() attr.Attributes { return specPtr.Requirements.Attributes }, + readGPU: func() attr.Attributes { return specPtr.Resources[0].Resources.GPU.Attributes }, + }, + { + name: "GroupSpec value", + readReqs: func() attr.Attributes { return specVal.Requirements.Attributes }, + readGPU: func() attr.Attributes { return specVal.Resources[0].Resources.GPU.Attributes }, + }, + { + name: "*Group", + readReqs: func() attr.Attributes { return groupPtr.GroupSpec.Requirements.Attributes }, + readGPU: func() attr.Attributes { return groupPtr.GroupSpec.Resources[0].Resources.GPU.Attributes }, + }, + { + name: "Group value", + readReqs: func() attr.Attributes { return groupVal.GroupSpec.Requirements.Attributes }, + readGPU: func() attr.Attributes { return groupVal.GroupSpec.Resources[0].Resources.GPU.Attributes }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assertInterconnectSignalsPresent(t, tc.name, tc.readReqs(), tc.readGPU()) + }) + } +} + +// TestCS6_GetResourceUnitsPreservesGPUAttributes exercises the +// ResourceGroup-interface path the provider's commit code uses. The +// provider iterates GetResourceUnits() and reads each unit's GPU.Attributes; +// a regression where this collection silently drops attributes would +// translate to interconnect opt-in disappearing on the wire. +func TestCS6_GetResourceUnitsPreservesGPUAttributes(t *testing.T) { + spec := interconnectSampleGroupSpec() + + units := spec.GetResourceUnits() + require.NotEmpty(t, units, "GetResourceUnits returned empty") + + hasGPUInterconnect := false + for _, u := range units { + if u.Resources.GPU == nil { + continue + } + for _, a := range u.Resources.GPU.Attributes { + if a.Key == "interconnect" && a.Value == "true" { + hasGPUInterconnect = true + } + } + } + require.True(t, hasGPUInterconnect, + "GetResourceUnits() dropped GPU.Attributes — provider would treat as non-interconnect") +} diff --git a/go/sdl/gpu.go b/go/sdl/gpu.go index 17a730cd..1caa7dc6 100644 --- a/go/sdl/gpu.go +++ b/go/sdl/gpu.go @@ -38,9 +38,34 @@ type gpuVendor struct { type v2GPUAttributes types.Attributes +// GPUAttributeInterconnect is the on-chain GPU-attribute key emitted when +// an SDL compute profile declares gpu.attributes.interconnect: true. +// Providers advertising GPU-interconnect-capable hardware match this +// attribute via the standard GPU MatchResourcesRequirements path. The +// fabric (InfiniBand vs RoCE) is hidden from the SDL surface; the +// provider picks whichever it has. +const GPUAttributeInterconnect = "interconnect" + +// GPUAttributeInterconnectGroup is the on-chain GPU-attribute key emitted +// when an SDL compute profile declares gpu.attributes.interconnect_group: +// . Carries the peer-group label all the way into the on-chain +// Resources.GPU.attributes so the provider's bid engine can enforce +// per-group node separation at fit time (Service.InterconnectGroup +// off-chain alone would leave the bid step blind). The value is also +// lifted into Service.InterconnectGroup so the workload builder's pod +// anti-affinity rule keys off the same string. +const GPUAttributeInterconnectGroup = "interconnect_group" + type v2ResourceGPU struct { Units gpuQuantity `yaml:"units" json:"units"` Attributes v2GPUAttributes `yaml:"attributes,omitempty" json:"attributes,omitempty"` + + // InterconnectGroup carries the parsed gpu.attributes.interconnect_group + // value. The same value is also present in Attributes (as + // GPUAttributeInterconnectGroup); this field exists so the + // higher-level manifest builder can route it to + // Service.InterconnectGroup without re-walking the slice. + InterconnectGroup string `yaml:"-" json:"-"` } func (sdl *v2ResourceGPU) UnmarshalYAML(node *yaml.Node) error { @@ -61,10 +86,50 @@ func (sdl *v2ResourceGPU) UnmarshalYAML(node *yaml.Node) error { } } + // Lift the interconnect_group attribute value into the dedicated + // InterconnectGroup field for downstream manifest builders, but KEEP + // it in the attributes slice — the provider's bid engine consumes the + // on-chain Resources.GPU.Attributes and needs interconnect_group + // present there to enforce per-group node separation during + // reservation. + if len(res.Attributes) > 0 { + for _, a := range res.Attributes { + if a.Key == GPUAttributeInterconnectGroup { + res.InterconnectGroup = a.Value + break + } + } + + // v2GPUAttributes.UnmarshalYAML defers Validate to here so the + // final attribute slice (including the interconnect_group key, + // which matches the on-chain attribute key regex) gets one + // validate pass. + final := types.Attributes(res.Attributes) + if err := final.Validate(); err != nil { + return fmt.Errorf("sdl: invalid GPU attributes: %w", err) + } + } + if res.Units > 0 && len(res.Attributes) == 0 { return fmt.Errorf("sdl: GPU attributes must be present if units > 0") } + // CS-5 invariant, enforced here so the SDL fails fast: interconnect + // and interconnect_group are nonsense without an actual GPU to attach + // an HCA to. A profile declaring interconnect: true or + // interconnect_group: with gpu.units == 0 would otherwise be + // classified as interconnect-enabled by downstream validation passes + // and the provider's reservation logic, then rejected much later (or, + // worse, treated as a misconfiguration). Reject up front. + if res.Units == 0 { + if gpuAttributesHaveInterconnect(res.Attributes) { + return fmt.Errorf("sdl: gpu.attributes.interconnect cannot be set when gpu.units == 0") + } + if res.InterconnectGroup != "" { + return fmt.Errorf("sdl: gpu.attributes.interconnect_group=%q cannot be set when gpu.units == 0", res.InterconnectGroup) + } + } + *sdl = res return nil @@ -74,6 +139,8 @@ func (sdl *v2GPUAttributes) UnmarshalYAML(node *yaml.Node) error { var res types.Attributes var vendor *gpuVendor + interconnectEnabled := false + interconnectGroup := "" for i := 0; i < len(node.Content); i += 2 { switch node.Content[i].Value { @@ -81,6 +148,26 @@ func (sdl *v2GPUAttributes) UnmarshalYAML(node *yaml.Node) error { if err := node.Content[i+1].Decode(&vendor); err != nil { return err } + case "interconnect": + // gpu.attributes.interconnect: bool (default false). When + // true, emit an on-chain GPU attribute so providers + // advertising GPU-interconnect-capable hardware can be + // matched. Fabric (IB vs RoCE) is hidden from the SDL — + // provider picks whichever it has. + var ic bool + if err := node.Content[i+1].Decode(&ic); err != nil { + return fmt.Errorf("sdl: invalid value for gpu.attributes.interconnect: %w", err) + } + interconnectEnabled = ic + case "interconnect_group": + // gpu.attributes.interconnect_group: string (peer group + // name). Emitted as an on-chain GPU attribute alongside + // `interconnect=true` so the provider's bid engine can + // track per-group node claims. Also lifted to + // v2ResourceGPU.InterconnectGroup for the manifest builder. + if err := node.Content[i+1].Decode(&interconnectGroup); err != nil { + return fmt.Errorf("sdl: invalid value for gpu.attributes.interconnect_group: %w", err) + } default: return fmt.Errorf("sdl: unsupported attribute (%s) for GPU resource", node.Content[i].Value) } @@ -90,7 +177,7 @@ func (sdl *v2GPUAttributes) UnmarshalYAML(node *yaml.Node) error { return fmt.Errorf("sdl: invalid GPU attributes. at least one vendor must be set") } - res = make(types.Attributes, 0, len(vendor.Nvidia)) + res = make(types.Attributes, 0, len(vendor.Nvidia)+2) for _, model := range vendor.Nvidia { res = append(res, types.Attribute{ @@ -106,12 +193,32 @@ func (sdl *v2GPUAttributes) UnmarshalYAML(node *yaml.Node) error { }) } - sort.Sort(res) + if interconnectEnabled { + res = append(res, types.Attribute{ + Key: GPUAttributeInterconnect, + Value: "true", + }) + } - if err := res.Validate(); err != nil { - return fmt.Errorf("sdl: invalid GPU attributes: %w", err) + // Emit interconnect_group directly as an on-chain GPU attribute. The + // provider's reservation Adjust step reads this to enforce per-group + // node separation; the parent v2ResourceGPU.UnmarshalYAML also lifts + // the value into v2ResourceGPU.InterconnectGroup so the manifest + // builder can route it to Service.InterconnectGroup for the off-chain + // workload builder. + if interconnectGroup != "" { + res = append(res, types.Attribute{ + Key: GPUAttributeInterconnectGroup, + Value: interconnectGroup, + }) } + sort.Sort(res) + + // Validate() is deferred to v2ResourceGPU.UnmarshalYAML so the parent + // hook can lift interconnect_group into the dedicated field before + // the final attribute-key regex runs across the slice. + *sdl = v2GPUAttributes(res) return nil diff --git a/go/sdl/groupBuilder_v2.go b/go/sdl/groupBuilder_v2.go index 29e4ea37..2852c934 100644 --- a/go/sdl/groupBuilder_v2.go +++ b/go/sdl/groupBuilder_v2.go @@ -97,6 +97,15 @@ func (sdl *v2) buildGroups() error { Expose: expose, } + // CS-3: gpu.attributes.interconnect_group is captured by the + // GPU parser (see go/sdl/gpu.go) into the + // v2ResourceGPU.InterconnectGroup field. Surface it onto the + // off-chain manifest service so the provider can apply + // per-group pod anti-affinity at deploy time. + if compute.Resources != nil && compute.Resources.GPU != nil { + msvc.InterconnectGroup = compute.Resources.GPU.InterconnectGroup + } + if svc.Params != nil { params := &manifest.ServiceParams{} diff --git a/go/sdl/groupBuilder_v2_1.go b/go/sdl/groupBuilder_v2_1.go index fd43ce55..a2248967 100644 --- a/go/sdl/groupBuilder_v2_1.go +++ b/go/sdl/groupBuilder_v2_1.go @@ -107,6 +107,11 @@ func (sdl *v2_1) buildGroups() error { Expose: expose, } + // CS-3: see groupBuilder_v2.go for rationale. + if compute.Resources != nil && compute.Resources.GPU != nil { + msvc.InterconnectGroup = compute.Resources.GPU.InterconnectGroup + } + if svc.Params != nil { params := &manifest.ServiceParams{} diff --git a/go/sdl/interconnect_gpu_test.go b/go/sdl/interconnect_gpu_test.go new file mode 100644 index 00000000..d40dfb50 --- /dev/null +++ b/go/sdl/interconnect_gpu_test.go @@ -0,0 +1,185 @@ +package sdl + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +// CS-2: gpu.attributes.interconnect: true flows to on-chain Resources.GPU.attributes +// as a free-form key=value pair, while interconnect: false (or unset) is absent. +func TestV2ResourceGPU_InterconnectFlag(t *testing.T) { + tests := []struct { + name string + yaml string + expectInterconnectAttr bool + expectGroup string + }{ + { + name: "interconnect true emits attribute", + yaml: `units: 1 +attributes: + vendor: + nvidia: + - model: a100 + interconnect: true`, + expectInterconnectAttr: true, + expectGroup: "", + }, + { + name: "interconnect false does not emit attribute", + yaml: `units: 1 +attributes: + vendor: + nvidia: + - model: a100 + interconnect: false`, + expectInterconnectAttr: false, + expectGroup: "", + }, + { + name: "no interconnect key behaves like interconnect false", + yaml: `units: 1 +attributes: + vendor: + nvidia: + - model: a100`, + expectInterconnectAttr: false, + expectGroup: "", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var gpu v2ResourceGPU + require.NoError(t, yaml.Unmarshal([]byte(tc.yaml), &gpu)) + + hasInterconnect := false + for _, a := range gpu.Attributes { + if a.Key == GPUAttributeInterconnect && a.Value == "true" { + hasInterconnect = true + } + } + require.Equal(t, tc.expectInterconnectAttr, hasInterconnect, + "unexpected presence of on-chain interconnect=true attribute") + require.Equal(t, tc.expectGroup, gpu.InterconnectGroup) + }) + } +} + +// AKT-443: interconnect_group flows end-to-end. It appears in the on-chain GPU +// attribute slice (so the provider's bid engine can enforce per-group +// node separation during reservation) AND is lifted onto +// v2ResourceGPU.InterconnectGroup for the manifest builder to route into +// Service.InterconnectGroup (so the workload builder can label pods for +// anti-affinity). Both consumers see the same value. +func TestV2ResourceGPU_InterconnectGroupOnChainAndOffChain(t *testing.T) { + yamlSrc := `units: 8 +attributes: + vendor: + nvidia: + - model: a100 + ram: 80Gi + interface: sxm + interconnect: true + interconnect_group: pair1` + + var gpu v2ResourceGPU + require.NoError(t, yaml.Unmarshal([]byte(yamlSrc), &gpu)) + + // Off-chain: dedicated field for the manifest builder. + require.Equal(t, "pair1", gpu.InterconnectGroup, + "v2ResourceGPU.InterconnectGroup must hold the interconnect_group value") + + // On-chain: present in the GPU attribute slice alongside interconnect=true. + keys := map[string]string{} + for _, a := range gpu.Attributes { + keys[a.Key] = a.Value + } + require.Equal(t, "true", keys[GPUAttributeInterconnect]) + require.Equal(t, "pair1", keys[GPUAttributeInterconnectGroup], + "interconnect_group must appear in on-chain GPU attributes for bid-engine group tracking") +} + +// AKT-443 (continued): omitting interconnect_group leaves both the field empty +// and the on-chain attribute absent — non-interconnect-group services produce +// the same byte-for-byte serialization they did before this feature. +func TestV2ResourceGPU_InterconnectGroupOmitted(t *testing.T) { + yamlSrc := `units: 8 +attributes: + vendor: + nvidia: + - model: a100 + interconnect: true` + + var gpu v2ResourceGPU + require.NoError(t, yaml.Unmarshal([]byte(yamlSrc), &gpu)) + + require.Empty(t, gpu.InterconnectGroup) + for _, a := range gpu.Attributes { + require.NotEqual(t, GPUAttributeInterconnectGroup, a.Key, + "interconnect_group must not appear when SDL omits it") + } +} + +// CodeRabbit follow-up: a profile with gpu.units == 0 that declares +// interconnect: true or interconnect_group: is a misconfiguration — there is no +// HCA to allocate, so the interconnect flags are meaningless. The parser must +// reject this fail-fast rather than letting downstream validation passes +// silently classify the profile as interconnect-enabled. +func TestV2ResourceGPU_InterconnectZeroUnitsRejected(t *testing.T) { + t.Run("interconnect true with zero units", func(t *testing.T) { + yamlSrc := `units: 0 +attributes: + vendor: + nvidia: + - model: a100 + interconnect: true` + var gpu v2ResourceGPU + err := yaml.Unmarshal([]byte(yamlSrc), &gpu) + require.Error(t, err) + require.Contains(t, err.Error(), "gpu.attributes.interconnect cannot be set when gpu.units == 0") + }) + + t.Run("interconnect_group with zero units", func(t *testing.T) { + yamlSrc := `units: 0 +attributes: + vendor: + nvidia: + - model: a100 + interconnect_group: pair1` + var gpu v2ResourceGPU + err := yaml.Unmarshal([]byte(yamlSrc), &gpu) + require.Error(t, err) + require.Contains(t, err.Error(), "interconnect_group") + require.Contains(t, err.Error(), "gpu.units == 0") + }) + + t.Run("zero units without interconnect is fine", func(t *testing.T) { + // Verifies the new guards don't accidentally break the existing + // path where a profile has gpu.units == 0 and no attributes at all + // (the parser leaves it alone). + yamlSrc := `units: 0` + var gpu v2ResourceGPU + require.NoError(t, yaml.Unmarshal([]byte(yamlSrc), &gpu)) + }) +} + +// CS-2: an unsupported attribute key under gpu.attributes still errors, +// confirming the parser's strict-key behavior is preserved alongside the +// new interconnect_group handling. +func TestV2GPUAttributes_UnsupportedKeyRejected(t *testing.T) { + yamlSrc := `units: 1 +attributes: + vendor: + nvidia: + - model: a100 + interconnect: true + bogus: yes` + + var gpu v2ResourceGPU + err := yaml.Unmarshal([]byte(yamlSrc), &gpu) + require.Error(t, err) + require.Contains(t, err.Error(), "unsupported attribute") +} diff --git a/go/sdl/interconnect_validation_test.go b/go/sdl/interconnect_validation_test.go new file mode 100644 index 00000000..f99cea1a --- /dev/null +++ b/go/sdl/interconnect_validation_test.go @@ -0,0 +1,188 @@ +package sdl + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +// CS-5: parser-level cross-field validations for interconnect. +// +// Each test builds a full SDL via Read() — the same entry point the +// CLI/provider use — so we exercise the real parse + validate pipeline. + +const sdlInterconnectFixtureHeader = `--- +version: "2.0" +services: + inference-head: + image: nvidia/cuda:latest + expose: + - port: 30000 + as: 80 + to: [{ global: true }] + inference-worker: + image: nvidia/cuda:latest + expose: + - port: 30000 + to: [{ service: inference-head }] +` + +func sdlBody(headGPU, workerGPU, placementAttrs string) string { + return sdlInterconnectFixtureHeader + ` +profiles: + compute: + inference-head: + resources: + cpu: { units: 1 } + memory: { size: 1Gi } + gpu: +` + headGPU + ` + storage: + - size: 512Mi + inference-worker: + resources: + cpu: { units: 1 } + memory: { size: 1Gi } + gpu: +` + workerGPU + ` + storage: + - size: 512Mi + placement: + fabric: + attributes: +` + placementAttrs + ` + pricing: + inference-head: { denom: uact, amount: 1000000 } + inference-worker: { denom: uact, amount: 1000000 } +deployment: + inference-head: + fabric: { profile: inference-head, count: 1 } + inference-worker: + fabric: { profile: inference-worker, count: 1 } +` +} + +func TestSDL_Interconnect_Rule1_InterconnectRequiresPlacementCapability(t *testing.T) { + // Profiles declare gpu.attributes.interconnect: true but placement does NOT + // require capabilities/gpu-interconnect=true — rule 1 must reject. + body := sdlBody( + ` units: 1 + attributes: + vendor: { nvidia: [{ model: a100 }] } + interconnect: true`, + ` units: 1 + attributes: + vendor: { nvidia: [{ model: a100 }] } + interconnect: true`, + ` capabilities/gpu: nvidia`, + ) + + _, err := Read([]byte(body)) + require.Error(t, err) + require.True(t, + strings.Contains(err.Error(), "does not require capabilities/gpu-interconnect=true"), + "unexpected error: %v", err) +} + +func TestSDL_Interconnect_Rule1_PassesWhenPlacementRequiresInterconnect(t *testing.T) { + body := sdlBody( + ` units: 1 + attributes: + vendor: { nvidia: [{ model: a100 }] } + interconnect: true`, + ` units: 1 + attributes: + vendor: { nvidia: [{ model: a100 }] } + interconnect: true`, + ` capabilities/gpu-interconnect: "true"`, + ) + + _, err := Read([]byte(body)) + require.NoError(t, err) +} + +func TestSDL_Interconnect_Rule2_InterconnectGroupRequiresInterconnectOnSameProfile(t *testing.T) { + body := sdlBody( + // head: interconnect: true + interconnect_group set (OK) + ` units: 1 + attributes: + vendor: { nvidia: [{ model: a100 }] } + interconnect: true + interconnect_group: pair1`, + // worker: interconnect_group set but interconnect is NOT true — rule 2 must reject. + ` units: 1 + attributes: + vendor: { nvidia: [{ model: a100 }] } + interconnect_group: pair1`, + ` capabilities/gpu-interconnect: "true"`, + ) + + _, err := Read([]byte(body)) + require.Error(t, err) + require.True(t, + strings.Contains(err.Error(), "interconnect_group") && strings.Contains(err.Error(), "interconnect: true"), + "unexpected error: %v", err) +} + +func TestSDL_Interconnect_Rule3_NoMixingExplicitAndImplicitGroup(t *testing.T) { + body := sdlBody( + // head: interconnect: true with no interconnect_group (implicit __default__). + ` units: 1 + attributes: + vendor: { nvidia: [{ model: a100 }] } + interconnect: true`, + // worker: interconnect: true with interconnect_group explicitly set. + // Rule 3: cannot mix; reject. + ` units: 1 + attributes: + vendor: { nvidia: [{ model: a100 }] } + interconnect: true + interconnect_group: pair1`, + ` capabilities/gpu-interconnect: "true"`, + ) + + _, err := Read([]byte(body)) + require.Error(t, err) + require.True(t, + strings.Contains(err.Error(), "mixes explicit and implicit interconnect_group"), + "unexpected error: %v", err) +} + +func TestSDL_Interconnect_Rule3_AllImplicitOK(t *testing.T) { + // Simple head/worker with no interconnect_group on either profile — the implicit + // default group is fine. + body := sdlBody( + ` units: 1 + attributes: + vendor: { nvidia: [{ model: a100 }] } + interconnect: true`, + ` units: 1 + attributes: + vendor: { nvidia: [{ model: a100 }] } + interconnect: true`, + ` capabilities/gpu-interconnect: "true"`, + ) + + _, err := Read([]byte(body)) + require.NoError(t, err) +} + +func TestSDL_Interconnect_Rule3_AllExplicitOK(t *testing.T) { + body := sdlBody( + ` units: 1 + attributes: + vendor: { nvidia: [{ model: a100 }] } + interconnect: true + interconnect_group: pair1`, + ` units: 1 + attributes: + vendor: { nvidia: [{ model: a100 }] } + interconnect: true + interconnect_group: pair1`, + ` capabilities/gpu-interconnect: "true"`, + ) + + _, err := Read([]byte(body)) + require.NoError(t, err) +} diff --git a/go/sdl/sdl-input.schema.yaml b/go/sdl/sdl-input.schema.yaml index 2efe3c91..637a2c1b 100644 --- a/go/sdl/sdl-input.schema.yaml +++ b/go/sdl/sdl-input.schema.yaml @@ -268,6 +268,24 @@ properties: type: object - type: 'null' type: object + interconnect: + description: | + Opt this resource into GPU interconnect. + When true, the bid engine matches against + providers advertising + capabilities/gpu-interconnect and the + workload builder requests one HCA per GPU + unit (1:1). Fabric (InfiniBand vs RoCE) is + chosen by the provider, not the SDL. + type: boolean + interconnect_group: + description: | + Peer-group label for multi-service NCCL + groups. All services sharing the same value + form one anti-affinity group: the provider + schedules them on distinct nodes. Implies + interconnect: true. + type: string type: object units: oneOf: diff --git a/go/sdl/v2.go b/go/sdl/v2.go index 80956b02..f13f692a 100644 --- a/go/sdl/v2.go +++ b/go/sdl/v2.go @@ -533,9 +533,181 @@ func (sdl *v2) validate() error { } } + if err := validateInterconnect(sdl.Profiles, sdl.Deployments); err != nil { + return err + } + return nil } +// validateInterconnect enforces the parser-level cross-field invariants +// for GPU interconnect (CS-5 in the implementation spec): +// +// 1. Any compute profile with gpu.attributes.interconnect: true must be +// used by a deployment whose placement requirements include +// capabilities/gpu-interconnect=true. +// 2. Any compute profile with gpu.attributes.interconnect_group set must +// also have gpu.attributes.interconnect: true on the same profile (a +// peer group label without an HCA is a misconfiguration). +// 3. Within one deployment, if any profile sets interconnect_group, +// every profile with gpu.attributes.interconnect: true in that +// deployment must set interconnect_group. No +// implicit-default-plus-explicit mixing within one deployment. +// +// The provider relies on these guarantees: rule 1 means an +// interconnect-required workload always reaches an interconnect-capable +// provider; rule 2 keeps peer-group labels meaningful; rule 3 keeps the +// per-group anti-affinity rule unambiguous. +// +// Free function (not a method on v2) so v2_1's validate() can call it +// against the same profile + deployment shape — v2.1 inherits the full +// GPU interconnect SDL grammar from v2 and must enforce the identical +// rules. +func validateInterconnect(profiles v2profiles, deployments v2Deployments) error { + // Per-profile rule 2: interconnect_group ⇒ interconnect=true. + // + // The gpu.Units > 0 guard is defense-in-depth: v2ResourceGPU.UnmarshalYAML + // already rejects interconnect / interconnect_group with gpu.units == 0 + // at parse time, but if that parser path is ever bypassed we'd otherwise + // classify a zero-GPU profile as interconnect-enabled here and propagate + // InterconnectGroup into downstream manifest builders. + for profileName, compute := range profiles.Compute { + if compute.Resources == nil || compute.Resources.GPU == nil { + continue + } + gpu := compute.Resources.GPU + if gpu.Units == 0 { + continue + } + if gpu.InterconnectGroup != "" && !gpuAttributesHaveInterconnect(gpu.Attributes) { + return fmt.Errorf( + "%w: compute profile %q sets gpu.attributes.interconnect_group=%q but does not set gpu.attributes.interconnect: true", + errSDLInvalid, + profileName, + gpu.InterconnectGroup, + ) + } + } + + // Rules 1 and 3 are scoped to one deployment block. SDL v2 only has one + // deployment block (sdl.Deployments) so we treat the whole map as a + // single deployment for the purposes of these rules. + // + // Walk every (service, placement) and remember per service-deployment: + // - which profile it points at, + // - whether that profile has interconnect=true, + // - whether that profile has interconnect_group set. + type profUsage struct { + profileName string + hasInterconnect bool + group string + } + type placementUsage struct { + // indexed by service name + usages []profUsage + } + usagesByPlacement := map[string]*placementUsage{} + + for svcName, depl := range deployments { + for placementName, svcdepl := range depl { + compute, ok := profiles.Compute[svcdepl.Profile] + if !ok { + continue // covered by earlier validate() loop + } + gpu := (*v2ResourceGPU)(nil) + if compute.Resources != nil { + gpu = compute.Resources.GPU + } + usage := profUsage{ + profileName: svcdepl.Profile, + } + // Same defense-in-depth as the rule-2 loop above: an + // interconnect attribute or interconnect_group on a zero-GPU + // profile would be parser-rejected upstream, but treat it as + // "not interconnect" here just in case the parser is bypassed. + if gpu != nil && gpu.Units > 0 { + usage.hasInterconnect = gpuAttributesHaveInterconnect(gpu.Attributes) + usage.group = gpu.InterconnectGroup + } + + pu, ok := usagesByPlacement[placementName] + if !ok { + pu = &placementUsage{} + usagesByPlacement[placementName] = pu + } + pu.usages = append(pu.usages, usage) + + // Rule 1: per-placement, if this profile has interconnect=true, + // the placement's requirements must include + // capabilities/gpu-interconnect=true. + if usage.hasInterconnect { + infra, ok := profiles.Placement[placementName] + if !ok { + continue // covered by earlier validate() loop + } + if !placementRequiresInterconnect(infra.Attributes) { + return fmt.Errorf( + "%w: service %q uses interconnect profile %q under placement %q but placement does not require capabilities/gpu-interconnect=true", + errSDLInvalid, + svcName, + svcdepl.Profile, + placementName, + ) + } + } + } + } + + // Rule 3: within one deployment block (= one placementUsage), if any + // profile sets interconnect_group, every profile with interconnect=true + // must set interconnect_group too. The spec's "deployment" scope is + // one sdl.Deployments, so we apply this rule per placement (each + // placement is what a tenant sees as one bid target / one set of + // leased pods). + for placementName, pu := range usagesByPlacement { + anyGrouped := false + for _, u := range pu.usages { + if u.group != "" { + anyGrouped = true + break + } + } + if !anyGrouped { + continue + } + for _, u := range pu.usages { + if u.hasInterconnect && u.group == "" { + return fmt.Errorf( + "%w: placement %q mixes explicit and implicit interconnect_group: profile %q has gpu.attributes.interconnect: true but no interconnect_group, while another profile under the same placement sets interconnect_group", + errSDLInvalid, + placementName, + u.profileName, + ) + } + } + } + + return nil +} + +func gpuAttributesHaveInterconnect(attrs v2GPUAttributes) bool { + for _, a := range attrs { + if a.Key == GPUAttributeInterconnect && a.Value == valueTrue { + return true + } + } + return false +} + +func placementRequiresInterconnect(attrs v2PlacementAttributes) bool { + for _, a := range attrs { + if a.Key == "capabilities/gpu-interconnect" && a.Value == valueTrue { + return true + } + } + return false +} + func (sdl *v2) computeEndpointSequenceNumbers() map[string]uint32 { var endpointNames []string res := make(map[string]uint32) diff --git a/go/sdl/v2_1.go b/go/sdl/v2_1.go index 88a1224b..8efb8439 100644 --- a/go/sdl/v2_1.go +++ b/go/sdl/v2_1.go @@ -341,6 +341,15 @@ func (sdl *v2_1) validate() error { } } + // v2.1 inherits the full GPU interconnect SDL grammar from v2 + // (gpu.attributes.interconnect and gpu.attributes.interconnect_group + // propagate to the manifest), so the cross-field validation rules + // must also apply. Without this, a v2.1 SDL could bypass the + // placement + grouping invariants v2 enforces. + if err := validateInterconnect(sdl.Profiles, sdl.Deployments); err != nil { + return err + } + return nil } diff --git a/proto/provider/akash/inventory/v1/node.proto b/proto/provider/akash/inventory/v1/node.proto index ca24fb7b..4fe34783 100644 --- a/proto/provider/akash/inventory/v1/node.proto +++ b/proto/provider/akash/inventory/v1/node.proto @@ -13,6 +13,38 @@ message NodeCapabilities { (gogoproto.jsontag) = "storage_classes", (gogoproto.moretags) = "yaml:\"storage_classes\"" ]; + + // Kubernetes extended-resource name the cluster's device plugin publishes + // for GPU interconnect HCAs (e.g. rdma/rdma_shared_device_ib for an + // InfiniBand fabric, rdma/rdma_shared_device_eth for RoCE). The + // `rdma/*` prefix is the device-plugin's own convention (Mellanox/NVIDIA) + // and stays unchanged here. Empty when the node has no GPU interconnect + // capability. Discovered by the inventory operator from k8s allocatable. + string interconnect_resource_name = 2 [ + (gogoproto.customname) = "InterconnectResourceName", + (gogoproto.jsontag) = "interconnect_resource_name,omitempty", + (gogoproto.moretags) = "yaml:\"interconnect_resource_name,omitempty\"" + ]; + + // GPU interconnect fabric type. "infiniband" or "roce". Internal / + // informational — the SDL surface is fabric-agnostic; tenants only + // declare `interconnect: true`. Derived from + // /sys/class/infiniband//ports/1/link_layer on the host node. + string interconnect_fabric = 3 [ + (gogoproto.customname) = "InterconnectFabric", + (gogoproto.jsontag) = "interconnect_fabric,omitempty", + (gogoproto.moretags) = "yaml:\"interconnect_fabric,omitempty\"" + ]; + + // NCCL IB HCA prefix - the common device-name prefix under + // /sys/class/infiniband (e.g. "mlx5"). Same key for IB and RoCE since + // NCCL uses the IB verbs API for both. Injected by the provider as + // NCCL_IB_HCA when scheduling GPU interconnect workloads. + string nccl_hca_prefix = 4 [ + (gogoproto.customname) = "NCCLHCAPrefix", + (gogoproto.jsontag) = "nccl_hca_prefix,omitempty", + (gogoproto.moretags) = "yaml:\"nccl_hca_prefix,omitempty\"" + ]; } // Node reports node inventory details diff --git a/proto/provider/akash/inventory/v1/resources.proto b/proto/provider/akash/inventory/v1/resources.proto index 837e1273..fa2e35db 100644 --- a/proto/provider/akash/inventory/v1/resources.proto +++ b/proto/provider/akash/inventory/v1/resources.proto @@ -53,4 +53,17 @@ message NodeResources { (gogoproto.jsontag) = "volumes_mounted", (gogoproto.moretags) = "yaml:\"volumes_mounted\"" ]; + + // GPUInterconnect reports node GPU-interconnect HCA capacity. + // Capacity/Allocatable/Allocated are populated by the inventory + // operator from k8s allocatable for whichever + // rdma/rdma_shared_device_* extended resource the cluster's device + // plugin publishes (the `rdma/*` prefix is the device plugin's + // naming convention; see NodeCapabilities.interconnect_resource_name). + ResourcePair gpu_interconnect = 7 [ + (gogoproto.nullable) = false, + (gogoproto.customname) = "GPUInterconnect", + (gogoproto.jsontag) = "gpu_interconnect", + (gogoproto.moretags) = "yaml:\"gpu_interconnect\"" + ]; } diff --git a/proto/provider/akash/manifest/v2beta3/service.proto b/proto/provider/akash/manifest/v2beta3/service.proto index d23e9e5d..060fced3 100644 --- a/proto/provider/akash/manifest/v2beta3/service.proto +++ b/proto/provider/akash/manifest/v2beta3/service.proto @@ -124,4 +124,22 @@ message Service { (gogoproto.jsontag) = "credentials", (gogoproto.moretags) = "yaml:\"credentials\"" ]; + + // InterconnectGroup carries the SDL gpu.attributes.interconnect_group + // peer-group label. Lifted from Resources.GPU.Attributes by the + // manifest builder so the off-chain workload builder can label pods + // for per-group anti-affinity. Services sharing the same value form + // one NCCL peer group; the provider schedules them on distinct nodes. + // Empty when the service is not part of any GPU interconnect group. + // + // JSON / YAML tags carry `omitempty`: the on-chain manifest `version` + // is a SHA hash of the JSON-serialized off-chain manifest, so any + // field that always serializes (even at zero value) would shift the + // hash for every non-interconnect SDL and break send-manifest + // validation on existing leases. + string interconnect_group = 11 [ + (gogoproto.customname) = "InterconnectGroup", + (gogoproto.jsontag) = "interconnectGroup,omitempty", + (gogoproto.moretags) = "yaml:\"interconnectGroup,omitempty\"" + ]; } diff --git a/testdata/sdl/input/v2.0/gpu-interconnect-group/input.yaml b/testdata/sdl/input/v2.0/gpu-interconnect-group/input.yaml new file mode 100644 index 00000000..adc90160 --- /dev/null +++ b/testdata/sdl/input/v2.0/gpu-interconnect-group/input.yaml @@ -0,0 +1,92 @@ +# yaml-language-server: $schema=../../../../go/sdl/sdl-input.schema.yaml +--- +# Multi-service GPU interconnect peer group fixture. inference-head and inference-worker +# share the same interconnect_group label ("pair0"), which: +# - emits an `interconnect_group=pair0` attribute into each service's on-chain +# Resources.GPU.Attributes (consumed by the provider's bid-engine +# group-aware Adjust, AKT-443) +# - lifts to Service.GPU interconnectGroup on the off-chain manifest (consumed by +# the provider's workload builder for pod anti-affinity) +# +# Each service has its own compute profile because the SDL→manifest +# resource-ID assignment treats per-profile groupings as one resource +# unit (this is a separate parity concern; see Go vs TS resource-ID logic +# in groupBuilder_v2.go and generateManifest.ts). Using distinct profiles +# keeps that orthogonal so this fixture isolates GPU interconnect group behavior. +version: "2.0" +services: + inference-head: + image: tensorflow/tensorflow:latest-gpu + expose: + - port: 8080 + as: 80 + to: + - global: true + inference-worker: + image: tensorflow/tensorflow:latest-gpu + expose: + - port: 5000 + to: + - service: inference-head +profiles: + compute: + interconnect-head: + resources: + cpu: + units: 1000m + memory: + size: 2Gi + storage: + size: 10Gi + gpu: + units: 1 + attributes: + interconnect: true + interconnect_group: pair0 + vendor: + nvidia: + - model: a100 + ram: 80Gi + interface: sxm + interconnect-worker: + resources: + cpu: + units: 1000m + memory: + size: 2Gi + storage: + size: 10Gi + gpu: + units: 1 + attributes: + interconnect: true + interconnect_group: pair0 + vendor: + nvidia: + - model: a100 + ram: 80Gi + interface: sxm + placement: + interconnect-cluster: + attributes: + capabilities/gpu-interconnect: "true" + capabilities/gpu-interconnect/fabric/infiniband: "true" + signedBy: + anyOf: + - akash1365yvmc4s7awdyj3n2sav7xfx76adc6dnmlx63 + pricing: + interconnect-head: + denom: uakt + amount: 1000 + interconnect-worker: + denom: uakt + amount: 1000 +deployment: + inference-head: + interconnect-cluster: + profile: interconnect-head + count: 1 + inference-worker: + interconnect-cluster: + profile: interconnect-worker + count: 1 diff --git a/testdata/sdl/output-fixtures/v2.0/gpu-interconnect-group/group-specs.json b/testdata/sdl/output-fixtures/v2.0/gpu-interconnect-group/group-specs.json new file mode 100644 index 00000000..48ce6a19 --- /dev/null +++ b/testdata/sdl/output-fixtures/v2.0/gpu-interconnect-group/group-specs.json @@ -0,0 +1,125 @@ +[ + { + "name": "interconnect-cluster", + "requirements": { + "signed_by": { + "all_of": null, + "any_of": [ + "akash1365yvmc4s7awdyj3n2sav7xfx76adc6dnmlx63" + ] + }, + "attributes": [ + { + "key": "capabilities/gpu-interconnect", + "value": "true" + }, + { + "key": "capabilities/gpu-interconnect/fabric/infiniband", + "value": "true" + } + ] + }, + "resources": [ + { + "resource": { + "id": 1, + "cpu": { + "units": { + "val": "1000" + } + }, + "memory": { + "size": { + "val": "2147483648" + } + }, + "storage": [ + { + "name": "default", + "size": { + "val": "10737418240" + } + } + ], + "gpu": { + "units": { + "val": "1" + }, + "attributes": [ + { + "key": "interconnect", + "value": "true" + }, + { + "key": "interconnect_group", + "value": "pair0" + }, + { + "key": "vendor/nvidia/model/a100/ram/80Gi/interface/sxm", + "value": "true" + } + ] + }, + "endpoints": [ + { + "sequence_number": 0 + } + ] + }, + "count": 1, + "price": { + "denom": "uakt", + "amount": "1000.000000000000000000" + } + }, + { + "resource": { + "id": 2, + "cpu": { + "units": { + "val": "1000" + } + }, + "memory": { + "size": { + "val": "2147483648" + } + }, + "storage": [ + { + "name": "default", + "size": { + "val": "10737418240" + } + } + ], + "gpu": { + "units": { + "val": "1" + }, + "attributes": [ + { + "key": "interconnect", + "value": "true" + }, + { + "key": "interconnect_group", + "value": "pair0" + }, + { + "key": "vendor/nvidia/model/a100/ram/80Gi/interface/sxm", + "value": "true" + } + ] + }, + "endpoints": [] + }, + "count": 1, + "price": { + "denom": "uakt", + "amount": "1000.000000000000000000" + } + } + ] + } +] \ No newline at end of file diff --git a/testdata/sdl/output-fixtures/v2.0/gpu-interconnect-group/manifest.json b/testdata/sdl/output-fixtures/v2.0/gpu-interconnect-group/manifest.json new file mode 100644 index 00000000..5692ce86 --- /dev/null +++ b/testdata/sdl/output-fixtures/v2.0/gpu-interconnect-group/manifest.json @@ -0,0 +1,159 @@ +[ + { + "name": "interconnect-cluster", + "services": [ + { + "name": "inference-head", + "image": "tensorflow/tensorflow:latest-gpu", + "command": null, + "args": null, + "env": null, + "resources": { + "id": 1, + "cpu": { + "units": { + "val": "1000" + } + }, + "memory": { + "size": { + "val": "2147483648" + } + }, + "storage": [ + { + "name": "default", + "size": { + "val": "10737418240" + } + } + ], + "gpu": { + "units": { + "val": "1" + }, + "attributes": [ + { + "key": "interconnect", + "value": "true" + }, + { + "key": "interconnect_group", + "value": "pair0" + }, + { + "key": "vendor/nvidia/model/a100/ram/80Gi/interface/sxm", + "value": "true" + } + ] + }, + "endpoints": [ + { + "sequence_number": 0 + } + ] + }, + "count": 1, + "expose": [ + { + "port": 8080, + "externalPort": 80, + "proto": "TCP", + "service": "", + "global": true, + "hosts": null, + "httpOptions": { + "maxBodySize": 1048576, + "readTimeout": 60000, + "sendTimeout": 60000, + "nextTries": 3, + "nextTimeout": 0, + "nextCases": [ + "error", + "timeout" + ] + }, + "ip": "", + "endpointSequenceNumber": 0 + } + ], + "credentials": null, + "interconnectGroup": "pair0" + }, + { + "name": "inference-worker", + "image": "tensorflow/tensorflow:latest-gpu", + "command": null, + "args": null, + "env": null, + "resources": { + "id": 2, + "cpu": { + "units": { + "val": "1000" + } + }, + "memory": { + "size": { + "val": "2147483648" + } + }, + "storage": [ + { + "name": "default", + "size": { + "val": "10737418240" + } + } + ], + "gpu": { + "units": { + "val": "1" + }, + "attributes": [ + { + "key": "interconnect", + "value": "true" + }, + { + "key": "interconnect_group", + "value": "pair0" + }, + { + "key": "vendor/nvidia/model/a100/ram/80Gi/interface/sxm", + "value": "true" + } + ] + }, + "endpoints": [] + }, + "count": 1, + "expose": [ + { + "port": 5000, + "externalPort": 0, + "proto": "TCP", + "service": "inference-head", + "global": false, + "hosts": null, + "httpOptions": { + "maxBodySize": 1048576, + "readTimeout": 60000, + "sendTimeout": 60000, + "nextTries": 3, + "nextTimeout": 0, + "nextCases": [ + "error", + "timeout" + ] + }, + "ip": "", + "endpointSequenceNumber": 0 + } + ], + "credentials": null, + "interconnectGroup": "pair0" + } + ] + } +] \ No newline at end of file diff --git a/ts/src/generated/protos/akash/inventory/v1/node.ts b/ts/src/generated/protos/akash/inventory/v1/node.ts index 9c7dda62..1bbf1f4c 100644 --- a/ts/src/generated/protos/akash/inventory/v1/node.ts +++ b/ts/src/generated/protos/akash/inventory/v1/node.ts @@ -14,6 +14,29 @@ import { NodeResources } from "./resources.ts"; /** NodeCapabilities extended list of node capabilities */ export interface NodeCapabilities { storageClasses: string[]; + /** + * Kubernetes extended-resource name the cluster's device plugin publishes + * for GPU interconnect HCAs (e.g. rdma/rdma_shared_device_ib for an + * InfiniBand fabric, rdma/rdma_shared_device_eth for RoCE). The + * `rdma/*` prefix is the device-plugin's own convention (Mellanox/NVIDIA) + * and stays unchanged here. Empty when the node has no GPU interconnect + * capability. Discovered by the inventory operator from k8s allocatable. + */ + interconnectResourceName: string; + /** + * GPU interconnect fabric type. "infiniband" or "roce". Internal / + * informational — the SDL surface is fabric-agnostic; tenants only + * declare `interconnect: true`. Derived from + * /sys/class/infiniband//ports/1/link_layer on the host node. + */ + interconnectFabric: string; + /** + * NCCL IB HCA prefix - the common device-name prefix under + * /sys/class/infiniband (e.g. "mlx5"). Same key for IB and RoCE since + * NCCL uses the IB verbs API for both. Injected by the provider as + * NCCL_IB_HCA when scheduling GPU interconnect workloads. + */ + ncclHcaPrefix: string; } /** Node reports node inventory details */ @@ -24,7 +47,7 @@ export interface Node { } function createBaseNodeCapabilities(): NodeCapabilities { - return { storageClasses: [] }; + return { storageClasses: [], interconnectResourceName: "", interconnectFabric: "", ncclHcaPrefix: "" }; } export const NodeCapabilities: MessageFns = { @@ -34,6 +57,15 @@ export const NodeCapabilities: MessageFns globalThis.String(e)) : [], + interconnectResourceName: isSet(object.interconnect_resource_name) + ? globalThis.String(object.interconnect_resource_name) + : "", + interconnectFabric: isSet(object.interconnect_fabric) ? globalThis.String(object.interconnect_fabric) : "", + ncclHcaPrefix: isSet(object.nccl_hca_prefix) ? globalThis.String(object.nccl_hca_prefix) : "", }; }, @@ -74,11 +135,23 @@ export const NodeCapabilities: MessageFns): NodeCapabilities { const message = createBaseNodeCapabilities(); message.storageClasses = object.storageClasses?.map((e) => e) || []; + message.interconnectResourceName = object.interconnectResourceName ?? ""; + message.interconnectFabric = object.interconnectFabric ?? ""; + message.ncclHcaPrefix = object.ncclHcaPrefix ?? ""; return message; }, }; diff --git a/ts/src/generated/protos/akash/inventory/v1/resources.ts b/ts/src/generated/protos/akash/inventory/v1/resources.ts index 0c99c19d..ce79d409 100644 --- a/ts/src/generated/protos/akash/inventory/v1/resources.ts +++ b/ts/src/generated/protos/akash/inventory/v1/resources.ts @@ -21,7 +21,18 @@ export interface NodeResources { gpu: GPU | undefined; ephemeralStorage: ResourcePair | undefined; volumesAttached: ResourcePair | undefined; - volumesMounted: ResourcePair | undefined; + volumesMounted: + | ResourcePair + | undefined; + /** + * GPUInterconnect reports node GPU-interconnect HCA capacity. + * Capacity/Allocatable/Allocated are populated by the inventory + * operator from k8s allocatable for whichever + * rdma/rdma_shared_device_* extended resource the cluster's device + * plugin publishes (the `rdma/*` prefix is the device plugin's + * naming convention; see NodeCapabilities.interconnect_resource_name). + */ + gpuInterconnect: ResourcePair | undefined; } function createBaseNodeResources(): NodeResources { @@ -32,6 +43,7 @@ function createBaseNodeResources(): NodeResources { ephemeralStorage: undefined, volumesAttached: undefined, volumesMounted: undefined, + gpuInterconnect: undefined, }; } @@ -57,6 +69,9 @@ export const NodeResources: MessageFns): NodeResources { @@ -173,6 +200,9 @@ export const NodeResources: MessageFns = { if (message.credentials !== undefined) { ImageCredentials.encode(message.credentials, writer.uint32(82).fork()).join(); } + if (message.interconnectGroup !== "") { + writer.uint32(90).string(message.interconnectGroup); + } return writer; }, @@ -544,6 +565,14 @@ export const Service: MessageFns = { message.credentials = ImageCredentials.decode(reader, reader.uint32()); continue; } + case 11: { + if (tag !== 90) { + break; + } + + message.interconnectGroup = reader.string(); + continue; + } } if ((tag & 7) === 4 || tag === 0) { break; @@ -565,6 +594,7 @@ export const Service: MessageFns = { expose: globalThis.Array.isArray(object?.expose) ? object.expose.map((e: any) => ServiceExpose.fromJSON(e)) : [], params: isSet(object.params) ? ServiceParams.fromJSON(object.params) : undefined, credentials: isSet(object.credentials) ? ImageCredentials.fromJSON(object.credentials) : undefined, + interconnectGroup: isSet(object.interconnect_group) ? globalThis.String(object.interconnect_group) : "", }; }, @@ -600,6 +630,9 @@ export const Service: MessageFns = { if (message.credentials !== undefined) { obj.credentials = ImageCredentials.toJSON(message.credentials); } + if (message.interconnectGroup !== "") { + obj.interconnect_group = message.interconnectGroup; + } return obj; }, fromPartial(object: DeepPartial): Service { @@ -620,6 +653,7 @@ export const Service: MessageFns = { message.credentials = (object.credentials !== undefined && object.credentials !== null) ? ImageCredentials.fromPartial(object.credentials) : undefined; + message.interconnectGroup = object.interconnectGroup ?? ""; return message; }, }; diff --git a/ts/src/sdl/manifest/generateManifest.ts b/ts/src/sdl/manifest/generateManifest.ts index 2f0d29b9..c9f3acd5 100644 --- a/ts/src/sdl/manifest/generateManifest.ts +++ b/ts/src/sdl/manifest/generateManifest.ts @@ -248,6 +248,13 @@ function buildManifestService( const params = buildParams(service); + // interconnect_group lives under gpu.attributes in the SDL but propagates to the + // off-chain manifest's Service.interconnectGroup field so the provider's workload + // builder can label pods for per-group anti-affinity. The same value is + // also emitted into Resources.GPU.Attributes by transformGpuAttributes; + // both consumers (bid engine vs. workload builder) see it. + const interconnectGroup = compute.resources.gpu?.attributes?.interconnect_group ?? ""; + return Service.fromPartial({ name, image: service.image, @@ -259,6 +266,7 @@ function buildManifestService( expose: buildManifestExpose(service, endpointSequenceNumbers), params, credentials, + interconnectGroup, }); } diff --git a/ts/src/sdl/manifest/generateManifestVersion.ts b/ts/src/sdl/manifest/generateManifestVersion.ts index a63fb6ab..433f24a6 100644 --- a/ts/src/sdl/manifest/generateManifestVersion.ts +++ b/ts/src/sdl/manifest/generateManifestVersion.ts @@ -7,6 +7,13 @@ const encoder = new TextEncoder(); const NULLABLE_MANIFEST_KEYS = new Set(["command", "args", "env", "hosts"]); const OMITTED_MANIFEST_KEYS = new Set(["kind", "attributes"]); +// Manifest fields that on the Go side use `omitempty` for string values — +// the field must NOT appear in the JSON when its value is the empty +// string, otherwise the manifest version hash (a SHA over this exact +// serialization) diverges from the Go side and breaks send-manifest +// validation on existing leases. +const OMITTED_WHEN_EMPTY_STRING_KEYS = new Set(["interconnectGroup"]); + export async function generateManifestVersion(manifest: Manifest): Promise { const jsonStr = manifestToSortedJSON(manifest); const sortedBytes = encoder.encode(jsonStr); @@ -41,6 +48,10 @@ function manifestReplacer(this: unknown, key: string | number, value: unknown): return undefined; } + if (OMITTED_WHEN_EMPTY_STRING_KEYS.has(key) && value === "") { + return undefined; + } + return value; } diff --git a/ts/src/sdl/manifest/manifestUtils.ts b/ts/src/sdl/manifest/manifestUtils.ts index ff5aa687..357912ca 100644 --- a/ts/src/sdl/manifest/manifestUtils.ts +++ b/ts/src/sdl/manifest/manifestUtils.ts @@ -54,24 +54,44 @@ export function isIngress(proto: string, global: boolean, externalPort: number, } export function transformGpuAttributes(attributes: SDLGpuAttributes): Attribute[] { + const result: Attribute[] = []; + const vendor = attributes.vendor; - if (!vendor) return []; + if (vendor) { + Object.keys(vendor) + .sort((a, b) => a.localeCompare(b)) + .forEach((vendorName) => { + const models = vendor[vendorName as keyof typeof vendor]; + if (!models) { + result.push({ key: `vendor/${vendorName}/model/*`, value: "true" }); + return; + } + for (const model of models) { + let key = `vendor/${vendorName}/model/${model.model}`; + if (model.ram) key += `/ram/${model.ram}`; + if (model.interface) key += `/interface/${model.interface}`; + result.push({ key, value: "true" }); + } + }); + } - return Object.keys(vendor) - .sort((a, b) => a.localeCompare(b)) - .flatMap((vendorName) => { - const models = vendor[vendorName as keyof typeof vendor]; - if (!models) { - return [{ key: `vendor/${vendorName}/model/*`, value: "true" }]; - } + // interconnect + interconnect_group flow into on-chain Resources.GPU.Attributes so the + // provider's bid engine can match capability and enforce per-group node + // separation. Keep parity with the Go SDL parser in go/sdl/gpu.go — same + // key names, same value encoding ("true" for the boolean opt-in). + if (attributes.interconnect === true) { + result.push({ key: "interconnect", value: "true" }); + } + if (attributes.interconnect_group && attributes.interconnect_group.length > 0) { + result.push({ key: "interconnect_group", value: attributes.interconnect_group }); + } - return models.map((model) => { - let key = `vendor/${vendorName}/model/${model.model}`; - if (model.ram) key += `/ram/${model.ram}`; - if (model.interface) key += `/interface/${model.interface}`; - return { key, value: "true" }; - }); - }); + // Go SDL parser canonicalizes the slice via sort.Sort(res) before + // returning. Mirror that here so the on-chain attribute order matches + // byte-for-byte across both implementations. + result.sort((a, b) => a.key.localeCompare(b.key)); + + return result; } export function buildHttpOptions(httpOptions?: SDLHttpOptions): ServiceExposeHTTPOptions { diff --git a/ts/src/sdl/validateSDL/validateSDL.spec.ts b/ts/src/sdl/validateSDL/validateSDL.spec.ts index 6b0768c8..007ba2b9 100644 --- a/ts/src/sdl/validateSDL/validateSDL.spec.ts +++ b/ts/src/sdl/validateSDL/validateSDL.spec.ts @@ -2226,6 +2226,147 @@ describe(validateSDL.name, () => { }); }); + // Mirrors the Go SDL parser's interconnect cross-field rules (go/sdl/v2.go + // validateInterconnect + go/sdl/gpu.go parse-time guards). Without these TS + // checks, tenants using @akashnetwork/chain-sdk could broadcast SDLs + // that the Go CLI would have rejected outright. + describe("GPU interconnect validation", () => { + function interconnectSetup(opts: { + interconnect?: boolean; + interconnectGroup?: string; + units?: number; + placementRequiresInterconnect?: boolean; + } = {}) { + const { interconnect, interconnectGroup, units = 1, placementRequiresInterconnect = true } = opts; + const placementAttrs: Record = {}; + if (placementRequiresInterconnect) placementAttrs["capabilities/gpu-interconnect"] = "true"; + + const gpuAttrs: Record = { vendor: { nvidia: [{ model: "a100" }] } }; + if (interconnect !== undefined) gpuAttrs.interconnect = interconnect; + if (interconnectGroup !== undefined) gpuAttrs.interconnect_group = interconnectGroup; + + return setup({ + profiles: { + compute: { + web: { + resources: { + cpu: { units: 1 }, + memory: { size: "512Mi" }, + storage: { size: "1Gi" }, + gpu: { units, attributes: gpuAttrs }, + }, + }, + }, + placement: { + dcloud: { + attributes: placementAttrs, + }, + }, + }, + }); + } + + it("accepts a valid interconnect profile under an interconnect-capable placement", () => { + const { validate } = interconnectSetup({ interconnect: true }); + expect(validate()).toBeUndefined(); + }); + + // units==0 + interconnect / interconnect_group is rejected by the schema-level + // gpuAttributesRequireUnitsGt0 rule (any attribute present requires + // units > 0), so the semantic validator never runs for that case. + // Pinning the schema's behavior here so a future schema relaxation + // doesn't silently open a hole. + it("rejects gpu.attributes.interconnect=true when gpu.units is 0 (schema-level)", () => { + const { validate } = interconnectSetup({ interconnect: true, units: 0 }); + expect(validate()).toContainEqual(expect.objectContaining({ + schemaPath: expect.stringContaining("gpuAttributesRequireUnitsGt0"), + })); + }); + + it("rejects gpu.attributes.interconnect_group when gpu.units is 0 (schema-level)", () => { + const { validate } = interconnectSetup({ interconnectGroup: "pair0", units: 0 }); + expect(validate()).toContainEqual(expect.objectContaining({ + schemaPath: expect.stringContaining("gpuAttributesRequireUnitsGt0"), + })); + }); + + it("rejects interconnect_group set without interconnect=true", () => { + const { validate } = interconnectSetup({ interconnectGroup: "pair0" }); + expect(validate()).toContainEqual(expect.objectContaining({ + message: expect.stringContaining(`sets gpu.attributes.interconnect_group="pair0" but does not set gpu.attributes.interconnect: true`), + })); + }); + + it("rejects interconnect=true under a placement that does not require capabilities/gpu-interconnect=true", () => { + const { validate } = interconnectSetup({ interconnect: true, placementRequiresInterconnect: false }); + expect(validate()).toContainEqual(expect.objectContaining({ + message: expect.stringContaining(`but placement does not require capabilities/gpu-interconnect=true`), + })); + }); + + it("rejects implicit + explicit interconnect_group mixing within one placement", () => { + // Two profiles, both interconnect, but only one sets interconnect_group. + const { validate } = setup({ + services: { + worker: { + image: "nginx:latest", + }, + }, + profiles: { + compute: { + web: { + resources: { + cpu: { units: 1 }, + memory: { size: "512Mi" }, + storage: { size: "1Gi" }, + gpu: { + units: 1, + attributes: { + vendor: { nvidia: [{ model: "a100" }] }, + interconnect: true, + interconnect_group: "pair0", + }, + }, + }, + }, + worker: { + resources: { + cpu: { units: 1 }, + memory: { size: "512Mi" }, + storage: { size: "1Gi" }, + gpu: { + units: 1, + attributes: { + vendor: { nvidia: [{ model: "a100" }] }, + interconnect: true, + // interconnect_group intentionally omitted -> rule 3 violation + }, + }, + }, + }, + }, + placement: { + dcloud: { + attributes: { "capabilities/gpu-interconnect": "true" }, + pricing: { + worker: { amount: "1000", denom: AKT_DENOM }, + }, + }, + }, + }, + deployment: { + worker: { + dcloud: { count: 1, profile: "worker" }, + }, + }, + }); + + expect(validate()).toContainEqual(expect.objectContaining({ + message: expect.stringContaining("mixes explicit and implicit interconnect_group"), + })); + }); + }); + function setup(overrides: DeepPartial = {}, networkId: NetworkId = "sandbox") { const defaultSDL: SDLInput = { version: "2.0", diff --git a/ts/src/sdl/validateSDL/validateSDL.ts b/ts/src/sdl/validateSDL/validateSDL.ts index 77219c4a..983fb1c9 100644 --- a/ts/src/sdl/validateSDL/validateSDL.ts +++ b/ts/src/sdl/validateSDL/validateSDL.ts @@ -51,6 +51,7 @@ class SDLValidator { }); } + this.#validateInterconnect(); this.#validateEndpoints(); return this.#errors; } @@ -257,6 +258,121 @@ class SDLValidator { }, }); } + + // Note: the units==0 + interconnect/interconnect_group case is already rejected by + // the schema-level rule `gpuAttributesRequireUnitsGt0` — it requires + // units > 0 whenever any attribute (including interconnect / interconnect_group) is + // present. So we don't need a duplicate semantic check here. The + // Go parser (go/sdl/gpu.go) carries its own parse-time check because + // YAML decoding has no schema layer above it. + } + + // Mirror the Go-side validateInterconnect in go/sdl/v2.go. Three cross-field + // rules the chain SDK enforces; TS must enforce them too so tenants + // using @akashnetwork/chain-sdk get the same fail-fast feedback as + // the Go CLI users (the chain doesn't validate SDL semantics). + // + // 1. A profile with gpu.attributes.interconnect=true must be used by a + // deployment whose placement requirements include + // capabilities/gpu-interconnect=true. + // 2. A profile with gpu.attributes.interconnect_group set must also have + // gpu.attributes.interconnect=true on the same profile. + // 3. Within one deployment (placement), if any profile sets + // interconnect_group, every interconnect=true profile under that placement must + // also set interconnect_group — no implicit-default-plus-explicit mixing. + #validateInterconnect() { + const profiles = this.#sdl.profiles?.compute; + const placements = this.#sdl.profiles?.placement; + if (!profiles) return; + + // Rule 2: interconnect_group ⇒ interconnect=true. Per-profile, units > 0 guard so + // the zero-GPU case is left to #validateGPU's error above (avoids + // duplicate complaints on the same profile). + for (const [profileName, compute] of Object.entries(profiles)) { + const gpu = compute?.resources?.gpu; + if (!gpu || gpu.units === 0 || gpu.units === undefined) continue; + + if (gpu.attributes?.interconnect_group && gpu.attributes.interconnect !== true) { + this.#errors.push({ + message: `Compute profile "${profileName}" sets gpu.attributes.interconnect_group="${gpu.attributes.interconnect_group}" but does not set gpu.attributes.interconnect: true.`, + instancePath: `/profiles/compute/${profileName}/resources/gpu/attributes.interconnect`, + schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/properties/attributes/properties/interconnect", + keyword: "required", + params: { missingProperty: "interconnect" }, + }); + } + } + + // Rules 1 + 3 are per-placement. Walk every (service, placement) and + // capture which profile each service uses, whether that profile has + // interconnect=true, and whether it sets interconnect_group. + interface profileUsage { + profileName: string; + serviceName: string; + hasInterconnect: boolean; + group: string; + } + const usagesByPlacement = new Map(); + + for (const [serviceName, perPlacement] of Object.entries(this.#sdl.deployment ?? {})) { + for (const [placementName, svcdepl] of Object.entries(perPlacement ?? {})) { + const compute = profiles[svcdepl.profile]; + if (!compute) continue; + const gpu = compute.resources?.gpu; + const usage: profileUsage = { + profileName: svcdepl.profile, + serviceName, + hasInterconnect: false, + group: "", + }; + if (gpu && gpu.units !== 0 && gpu.units !== undefined) { + usage.hasInterconnect = gpu.attributes?.interconnect === true; + usage.group = gpu.attributes?.interconnect_group ?? ""; + } + + const bucket = usagesByPlacement.get(placementName) ?? []; + bucket.push(usage); + usagesByPlacement.set(placementName, bucket); + + // Rule 1: a profile with interconnect=true must be deployed under a + // placement whose attributes require capabilities/gpu-interconnect=true. + if (usage.hasInterconnect) { + const placement = placements?.[placementName]; + // The placement attributes shape is `Record` + // in the schema; we look for the literal key/value pair. + const attrs = (placement?.attributes ?? {}) as Record; + const capability = attrs["capabilities/gpu-interconnect"]; + const requiresInterconnect = capability === "true" || capability === true; + if (!requiresInterconnect) { + this.#errors.push({ + message: `Service "${serviceName}" uses interconnect profile "${svcdepl.profile}" under placement "${placementName}" but placement does not require capabilities/gpu-interconnect=true.`, + instancePath: `/profiles/placement/${placementName}/attributes`, + schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/attributes", + keyword: "required", + params: { missingProperty: "capabilities/gpu-interconnect" }, + }); + } + } + } + } + + // Rule 3: per placement, if any profile sets interconnect_group then every + // interconnect=true profile under that placement must also set interconnect_group. + for (const [placementName, usages] of usagesByPlacement.entries()) { + const anyGrouped = usages.some((u) => u.group !== ""); + if (!anyGrouped) continue; + for (const u of usages) { + if (u.hasInterconnect && u.group === "") { + this.#errors.push({ + message: `Placement "${placementName}" mixes explicit and implicit interconnect_group: profile "${u.profileName}" has gpu.attributes.interconnect: true but no interconnect_group, while another profile under the same placement sets interconnect_group.`, + instancePath: `/profiles/compute/${u.profileName}/resources/gpu/attributes.interconnect_group`, + schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/properties/attributes/properties/interconnect_group", + keyword: "required", + params: { missingProperty: "interconnect_group" }, + }); + } + } + } } #validateLeaseIP(serviceName: string) { diff --git a/ts/src/sdl/validateSDL/validateSDLInput.ts b/ts/src/sdl/validateSDL/validateSDLInput.ts index d7881c5c..eabadc63 100644 --- a/ts/src/sdl/validateSDL/validateSDLInput.ts +++ b/ts/src/sdl/validateSDL/validateSDLInput.ts @@ -87,6 +87,26 @@ export interface SDLInput { }[] | null; }; + /** + * Opt this resource into GPU interconnect. + * When true, the bid engine matches against + * providers advertising + * capabilities/gpu-interconnect and the + * workload builder requests one HCA per GPU + * unit (1:1). Fabric (InfiniBand vs RoCE) is + * chosen by the provider, not the SDL. + * + */ + interconnect?: boolean; + /** + * Peer-group label for multi-service NCCL + * groups. All services sharing the same value + * form one anti-affinity group: the provider + * schedules them on distinct nodes. Implies + * interconnect: true. + * + */ + interconnect_group?: string; }; units?: string | number; }; @@ -239,7 +259,7 @@ var require_ucs2length = __commonJS({ // var validate = validate27; var stdin_default = validate27; -var schema28 = { "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": false, "description": "Schema for Akash Stack Definition Language (SDL) YAML input files.\n\nNote: This schema validates structure only. Semantic validations (cross-references,\nunused endpoints, profile references, etc.) are performed at runtime by the Go parser.\nSee README.md for details.\n", "definitions": { "stringArrayOrNull": { "description": "String array or null value (used for command args and env vars)", "oneOf": [{ "items": { "type": "string" }, "type": "array" }, { "type": "null" }] }, "portNumber": { "description": "Valid TCP/UDP port number (1-65535)", "type": "integer", "minimum": 1, "maximum": 65535 }, "httpErrorCode": { "description": "HTTP error codes for proxy retry logic", "enum": ["error", "timeout", "500", "502", "503", "504", "403", "404", "429", "off"], "type": "string" }, "priceCoin": { "description": "Price definition with amount and denomination", "additionalProperties": false, "properties": { "amount": { "oneOf": [{ "type": "string", "pattern": "^[0-9]+(\\.[0-9]+)?$", "description": "Positive number as string" }, { "type": "number", "minimum": 0, "description": "Positive number" }] }, "denom": { "enum": ["uakt", "uact"], "type": "string" } }, "required": ["denom", "amount"], "type": "object" }, "storageRamClassMustNotBePersistent": { "description": "RAM storage class must not have persistent=true", "not": { "properties": { "class": { "const": "ram" }, "persistent": { "oneOf": [{ "const": true }, { "const": "true" }] } }, "required": ["class", "persistent"] } }, "storageNonRamClassRequiresPersistent": { "description": "Non-RAM storage classes (beta1, beta2, beta3, default) require persistent=true", "if": { "properties": { "class": { "enum": ["beta1", "beta2", "beta3", "default"] } }, "required": ["class"] }, "then": { "properties": { "persistent": { "oneOf": [{ "const": true }, { "const": "true" }] } }, "required": ["persistent"] } }, "storageAttributesValidation": { "description": "Storage attributes validation:\n1. RAM class must not be persistent\n2. Non-RAM classes (beta1, beta2, beta3, default) require persistent=true\n", "additionalProperties": false, "properties": { "class": { "type": "string" }, "persistent": { "oneOf": [{ "type": "boolean" }, { "type": "string" }] } }, "allOf": [{ "$ref": "#/definitions/storageRamClassMustNotBePersistent" }, { "$ref": "#/definitions/storageNonRamClassRequiresPersistent" }], "required": ["class", "persistent"], "type": "object" }, "storageVolume": { "description": "Storage volume definition with size and optional attributes", "additionalProperties": false, "properties": { "attributes": { "$ref": "#/definitions/storageAttributesValidation" }, "name": { "type": "string" }, "size": { "type": "string" } }, "required": ["size"], "type": "object" }, "absolutePath": { "description": "Absolute filesystem path starting with /", "type": "string", "minLength": 1, "pattern": "^/" }, "gpuUnitsGt0RequiresAttributes": { "description": "GPU units > 0 requires attributes to be present", "if": { "properties": { "units": { "oneOf": [{ "type": "number", "exclusiveMinimum": 0 }, { "type": "string", "not": { "pattern": "^0+(\\.0+)?$" } }] } }, "required": ["units"] }, "then": { "required": ["attributes"] } }, "gpuAttributesRequireUnitsGt0": { "description": "GPU attributes present requires units > 0", "if": { "required": ["attributes"] }, "then": { "properties": { "units": { "oneOf": [{ "type": "number", "exclusiveMinimum": 0 }, { "type": "string", "not": { "pattern": "^0+(\\.0+)?$" } }] } }, "required": ["units"] } }, "exposeToWithIpEnforcesGlobal": { "description": "Expose to with IP enforces global", "if": { "properties": { "ip": { "type": "string", "minLength": 1 } }, "required": ["ip"] }, "then": { "properties": { "global": { "const": true } }, "required": ["global"] } } }, "properties": { "deployment": { "additionalProperties": { "additionalProperties": { "additionalProperties": false, "properties": { "count": { "minimum": 1, "type": "integer" }, "profile": { "type": "string" } }, "required": ["profile", "count"], "type": "object" }, "type": "object" }, "type": "object" }, "endpoints": { "additionalProperties": false, "patternProperties": { "^[a-z]+[-_0-9a-z]+$": { "additionalProperties": false, "properties": { "kind": { "enum": ["ip"], "type": "string" } }, "required": ["kind"], "type": "object" } }, "type": "object" }, "include": { "description": "Optional list of files to include", "items": { "type": "string" }, "type": "array" }, "profiles": { "additionalProperties": false, "properties": { "compute": { "additionalProperties": { "additionalProperties": false, "properties": { "resources": { "additionalProperties": false, "properties": { "cpu": { "additionalProperties": false, "properties": { "attributes": { "type": "object" }, "units": { "oneOf": [{ "type": "string", "pattern": "^[0-9]+(\\.[0-9]+)?[a-zA-Z]*$", "not": { "pattern": "^0+(\\.0+)?m?$" } }, { "type": "number", "exclusiveMinimum": 0 }] } }, "required": ["units"], "type": "object" }, "gpu": { "description": "GPU resource specification.\n- units defaults to 0 if omitted\n- Bidirectional validation: units > 0 requires attributes, and attributes require units > 0\n", "additionalProperties": false, "properties": { "attributes": { "additionalProperties": false, "properties": { "vendor": { "additionalProperties": false, "minProperties": 1, "properties": { "nvidia": { "oneOf": [{ "type": "array", "items": { "additionalProperties": false, "properties": { "interface": { "enum": ["pcie", "sxm"], "type": "string" }, "model": { "type": "string" }, "ram": { "type": "string" } }, "type": "object" } }, { "type": "null" }] } }, "type": "object" } }, "type": "object" }, "units": { "oneOf": [{ "type": "string" }, { "type": "number" }] } }, "allOf": [{ "$ref": "#/definitions/gpuUnitsGt0RequiresAttributes" }, { "$ref": "#/definitions/gpuAttributesRequireUnitsGt0" }], "type": "object" }, "memory": { "additionalProperties": false, "properties": { "size": { "type": "string" } }, "required": ["size"], "type": "object" }, "storage": { "oneOf": [{ "$ref": "#/definitions/storageVolume" }, { "items": { "$ref": "#/definitions/storageVolume" }, "type": "array" }] } }, "required": ["cpu", "memory", "storage"], "type": "object" } }, "required": ["resources"], "type": "object" }, "type": "object" }, "placement": { "additionalProperties": { "additionalProperties": false, "properties": { "attributes": { "type": "object" }, "pricing": { "additionalProperties": { "$ref": "#/definitions/priceCoin" }, "type": "object" }, "signedBy": { "additionalProperties": false, "properties": { "allOf": { "items": { "type": "string" }, "type": "array" }, "anyOf": { "items": { "type": "string" }, "type": "array" } }, "type": "object" } }, "required": ["pricing"], "type": "object" }, "type": "object" } }, "required": ["compute", "placement"], "type": "object" }, "services": { "additionalProperties": { "properties": { "args": { "$ref": "#/definitions/stringArrayOrNull" }, "command": { "$ref": "#/definitions/stringArrayOrNull" }, "credentials": { "additionalProperties": false, "properties": { "email": { "type": "string", "minLength": 5 }, "host": { "type": "string", "minLength": 1 }, "password": { "type": "string", "minLength": 6 }, "username": { "type": "string", "minLength": 1 } }, "required": ["host", "username", "password"], "type": "object" }, "dependencies": { "items": { "additionalProperties": false, "properties": { "service": { "type": "string" } }, "type": "object" }, "type": "array" }, "env": { "$ref": "#/definitions/stringArrayOrNull" }, "expose": { "oneOf": [{ "type": "array", "items": { "additionalProperties": false, "properties": { "accept": { "items": { "type": "string" }, "type": "array" }, "as": { "$ref": "#/definitions/portNumber" }, "http_options": { "additionalProperties": false, "properties": { "max_body_size": { "type": "integer", "minimum": 0, "maximum": 104857600, "description": "Maximum body size in bytes (max 100 MB)" }, "next_cases": { "oneOf": [{ "type": "array", "items": { "$ref": "#/definitions/httpErrorCode" }, "contains": { "const": "off" }, "maxItems": 1, "minItems": 1 }, { "type": "array", "items": { "$ref": "#/definitions/httpErrorCode" }, "not": { "contains": { "const": "off" } } }] }, "next_timeout": { "type": "integer", "minimum": 0 }, "next_tries": { "type": "integer", "minimum": 0 }, "read_timeout": { "type": "integer", "minimum": 0, "maximum": 6e4, "description": "Read timeout in milliseconds (max 60 seconds)" }, "send_timeout": { "type": "integer", "minimum": 0, "maximum": 6e4, "description": "Send timeout in milliseconds (max 60 seconds)" } }, "type": "object" }, "port": { "$ref": "#/definitions/portNumber" }, "proto": { "enum": ["TCP", "UDP", "tcp", "udp"], "type": "string" }, "to": { "items": { "additionalProperties": false, "properties": { "global": { "type": "boolean" }, "ip": { "minLength": 1, "type": "string" }, "service": { "type": "string" } }, "allOf": [{ "$ref": "#/definitions/exposeToWithIpEnforcesGlobal" }], "type": "object" }, "type": "array" } }, "required": ["port"], "type": "object" } }, { "type": "null" }] }, "image": { "type": "string", "minLength": 1 }, "params": { "additionalProperties": false, "properties": { "storage": { "additionalProperties": { "additionalProperties": false, "properties": { "mount": { "$ref": "#/definitions/absolutePath" }, "readOnly": { "type": "boolean" } }, "type": "object" }, "type": "object" }, "permissions": { "additionalProperties": false, "properties": { "read": { "items": { "type": "string", "enum": ["deployment", "logs", "events"] }, "type": "array" } }, "type": "object" } }, "type": "object" } }, "required": ["image"], "type": "object", "additionalProperties": false }, "type": "object" }, "reclamation": { "description": "Deployment-level reclamation requirements (optional). When set, providers must offer a reclamation window meeting or exceeding the specified minimum to bid on this deployment.", "additionalProperties": false, "properties": { "min_window": { "type": "string", "pattern": "^[1-9][0-9]*(s|m|h)$", "description": 'Minimum reclamation window the tenant requires, as a whole number followed by a unit (s, m, or h). E.g. "1h", "24h", "720h".' } }, "required": ["min_window"], "type": "object" }, "version": { "description": "SDL version", "enum": ["2.0", "2.1"], "type": "string" } }, "required": ["version", "services", "profiles", "deployment"], "title": "Akash SDL Input Schema", "type": "object" }; +var schema28 = { "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": false, "description": "Schema for Akash Stack Definition Language (SDL) YAML input files.\n\nNote: This schema validates structure only. Semantic validations (cross-references,\nunused endpoints, profile references, etc.) are performed at runtime by the Go parser.\nSee README.md for details.\n", "definitions": { "stringArrayOrNull": { "description": "String array or null value (used for command args and env vars)", "oneOf": [{ "items": { "type": "string" }, "type": "array" }, { "type": "null" }] }, "portNumber": { "description": "Valid TCP/UDP port number (1-65535)", "type": "integer", "minimum": 1, "maximum": 65535 }, "httpErrorCode": { "description": "HTTP error codes for proxy retry logic", "enum": ["error", "timeout", "500", "502", "503", "504", "403", "404", "429", "off"], "type": "string" }, "priceCoin": { "description": "Price definition with amount and denomination", "additionalProperties": false, "properties": { "amount": { "oneOf": [{ "type": "string", "pattern": "^[0-9]+(\\.[0-9]+)?$", "description": "Positive number as string" }, { "type": "number", "minimum": 0, "description": "Positive number" }] }, "denom": { "enum": ["uakt", "uact"], "type": "string" } }, "required": ["denom", "amount"], "type": "object" }, "storageRamClassMustNotBePersistent": { "description": "RAM storage class must not have persistent=true", "not": { "properties": { "class": { "const": "ram" }, "persistent": { "oneOf": [{ "const": true }, { "const": "true" }] } }, "required": ["class", "persistent"] } }, "storageNonRamClassRequiresPersistent": { "description": "Non-RAM storage classes (beta1, beta2, beta3, default) require persistent=true", "if": { "properties": { "class": { "enum": ["beta1", "beta2", "beta3", "default"] } }, "required": ["class"] }, "then": { "properties": { "persistent": { "oneOf": [{ "const": true }, { "const": "true" }] } }, "required": ["persistent"] } }, "storageAttributesValidation": { "description": "Storage attributes validation:\n1. RAM class must not be persistent\n2. Non-RAM classes (beta1, beta2, beta3, default) require persistent=true\n", "additionalProperties": false, "properties": { "class": { "type": "string" }, "persistent": { "oneOf": [{ "type": "boolean" }, { "type": "string" }] } }, "allOf": [{ "$ref": "#/definitions/storageRamClassMustNotBePersistent" }, { "$ref": "#/definitions/storageNonRamClassRequiresPersistent" }], "required": ["class", "persistent"], "type": "object" }, "storageVolume": { "description": "Storage volume definition with size and optional attributes", "additionalProperties": false, "properties": { "attributes": { "$ref": "#/definitions/storageAttributesValidation" }, "name": { "type": "string" }, "size": { "type": "string" } }, "required": ["size"], "type": "object" }, "absolutePath": { "description": "Absolute filesystem path starting with /", "type": "string", "minLength": 1, "pattern": "^/" }, "gpuUnitsGt0RequiresAttributes": { "description": "GPU units > 0 requires attributes to be present", "if": { "properties": { "units": { "oneOf": [{ "type": "number", "exclusiveMinimum": 0 }, { "type": "string", "not": { "pattern": "^0+(\\.0+)?$" } }] } }, "required": ["units"] }, "then": { "required": ["attributes"] } }, "gpuAttributesRequireUnitsGt0": { "description": "GPU attributes present requires units > 0", "if": { "required": ["attributes"] }, "then": { "properties": { "units": { "oneOf": [{ "type": "number", "exclusiveMinimum": 0 }, { "type": "string", "not": { "pattern": "^0+(\\.0+)?$" } }] } }, "required": ["units"] } }, "exposeToWithIpEnforcesGlobal": { "description": "Expose to with IP enforces global", "if": { "properties": { "ip": { "type": "string", "minLength": 1 } }, "required": ["ip"] }, "then": { "properties": { "global": { "const": true } }, "required": ["global"] } } }, "properties": { "deployment": { "additionalProperties": { "additionalProperties": { "additionalProperties": false, "properties": { "count": { "minimum": 1, "type": "integer" }, "profile": { "type": "string" } }, "required": ["profile", "count"], "type": "object" }, "type": "object" }, "type": "object" }, "endpoints": { "additionalProperties": false, "patternProperties": { "^[a-z]+[-_0-9a-z]+$": { "additionalProperties": false, "properties": { "kind": { "enum": ["ip"], "type": "string" } }, "required": ["kind"], "type": "object" } }, "type": "object" }, "include": { "description": "Optional list of files to include", "items": { "type": "string" }, "type": "array" }, "profiles": { "additionalProperties": false, "properties": { "compute": { "additionalProperties": { "additionalProperties": false, "properties": { "resources": { "additionalProperties": false, "properties": { "cpu": { "additionalProperties": false, "properties": { "attributes": { "type": "object" }, "units": { "oneOf": [{ "type": "string", "pattern": "^[0-9]+(\\.[0-9]+)?[a-zA-Z]*$", "not": { "pattern": "^0+(\\.0+)?m?$" } }, { "type": "number", "exclusiveMinimum": 0 }] } }, "required": ["units"], "type": "object" }, "gpu": { "description": "GPU resource specification.\n- units defaults to 0 if omitted\n- Bidirectional validation: units > 0 requires attributes, and attributes require units > 0\n", "additionalProperties": false, "properties": { "attributes": { "additionalProperties": false, "properties": { "vendor": { "additionalProperties": false, "minProperties": 1, "properties": { "nvidia": { "oneOf": [{ "type": "array", "items": { "additionalProperties": false, "properties": { "interface": { "enum": ["pcie", "sxm"], "type": "string" }, "model": { "type": "string" }, "ram": { "type": "string" } }, "type": "object" } }, { "type": "null" }] } }, "type": "object" }, "interconnect": { "description": "Opt this resource into GPU interconnect.\nWhen true, the bid engine matches against\nproviders advertising\ncapabilities/gpu-interconnect and the\nworkload builder requests one HCA per GPU\nunit (1:1). Fabric (InfiniBand vs RoCE) is\nchosen by the provider, not the SDL.\n", "type": "boolean" }, "interconnect_group": { "description": "Peer-group label for multi-service NCCL\ngroups. All services sharing the same value\nform one anti-affinity group: the provider\nschedules them on distinct nodes. Implies\ninterconnect: true.\n", "type": "string" } }, "type": "object" }, "units": { "oneOf": [{ "type": "string" }, { "type": "number" }] } }, "allOf": [{ "$ref": "#/definitions/gpuUnitsGt0RequiresAttributes" }, { "$ref": "#/definitions/gpuAttributesRequireUnitsGt0" }], "type": "object" }, "memory": { "additionalProperties": false, "properties": { "size": { "type": "string" } }, "required": ["size"], "type": "object" }, "storage": { "oneOf": [{ "$ref": "#/definitions/storageVolume" }, { "items": { "$ref": "#/definitions/storageVolume" }, "type": "array" }] } }, "required": ["cpu", "memory", "storage"], "type": "object" } }, "required": ["resources"], "type": "object" }, "type": "object" }, "placement": { "additionalProperties": { "additionalProperties": false, "properties": { "attributes": { "type": "object" }, "pricing": { "additionalProperties": { "$ref": "#/definitions/priceCoin" }, "type": "object" }, "signedBy": { "additionalProperties": false, "properties": { "allOf": { "items": { "type": "string" }, "type": "array" }, "anyOf": { "items": { "type": "string" }, "type": "array" } }, "type": "object" } }, "required": ["pricing"], "type": "object" }, "type": "object" } }, "required": ["compute", "placement"], "type": "object" }, "services": { "additionalProperties": { "properties": { "args": { "$ref": "#/definitions/stringArrayOrNull" }, "command": { "$ref": "#/definitions/stringArrayOrNull" }, "credentials": { "additionalProperties": false, "properties": { "email": { "type": "string", "minLength": 5 }, "host": { "type": "string", "minLength": 1 }, "password": { "type": "string", "minLength": 6 }, "username": { "type": "string", "minLength": 1 } }, "required": ["host", "username", "password"], "type": "object" }, "dependencies": { "items": { "additionalProperties": false, "properties": { "service": { "type": "string" } }, "type": "object" }, "type": "array" }, "env": { "$ref": "#/definitions/stringArrayOrNull" }, "expose": { "oneOf": [{ "type": "array", "items": { "additionalProperties": false, "properties": { "accept": { "items": { "type": "string" }, "type": "array" }, "as": { "$ref": "#/definitions/portNumber" }, "http_options": { "additionalProperties": false, "properties": { "max_body_size": { "type": "integer", "minimum": 0, "maximum": 104857600, "description": "Maximum body size in bytes (max 100 MB)" }, "next_cases": { "oneOf": [{ "type": "array", "items": { "$ref": "#/definitions/httpErrorCode" }, "contains": { "const": "off" }, "maxItems": 1, "minItems": 1 }, { "type": "array", "items": { "$ref": "#/definitions/httpErrorCode" }, "not": { "contains": { "const": "off" } } }] }, "next_timeout": { "type": "integer", "minimum": 0 }, "next_tries": { "type": "integer", "minimum": 0 }, "read_timeout": { "type": "integer", "minimum": 0, "maximum": 6e4, "description": "Read timeout in milliseconds (max 60 seconds)" }, "send_timeout": { "type": "integer", "minimum": 0, "maximum": 6e4, "description": "Send timeout in milliseconds (max 60 seconds)" } }, "type": "object" }, "port": { "$ref": "#/definitions/portNumber" }, "proto": { "enum": ["TCP", "UDP", "tcp", "udp"], "type": "string" }, "to": { "items": { "additionalProperties": false, "properties": { "global": { "type": "boolean" }, "ip": { "minLength": 1, "type": "string" }, "service": { "type": "string" } }, "allOf": [{ "$ref": "#/definitions/exposeToWithIpEnforcesGlobal" }], "type": "object" }, "type": "array" } }, "required": ["port"], "type": "object" } }, { "type": "null" }] }, "image": { "type": "string", "minLength": 1 }, "params": { "additionalProperties": false, "properties": { "storage": { "additionalProperties": { "additionalProperties": false, "properties": { "mount": { "$ref": "#/definitions/absolutePath" }, "readOnly": { "type": "boolean" } }, "type": "object" }, "type": "object" }, "permissions": { "additionalProperties": false, "properties": { "read": { "items": { "type": "string", "enum": ["deployment", "logs", "events"] }, "type": "array" } }, "type": "object" } }, "type": "object" } }, "required": ["image"], "type": "object", "additionalProperties": false }, "type": "object" }, "reclamation": { "description": "Deployment-level reclamation requirements (optional). When set, providers must offer a reclamation window meeting or exceeding the specified minimum to bid on this deployment.", "additionalProperties": false, "properties": { "min_window": { "type": "string", "pattern": "^[1-9][0-9]*(s|m|h)$", "description": 'Minimum reclamation window the tenant requires, as a whole number followed by a unit (s, m, or h). E.g. "1h", "24h", "720h".' } }, "required": ["min_window"], "type": "object" }, "version": { "description": "SDL version", "enum": ["2.0", "2.1"], "type": "string" } }, "required": ["version", "services", "profiles", "deployment"], "title": "Akash SDL Input Schema", "type": "object" }; var schema35 = { "description": "Price definition with amount and denomination", "additionalProperties": false, "properties": { "amount": { "oneOf": [{ "type": "string", "pattern": "^[0-9]+(\\.[0-9]+)?$", "description": "Positive number as string" }, { "type": "number", "minimum": 0, "description": "Positive number" }] }, "denom": { "enum": ["uakt", "uact"], "type": "string" } }, "required": ["denom", "amount"], "type": "object" }; var schema40 = { "description": "HTTP error codes for proxy retry logic", "enum": ["error", "timeout", "500", "502", "503", "504", "403", "404", "429", "off"], "type": "string" }; var pattern4 = new RegExp("^[a-z]+[-_0-9a-z]+$", "u"); @@ -1498,7 +1518,7 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r let data20 = data17.attributes; if (data20 && typeof data20 == "object" && !Array.isArray(data20)) { for (const key13 in data20) { - if (!(key13 === "vendor")) { + if (!(key13 === "vendor" || key13 === "interconnect" || key13 === "interconnect_group")) { const err62 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/gpu/attributes", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/properties/attributes/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key13 }, message: "must NOT have additional properties" }; if (vErrors === null) { vErrors = [err62]; @@ -1669,47 +1689,69 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r errors++; } } + if (data20.interconnect !== void 0) { + if (typeof data20.interconnect !== "boolean") { + const err75 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/gpu/attributes/interconnect", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/properties/attributes/properties/interconnect/type", keyword: "type", params: { type: "boolean" }, message: "must be boolean" }; + if (vErrors === null) { + vErrors = [err75]; + } else { + vErrors.push(err75); + } + errors++; + } + } + if (data20.interconnect_group !== void 0) { + if (typeof data20.interconnect_group !== "string") { + const err76 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/gpu/attributes/interconnect_group", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/properties/attributes/properties/interconnect_group/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + if (vErrors === null) { + vErrors = [err76]; + } else { + vErrors.push(err76); + } + errors++; + } + } } else { - const err75 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/gpu/attributes", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/properties/attributes/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err77 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/gpu/attributes", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/properties/attributes/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err75]; + vErrors = [err77]; } else { - vErrors.push(err75); + vErrors.push(err77); } errors++; } } if (data17.units !== void 0) { - let data27 = data17.units; - const _errs103 = errors; + let data29 = data17.units; + const _errs107 = errors; let valid33 = false; let passing4 = null; - const _errs104 = errors; - if (typeof data27 !== "string") { - const err76 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/gpu/units", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/properties/units/oneOf/0/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + const _errs108 = errors; + if (typeof data29 !== "string") { + const err78 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/gpu/units", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/properties/units/oneOf/0/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err76]; + vErrors = [err78]; } else { - vErrors.push(err76); + vErrors.push(err78); } errors++; } - var _valid6 = _errs104 === errors; + var _valid6 = _errs108 === errors; if (_valid6) { valid33 = true; passing4 = 0; } - const _errs106 = errors; - if (!(typeof data27 == "number")) { - const err77 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/gpu/units", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/properties/units/oneOf/1/type", keyword: "type", params: { type: "number" }, message: "must be number" }; + const _errs110 = errors; + if (!(typeof data29 == "number")) { + const err79 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/gpu/units", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/properties/units/oneOf/1/type", keyword: "type", params: { type: "number" }, message: "must be number" }; if (vErrors === null) { - vErrors = [err77]; + vErrors = [err79]; } else { - vErrors.push(err77); + vErrors.push(err79); } errors++; } - var _valid6 = _errs106 === errors; + var _valid6 = _errs110 === errors; if (_valid6 && valid33) { valid33 = false; passing4 = [passing4, 1]; @@ -1720,18 +1762,18 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r } } if (!valid33) { - const err78 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/gpu/units", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/properties/units/oneOf", keyword: "oneOf", params: { passingSchemas: passing4 }, message: "must match exactly one schema in oneOf" }; + const err80 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/gpu/units", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/properties/units/oneOf", keyword: "oneOf", params: { passingSchemas: passing4 }, message: "must match exactly one schema in oneOf" }; if (vErrors === null) { - vErrors = [err78]; + vErrors = [err80]; } else { - vErrors.push(err78); + vErrors.push(err80); } errors++; } else { - errors = _errs103; + errors = _errs107; if (vErrors !== null) { - if (_errs103) { - vErrors.length = _errs103; + if (_errs107) { + vErrors.length = _errs107; } else { vErrors = null; } @@ -1739,93 +1781,93 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r } } } else { - const err79 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/gpu", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err81 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/gpu", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/gpu/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err79]; + vErrors = [err81]; } else { - vErrors.push(err79); + vErrors.push(err81); } errors++; } } if (data13.memory !== void 0) { - let data28 = data13.memory; - if (data28 && typeof data28 == "object" && !Array.isArray(data28)) { - if (data28.size === void 0) { - const err80 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/memory", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/memory/required", keyword: "required", params: { missingProperty: "size" }, message: "must have required property 'size'" }; + let data30 = data13.memory; + if (data30 && typeof data30 == "object" && !Array.isArray(data30)) { + if (data30.size === void 0) { + const err82 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/memory", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/memory/required", keyword: "required", params: { missingProperty: "size" }, message: "must have required property 'size'" }; if (vErrors === null) { - vErrors = [err80]; + vErrors = [err82]; } else { - vErrors.push(err80); + vErrors.push(err82); } errors++; } - for (const key16 in data28) { + for (const key16 in data30) { if (!(key16 === "size")) { - const err81 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/memory", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/memory/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key16 }, message: "must NOT have additional properties" }; + const err83 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/memory", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/memory/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key16 }, message: "must NOT have additional properties" }; if (vErrors === null) { - vErrors = [err81]; + vErrors = [err83]; } else { - vErrors.push(err81); + vErrors.push(err83); } errors++; } } - if (data28.size !== void 0) { - if (typeof data28.size !== "string") { - const err82 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/memory/size", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/memory/properties/size/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + if (data30.size !== void 0) { + if (typeof data30.size !== "string") { + const err84 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/memory/size", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/memory/properties/size/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err82]; + vErrors = [err84]; } else { - vErrors.push(err82); + vErrors.push(err84); } errors++; } } } else { - const err83 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/memory", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/memory/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err85 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/memory", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/memory/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err83]; + vErrors = [err85]; } else { - vErrors.push(err83); + vErrors.push(err85); } errors++; } } if (data13.storage !== void 0) { - let data30 = data13.storage; - const _errs114 = errors; + let data32 = data13.storage; + const _errs118 = errors; let valid35 = false; let passing5 = null; - const _errs115 = errors; - if (!validate28(data30, { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/storage", parentData: data13, parentDataProperty: "storage", rootData })) { + const _errs119 = errors; + if (!validate28(data32, { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/storage", parentData: data13, parentDataProperty: "storage", rootData })) { vErrors = vErrors === null ? validate28.errors : vErrors.concat(validate28.errors); errors = vErrors.length; } - var _valid7 = _errs115 === errors; + var _valid7 = _errs119 === errors; if (_valid7) { valid35 = true; passing5 = 0; } - const _errs116 = errors; - if (Array.isArray(data30)) { - const len2 = data30.length; + const _errs120 = errors; + if (Array.isArray(data32)) { + const len2 = data32.length; for (let i2 = 0; i2 < len2; i2++) { - if (!validate28(data30[i2], { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/storage/" + i2, parentData: data30, parentDataProperty: i2, rootData })) { + if (!validate28(data32[i2], { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/storage/" + i2, parentData: data32, parentDataProperty: i2, rootData })) { vErrors = vErrors === null ? validate28.errors : vErrors.concat(validate28.errors); errors = vErrors.length; } } } else { - const err84 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/storage", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/storage/oneOf/1/type", keyword: "type", params: { type: "array" }, message: "must be array" }; + const err86 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/storage", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/storage/oneOf/1/type", keyword: "type", params: { type: "array" }, message: "must be array" }; if (vErrors === null) { - vErrors = [err84]; + vErrors = [err86]; } else { - vErrors.push(err84); + vErrors.push(err86); } errors++; } - var _valid7 = _errs116 === errors; + var _valid7 = _errs120 === errors; if (_valid7 && valid35) { valid35 = false; passing5 = [passing5, 1]; @@ -1836,18 +1878,18 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r } } if (!valid35) { - const err85 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/storage", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/storage/oneOf", keyword: "oneOf", params: { passingSchemas: passing5 }, message: "must match exactly one schema in oneOf" }; + const err87 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources/storage", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/properties/storage/oneOf", keyword: "oneOf", params: { passingSchemas: passing5 }, message: "must match exactly one schema in oneOf" }; if (vErrors === null) { - vErrors = [err85]; + vErrors = [err87]; } else { - vErrors.push(err85); + vErrors.push(err87); } errors++; } else { - errors = _errs114; + errors = _errs118; if (vErrors !== null) { - if (_errs114) { - vErrors.length = _errs114; + if (_errs118) { + vErrors.length = _errs118; } else { vErrors = null; } @@ -1855,159 +1897,159 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r } } } else { - const err86 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err88 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1") + "/resources", schemaPath: "#/properties/profiles/properties/compute/additionalProperties/properties/resources/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err86]; + vErrors = [err88]; } else { - vErrors.push(err86); + vErrors.push(err88); } errors++; } } } else { - const err87 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/profiles/properties/compute/additionalProperties/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err89 = { instancePath: instancePath + "/profiles/compute/" + key8.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/profiles/properties/compute/additionalProperties/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err87]; + vErrors = [err89]; } else { - vErrors.push(err87); + vErrors.push(err89); } errors++; } } } else { - const err88 = { instancePath: instancePath + "/profiles/compute", schemaPath: "#/properties/profiles/properties/compute/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err90 = { instancePath: instancePath + "/profiles/compute", schemaPath: "#/properties/profiles/properties/compute/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err88]; + vErrors = [err90]; } else { - vErrors.push(err88); + vErrors.push(err90); } errors++; } } if (data10.placement !== void 0) { - let data32 = data10.placement; - if (data32 && typeof data32 == "object" && !Array.isArray(data32)) { - for (const key17 in data32) { - let data33 = data32[key17]; - if (data33 && typeof data33 == "object" && !Array.isArray(data33)) { - if (data33.pricing === void 0) { - const err89 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/profiles/properties/placement/additionalProperties/required", keyword: "required", params: { missingProperty: "pricing" }, message: "must have required property 'pricing'" }; + let data34 = data10.placement; + if (data34 && typeof data34 == "object" && !Array.isArray(data34)) { + for (const key17 in data34) { + let data35 = data34[key17]; + if (data35 && typeof data35 == "object" && !Array.isArray(data35)) { + if (data35.pricing === void 0) { + const err91 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/profiles/properties/placement/additionalProperties/required", keyword: "required", params: { missingProperty: "pricing" }, message: "must have required property 'pricing'" }; if (vErrors === null) { - vErrors = [err89]; + vErrors = [err91]; } else { - vErrors.push(err89); + vErrors.push(err91); } errors++; } - for (const key18 in data33) { + for (const key18 in data35) { if (!(key18 === "attributes" || key18 === "pricing" || key18 === "signedBy")) { - const err90 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/profiles/properties/placement/additionalProperties/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key18 }, message: "must NOT have additional properties" }; + const err92 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/profiles/properties/placement/additionalProperties/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key18 }, message: "must NOT have additional properties" }; if (vErrors === null) { - vErrors = [err90]; + vErrors = [err92]; } else { - vErrors.push(err90); + vErrors.push(err92); } errors++; } } - if (data33.attributes !== void 0) { - let data34 = data33.attributes; - if (!(data34 && typeof data34 == "object" && !Array.isArray(data34))) { - const err91 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/attributes", schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/attributes/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + if (data35.attributes !== void 0) { + let data36 = data35.attributes; + if (!(data36 && typeof data36 == "object" && !Array.isArray(data36))) { + const err93 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/attributes", schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/attributes/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err91]; + vErrors = [err93]; } else { - vErrors.push(err91); + vErrors.push(err93); } errors++; } } - if (data33.pricing !== void 0) { - let data35 = data33.pricing; - if (data35 && typeof data35 == "object" && !Array.isArray(data35)) { - for (const key19 in data35) { - let data36 = data35[key19]; - if (data36 && typeof data36 == "object" && !Array.isArray(data36)) { - if (data36.denom === void 0) { - const err92 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/definitions/priceCoin/required", keyword: "required", params: { missingProperty: "denom" }, message: "must have required property 'denom'" }; + if (data35.pricing !== void 0) { + let data37 = data35.pricing; + if (data37 && typeof data37 == "object" && !Array.isArray(data37)) { + for (const key19 in data37) { + let data38 = data37[key19]; + if (data38 && typeof data38 == "object" && !Array.isArray(data38)) { + if (data38.denom === void 0) { + const err94 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/definitions/priceCoin/required", keyword: "required", params: { missingProperty: "denom" }, message: "must have required property 'denom'" }; if (vErrors === null) { - vErrors = [err92]; + vErrors = [err94]; } else { - vErrors.push(err92); + vErrors.push(err94); } errors++; } - if (data36.amount === void 0) { - const err93 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/definitions/priceCoin/required", keyword: "required", params: { missingProperty: "amount" }, message: "must have required property 'amount'" }; + if (data38.amount === void 0) { + const err95 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/definitions/priceCoin/required", keyword: "required", params: { missingProperty: "amount" }, message: "must have required property 'amount'" }; if (vErrors === null) { - vErrors = [err93]; + vErrors = [err95]; } else { - vErrors.push(err93); + vErrors.push(err95); } errors++; } - for (const key20 in data36) { + for (const key20 in data38) { if (!(key20 === "amount" || key20 === "denom")) { - const err94 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/definitions/priceCoin/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key20 }, message: "must NOT have additional properties" }; + const err96 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/definitions/priceCoin/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key20 }, message: "must NOT have additional properties" }; if (vErrors === null) { - vErrors = [err94]; + vErrors = [err96]; } else { - vErrors.push(err94); + vErrors.push(err96); } errors++; } } - if (data36.amount !== void 0) { - let data37 = data36.amount; - const _errs135 = errors; + if (data38.amount !== void 0) { + let data39 = data38.amount; + const _errs139 = errors; let valid43 = false; let passing6 = null; - const _errs136 = errors; - if (typeof data37 === "string") { - if (!pattern10.test(data37)) { - const err95 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/amount", schemaPath: "#/definitions/priceCoin/properties/amount/oneOf/0/pattern", keyword: "pattern", params: { pattern: "^[0-9]+(\\.[0-9]+)?$" }, message: 'must match pattern "^[0-9]+(\\.[0-9]+)?$"' }; + const _errs140 = errors; + if (typeof data39 === "string") { + if (!pattern10.test(data39)) { + const err97 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/amount", schemaPath: "#/definitions/priceCoin/properties/amount/oneOf/0/pattern", keyword: "pattern", params: { pattern: "^[0-9]+(\\.[0-9]+)?$" }, message: 'must match pattern "^[0-9]+(\\.[0-9]+)?$"' }; if (vErrors === null) { - vErrors = [err95]; + vErrors = [err97]; } else { - vErrors.push(err95); + vErrors.push(err97); } errors++; } } else { - const err96 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/amount", schemaPath: "#/definitions/priceCoin/properties/amount/oneOf/0/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + const err98 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/amount", schemaPath: "#/definitions/priceCoin/properties/amount/oneOf/0/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err96]; + vErrors = [err98]; } else { - vErrors.push(err96); + vErrors.push(err98); } errors++; } - var _valid8 = _errs136 === errors; + var _valid8 = _errs140 === errors; if (_valid8) { valid43 = true; passing6 = 0; } - const _errs138 = errors; - if (typeof data37 == "number") { - if (data37 < 0 || isNaN(data37)) { - const err97 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/amount", schemaPath: "#/definitions/priceCoin/properties/amount/oneOf/1/minimum", keyword: "minimum", params: { comparison: ">=", limit: 0 }, message: "must be >= 0" }; + const _errs142 = errors; + if (typeof data39 == "number") { + if (data39 < 0 || isNaN(data39)) { + const err99 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/amount", schemaPath: "#/definitions/priceCoin/properties/amount/oneOf/1/minimum", keyword: "minimum", params: { comparison: ">=", limit: 0 }, message: "must be >= 0" }; if (vErrors === null) { - vErrors = [err97]; + vErrors = [err99]; } else { - vErrors.push(err97); + vErrors.push(err99); } errors++; } } else { - const err98 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/amount", schemaPath: "#/definitions/priceCoin/properties/amount/oneOf/1/type", keyword: "type", params: { type: "number" }, message: "must be number" }; + const err100 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/amount", schemaPath: "#/definitions/priceCoin/properties/amount/oneOf/1/type", keyword: "type", params: { type: "number" }, message: "must be number" }; if (vErrors === null) { - vErrors = [err98]; + vErrors = [err100]; } else { - vErrors.push(err98); + vErrors.push(err100); } errors++; } - var _valid8 = _errs138 === errors; + var _valid8 = _errs142 === errors; if (_valid8 && valid43) { valid43 = false; passing6 = [passing6, 1]; @@ -2018,239 +2060,239 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r } } if (!valid43) { - const err99 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/amount", schemaPath: "#/definitions/priceCoin/properties/amount/oneOf", keyword: "oneOf", params: { passingSchemas: passing6 }, message: "must match exactly one schema in oneOf" }; + const err101 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/amount", schemaPath: "#/definitions/priceCoin/properties/amount/oneOf", keyword: "oneOf", params: { passingSchemas: passing6 }, message: "must match exactly one schema in oneOf" }; if (vErrors === null) { - vErrors = [err99]; + vErrors = [err101]; } else { - vErrors.push(err99); + vErrors.push(err101); } errors++; } else { - errors = _errs135; + errors = _errs139; if (vErrors !== null) { - if (_errs135) { - vErrors.length = _errs135; + if (_errs139) { + vErrors.length = _errs139; } else { vErrors = null; } } } } - if (data36.denom !== void 0) { - let data38 = data36.denom; - if (typeof data38 !== "string") { - const err100 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/denom", schemaPath: "#/definitions/priceCoin/properties/denom/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + if (data38.denom !== void 0) { + let data40 = data38.denom; + if (typeof data40 !== "string") { + const err102 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/denom", schemaPath: "#/definitions/priceCoin/properties/denom/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err100]; + vErrors = [err102]; } else { - vErrors.push(err100); + vErrors.push(err102); } errors++; } - if (!(data38 === "uakt" || data38 === "uact")) { - const err101 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/denom", schemaPath: "#/definitions/priceCoin/properties/denom/enum", keyword: "enum", params: { allowedValues: schema35.properties.denom.enum }, message: "must be equal to one of the allowed values" }; + if (!(data40 === "uakt" || data40 === "uact")) { + const err103 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1") + "/denom", schemaPath: "#/definitions/priceCoin/properties/denom/enum", keyword: "enum", params: { allowedValues: schema35.properties.denom.enum }, message: "must be equal to one of the allowed values" }; if (vErrors === null) { - vErrors = [err101]; + vErrors = [err103]; } else { - vErrors.push(err101); + vErrors.push(err103); } errors++; } } } else { - const err102 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/definitions/priceCoin/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err104 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing/" + key19.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/definitions/priceCoin/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err102]; + vErrors = [err104]; } else { - vErrors.push(err102); + vErrors.push(err104); } errors++; } } } else { - const err103 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing", schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/pricing/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err105 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/pricing", schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/pricing/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err103]; + vErrors = [err105]; } else { - vErrors.push(err103); + vErrors.push(err105); } errors++; } } - if (data33.signedBy !== void 0) { - let data39 = data33.signedBy; - if (data39 && typeof data39 == "object" && !Array.isArray(data39)) { - for (const key21 in data39) { + if (data35.signedBy !== void 0) { + let data41 = data35.signedBy; + if (data41 && typeof data41 == "object" && !Array.isArray(data41)) { + for (const key21 in data41) { if (!(key21 === "allOf" || key21 === "anyOf")) { - const err104 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/signedBy", schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/signedBy/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key21 }, message: "must NOT have additional properties" }; + const err106 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/signedBy", schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/signedBy/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key21 }, message: "must NOT have additional properties" }; if (vErrors === null) { - vErrors = [err104]; + vErrors = [err106]; } else { - vErrors.push(err104); + vErrors.push(err106); } errors++; } } - if (data39.allOf !== void 0) { - let data40 = data39.allOf; - if (Array.isArray(data40)) { - const len3 = data40.length; + if (data41.allOf !== void 0) { + let data42 = data41.allOf; + if (Array.isArray(data42)) { + const len3 = data42.length; for (let i3 = 0; i3 < len3; i3++) { - if (typeof data40[i3] !== "string") { - const err105 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/signedBy/allOf/" + i3, schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/signedBy/properties/allOf/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + if (typeof data42[i3] !== "string") { + const err107 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/signedBy/allOf/" + i3, schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/signedBy/properties/allOf/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err105]; + vErrors = [err107]; } else { - vErrors.push(err105); + vErrors.push(err107); } errors++; } } } else { - const err106 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/signedBy/allOf", schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/signedBy/properties/allOf/type", keyword: "type", params: { type: "array" }, message: "must be array" }; + const err108 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/signedBy/allOf", schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/signedBy/properties/allOf/type", keyword: "type", params: { type: "array" }, message: "must be array" }; if (vErrors === null) { - vErrors = [err106]; + vErrors = [err108]; } else { - vErrors.push(err106); + vErrors.push(err108); } errors++; } } - if (data39.anyOf !== void 0) { - let data42 = data39.anyOf; - if (Array.isArray(data42)) { - const len4 = data42.length; + if (data41.anyOf !== void 0) { + let data44 = data41.anyOf; + if (Array.isArray(data44)) { + const len4 = data44.length; for (let i4 = 0; i4 < len4; i4++) { - if (typeof data42[i4] !== "string") { - const err107 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/signedBy/anyOf/" + i4, schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/signedBy/properties/anyOf/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + if (typeof data44[i4] !== "string") { + const err109 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/signedBy/anyOf/" + i4, schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/signedBy/properties/anyOf/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err107]; + vErrors = [err109]; } else { - vErrors.push(err107); + vErrors.push(err109); } errors++; } } } else { - const err108 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/signedBy/anyOf", schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/signedBy/properties/anyOf/type", keyword: "type", params: { type: "array" }, message: "must be array" }; + const err110 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/signedBy/anyOf", schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/signedBy/properties/anyOf/type", keyword: "type", params: { type: "array" }, message: "must be array" }; if (vErrors === null) { - vErrors = [err108]; + vErrors = [err110]; } else { - vErrors.push(err108); + vErrors.push(err110); } errors++; } } } else { - const err109 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/signedBy", schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/signedBy/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err111 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1") + "/signedBy", schemaPath: "#/properties/profiles/properties/placement/additionalProperties/properties/signedBy/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err109]; + vErrors = [err111]; } else { - vErrors.push(err109); + vErrors.push(err111); } errors++; } } } else { - const err110 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/profiles/properties/placement/additionalProperties/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err112 = { instancePath: instancePath + "/profiles/placement/" + key17.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/profiles/properties/placement/additionalProperties/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err110]; + vErrors = [err112]; } else { - vErrors.push(err110); + vErrors.push(err112); } errors++; } } } else { - const err111 = { instancePath: instancePath + "/profiles/placement", schemaPath: "#/properties/profiles/properties/placement/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err113 = { instancePath: instancePath + "/profiles/placement", schemaPath: "#/properties/profiles/properties/placement/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err111]; + vErrors = [err113]; } else { - vErrors.push(err111); + vErrors.push(err113); } errors++; } } } else { - const err112 = { instancePath: instancePath + "/profiles", schemaPath: "#/properties/profiles/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err114 = { instancePath: instancePath + "/profiles", schemaPath: "#/properties/profiles/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err112]; + vErrors = [err114]; } else { - vErrors.push(err112); + vErrors.push(err114); } errors++; } } if (data.services !== void 0) { - let data44 = data.services; - if (data44 && typeof data44 == "object" && !Array.isArray(data44)) { - for (const key22 in data44) { - let data45 = data44[key22]; - if (data45 && typeof data45 == "object" && !Array.isArray(data45)) { - if (data45.image === void 0) { - const err113 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/services/additionalProperties/required", keyword: "required", params: { missingProperty: "image" }, message: "must have required property 'image'" }; + let data46 = data.services; + if (data46 && typeof data46 == "object" && !Array.isArray(data46)) { + for (const key22 in data46) { + let data47 = data46[key22]; + if (data47 && typeof data47 == "object" && !Array.isArray(data47)) { + if (data47.image === void 0) { + const err115 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/services/additionalProperties/required", keyword: "required", params: { missingProperty: "image" }, message: "must have required property 'image'" }; if (vErrors === null) { - vErrors = [err113]; + vErrors = [err115]; } else { - vErrors.push(err113); + vErrors.push(err115); } errors++; } - for (const key23 in data45) { + for (const key23 in data47) { if (!(key23 === "args" || key23 === "command" || key23 === "credentials" || key23 === "dependencies" || key23 === "env" || key23 === "expose" || key23 === "image" || key23 === "params")) { - const err114 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/services/additionalProperties/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key23 }, message: "must NOT have additional properties" }; + const err116 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/services/additionalProperties/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key23 }, message: "must NOT have additional properties" }; if (vErrors === null) { - vErrors = [err114]; + vErrors = [err116]; } else { - vErrors.push(err114); + vErrors.push(err116); } errors++; } } - if (data45.args !== void 0) { - let data46 = data45.args; - const _errs161 = errors; + if (data47.args !== void 0) { + let data48 = data47.args; + const _errs165 = errors; let valid52 = false; let passing7 = null; - const _errs162 = errors; - if (Array.isArray(data46)) { - const len5 = data46.length; + const _errs166 = errors; + if (Array.isArray(data48)) { + const len5 = data48.length; for (let i5 = 0; i5 < len5; i5++) { - if (typeof data46[i5] !== "string") { - const err115 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/args/" + i5, schemaPath: "#/definitions/stringArrayOrNull/oneOf/0/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + if (typeof data48[i5] !== "string") { + const err117 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/args/" + i5, schemaPath: "#/definitions/stringArrayOrNull/oneOf/0/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err115]; + vErrors = [err117]; } else { - vErrors.push(err115); + vErrors.push(err117); } errors++; } } } else { - const err116 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/args", schemaPath: "#/definitions/stringArrayOrNull/oneOf/0/type", keyword: "type", params: { type: "array" }, message: "must be array" }; + const err118 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/args", schemaPath: "#/definitions/stringArrayOrNull/oneOf/0/type", keyword: "type", params: { type: "array" }, message: "must be array" }; if (vErrors === null) { - vErrors = [err116]; + vErrors = [err118]; } else { - vErrors.push(err116); + vErrors.push(err118); } errors++; } - var _valid9 = _errs162 === errors; + var _valid9 = _errs166 === errors; if (_valid9) { valid52 = true; passing7 = 0; } - const _errs166 = errors; - if (data46 !== null) { - const err117 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/args", schemaPath: "#/definitions/stringArrayOrNull/oneOf/1/type", keyword: "type", params: { type: "null" }, message: "must be null" }; + const _errs170 = errors; + if (data48 !== null) { + const err119 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/args", schemaPath: "#/definitions/stringArrayOrNull/oneOf/1/type", keyword: "type", params: { type: "null" }, message: "must be null" }; if (vErrors === null) { - vErrors = [err117]; + vErrors = [err119]; } else { - vErrors.push(err117); + vErrors.push(err119); } errors++; } - var _valid9 = _errs166 === errors; + var _valid9 = _errs170 === errors; if (_valid9 && valid52) { valid52 = false; passing7 = [passing7, 1]; @@ -2261,68 +2303,68 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r } } if (!valid52) { - const err118 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/args", schemaPath: "#/definitions/stringArrayOrNull/oneOf", keyword: "oneOf", params: { passingSchemas: passing7 }, message: "must match exactly one schema in oneOf" }; + const err120 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/args", schemaPath: "#/definitions/stringArrayOrNull/oneOf", keyword: "oneOf", params: { passingSchemas: passing7 }, message: "must match exactly one schema in oneOf" }; if (vErrors === null) { - vErrors = [err118]; + vErrors = [err120]; } else { - vErrors.push(err118); + vErrors.push(err120); } errors++; } else { - errors = _errs161; + errors = _errs165; if (vErrors !== null) { - if (_errs161) { - vErrors.length = _errs161; + if (_errs165) { + vErrors.length = _errs165; } else { vErrors = null; } } } } - if (data45.command !== void 0) { - let data48 = data45.command; - const _errs170 = errors; + if (data47.command !== void 0) { + let data50 = data47.command; + const _errs174 = errors; let valid56 = false; let passing8 = null; - const _errs171 = errors; - if (Array.isArray(data48)) { - const len6 = data48.length; + const _errs175 = errors; + if (Array.isArray(data50)) { + const len6 = data50.length; for (let i6 = 0; i6 < len6; i6++) { - if (typeof data48[i6] !== "string") { - const err119 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/command/" + i6, schemaPath: "#/definitions/stringArrayOrNull/oneOf/0/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + if (typeof data50[i6] !== "string") { + const err121 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/command/" + i6, schemaPath: "#/definitions/stringArrayOrNull/oneOf/0/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err119]; + vErrors = [err121]; } else { - vErrors.push(err119); + vErrors.push(err121); } errors++; } } } else { - const err120 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/command", schemaPath: "#/definitions/stringArrayOrNull/oneOf/0/type", keyword: "type", params: { type: "array" }, message: "must be array" }; + const err122 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/command", schemaPath: "#/definitions/stringArrayOrNull/oneOf/0/type", keyword: "type", params: { type: "array" }, message: "must be array" }; if (vErrors === null) { - vErrors = [err120]; + vErrors = [err122]; } else { - vErrors.push(err120); + vErrors.push(err122); } errors++; } - var _valid10 = _errs171 === errors; + var _valid10 = _errs175 === errors; if (_valid10) { valid56 = true; passing8 = 0; } - const _errs175 = errors; - if (data48 !== null) { - const err121 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/command", schemaPath: "#/definitions/stringArrayOrNull/oneOf/1/type", keyword: "type", params: { type: "null" }, message: "must be null" }; + const _errs179 = errors; + if (data50 !== null) { + const err123 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/command", schemaPath: "#/definitions/stringArrayOrNull/oneOf/1/type", keyword: "type", params: { type: "null" }, message: "must be null" }; if (vErrors === null) { - vErrors = [err121]; + vErrors = [err123]; } else { - vErrors.push(err121); + vErrors.push(err123); } errors++; } - var _valid10 = _errs175 === errors; + var _valid10 = _errs179 === errors; if (_valid10 && valid56) { valid56 = false; passing8 = [passing8, 1]; @@ -2333,79 +2375,57 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r } } if (!valid56) { - const err122 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/command", schemaPath: "#/definitions/stringArrayOrNull/oneOf", keyword: "oneOf", params: { passingSchemas: passing8 }, message: "must match exactly one schema in oneOf" }; + const err124 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/command", schemaPath: "#/definitions/stringArrayOrNull/oneOf", keyword: "oneOf", params: { passingSchemas: passing8 }, message: "must match exactly one schema in oneOf" }; if (vErrors === null) { - vErrors = [err122]; + vErrors = [err124]; } else { - vErrors.push(err122); + vErrors.push(err124); } errors++; } else { - errors = _errs170; + errors = _errs174; if (vErrors !== null) { - if (_errs170) { - vErrors.length = _errs170; + if (_errs174) { + vErrors.length = _errs174; } else { vErrors = null; } } } } - if (data45.credentials !== void 0) { - let data50 = data45.credentials; - if (data50 && typeof data50 == "object" && !Array.isArray(data50)) { - if (data50.host === void 0) { - const err123 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials", schemaPath: "#/properties/services/additionalProperties/properties/credentials/required", keyword: "required", params: { missingProperty: "host" }, message: "must have required property 'host'" }; + if (data47.credentials !== void 0) { + let data52 = data47.credentials; + if (data52 && typeof data52 == "object" && !Array.isArray(data52)) { + if (data52.host === void 0) { + const err125 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials", schemaPath: "#/properties/services/additionalProperties/properties/credentials/required", keyword: "required", params: { missingProperty: "host" }, message: "must have required property 'host'" }; if (vErrors === null) { - vErrors = [err123]; + vErrors = [err125]; } else { - vErrors.push(err123); + vErrors.push(err125); } errors++; } - if (data50.username === void 0) { - const err124 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials", schemaPath: "#/properties/services/additionalProperties/properties/credentials/required", keyword: "required", params: { missingProperty: "username" }, message: "must have required property 'username'" }; + if (data52.username === void 0) { + const err126 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials", schemaPath: "#/properties/services/additionalProperties/properties/credentials/required", keyword: "required", params: { missingProperty: "username" }, message: "must have required property 'username'" }; if (vErrors === null) { - vErrors = [err124]; + vErrors = [err126]; } else { - vErrors.push(err124); + vErrors.push(err126); } errors++; } - if (data50.password === void 0) { - const err125 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials", schemaPath: "#/properties/services/additionalProperties/properties/credentials/required", keyword: "required", params: { missingProperty: "password" }, message: "must have required property 'password'" }; + if (data52.password === void 0) { + const err127 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials", schemaPath: "#/properties/services/additionalProperties/properties/credentials/required", keyword: "required", params: { missingProperty: "password" }, message: "must have required property 'password'" }; if (vErrors === null) { - vErrors = [err125]; + vErrors = [err127]; } else { - vErrors.push(err125); + vErrors.push(err127); } errors++; } - for (const key24 in data50) { + for (const key24 in data52) { if (!(key24 === "email" || key24 === "host" || key24 === "password" || key24 === "username")) { - const err126 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials", schemaPath: "#/properties/services/additionalProperties/properties/credentials/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key24 }, message: "must NOT have additional properties" }; - if (vErrors === null) { - vErrors = [err126]; - } else { - vErrors.push(err126); - } - errors++; - } - } - if (data50.email !== void 0) { - let data51 = data50.email; - if (typeof data51 === "string") { - if (func2(data51) < 5) { - const err127 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/email", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/email/minLength", keyword: "minLength", params: { limit: 5 }, message: "must NOT have fewer than 5 characters" }; - if (vErrors === null) { - vErrors = [err127]; - } else { - vErrors.push(err127); - } - errors++; - } - } else { - const err128 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/email", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/email/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + const err128 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials", schemaPath: "#/properties/services/additionalProperties/properties/credentials/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key24 }, message: "must NOT have additional properties" }; if (vErrors === null) { vErrors = [err128]; } else { @@ -2414,11 +2434,11 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r errors++; } } - if (data50.host !== void 0) { - let data52 = data50.host; - if (typeof data52 === "string") { - if (func2(data52) < 1) { - const err129 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/host", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/host/minLength", keyword: "minLength", params: { limit: 1 }, message: "must NOT have fewer than 1 characters" }; + if (data52.email !== void 0) { + let data53 = data52.email; + if (typeof data53 === "string") { + if (func2(data53) < 5) { + const err129 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/email", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/email/minLength", keyword: "minLength", params: { limit: 5 }, message: "must NOT have fewer than 5 characters" }; if (vErrors === null) { vErrors = [err129]; } else { @@ -2427,7 +2447,7 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r errors++; } } else { - const err130 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/host", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/host/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + const err130 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/email", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/email/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { vErrors = [err130]; } else { @@ -2436,11 +2456,11 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r errors++; } } - if (data50.password !== void 0) { - let data53 = data50.password; - if (typeof data53 === "string") { - if (func2(data53) < 6) { - const err131 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/password", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/password/minLength", keyword: "minLength", params: { limit: 6 }, message: "must NOT have fewer than 6 characters" }; + if (data52.host !== void 0) { + let data54 = data52.host; + if (typeof data54 === "string") { + if (func2(data54) < 1) { + const err131 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/host", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/host/minLength", keyword: "minLength", params: { limit: 1 }, message: "must NOT have fewer than 1 characters" }; if (vErrors === null) { vErrors = [err131]; } else { @@ -2449,7 +2469,7 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r errors++; } } else { - const err132 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/password", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/password/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + const err132 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/host", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/host/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { vErrors = [err132]; } else { @@ -2458,11 +2478,11 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r errors++; } } - if (data50.username !== void 0) { - let data54 = data50.username; - if (typeof data54 === "string") { - if (func2(data54) < 1) { - const err133 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/username", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/username/minLength", keyword: "minLength", params: { limit: 1 }, message: "must NOT have fewer than 1 characters" }; + if (data52.password !== void 0) { + let data55 = data52.password; + if (typeof data55 === "string") { + if (func2(data55) < 6) { + const err133 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/password", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/password/minLength", keyword: "minLength", params: { limit: 6 }, message: "must NOT have fewer than 6 characters" }; if (vErrors === null) { vErrors = [err133]; } else { @@ -2471,7 +2491,7 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r errors++; } } else { - const err134 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/username", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/username/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + const err134 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/password", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/password/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { vErrors = [err134]; } else { @@ -2480,109 +2500,131 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r errors++; } } + if (data52.username !== void 0) { + let data56 = data52.username; + if (typeof data56 === "string") { + if (func2(data56) < 1) { + const err135 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/username", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/username/minLength", keyword: "minLength", params: { limit: 1 }, message: "must NOT have fewer than 1 characters" }; + if (vErrors === null) { + vErrors = [err135]; + } else { + vErrors.push(err135); + } + errors++; + } + } else { + const err136 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials/username", schemaPath: "#/properties/services/additionalProperties/properties/credentials/properties/username/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + if (vErrors === null) { + vErrors = [err136]; + } else { + vErrors.push(err136); + } + errors++; + } + } } else { - const err135 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials", schemaPath: "#/properties/services/additionalProperties/properties/credentials/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err137 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/credentials", schemaPath: "#/properties/services/additionalProperties/properties/credentials/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err135]; + vErrors = [err137]; } else { - vErrors.push(err135); + vErrors.push(err137); } errors++; } } - if (data45.dependencies !== void 0) { - let data55 = data45.dependencies; - if (Array.isArray(data55)) { - const len7 = data55.length; + if (data47.dependencies !== void 0) { + let data57 = data47.dependencies; + if (Array.isArray(data57)) { + const len7 = data57.length; for (let i7 = 0; i7 < len7; i7++) { - let data56 = data55[i7]; - if (data56 && typeof data56 == "object" && !Array.isArray(data56)) { - for (const key25 in data56) { + let data58 = data57[i7]; + if (data58 && typeof data58 == "object" && !Array.isArray(data58)) { + for (const key25 in data58) { if (!(key25 === "service")) { - const err136 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/dependencies/" + i7, schemaPath: "#/properties/services/additionalProperties/properties/dependencies/items/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key25 }, message: "must NOT have additional properties" }; + const err138 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/dependencies/" + i7, schemaPath: "#/properties/services/additionalProperties/properties/dependencies/items/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key25 }, message: "must NOT have additional properties" }; if (vErrors === null) { - vErrors = [err136]; + vErrors = [err138]; } else { - vErrors.push(err136); + vErrors.push(err138); } errors++; } } - if (data56.service !== void 0) { - if (typeof data56.service !== "string") { - const err137 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/dependencies/" + i7 + "/service", schemaPath: "#/properties/services/additionalProperties/properties/dependencies/items/properties/service/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + if (data58.service !== void 0) { + if (typeof data58.service !== "string") { + const err139 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/dependencies/" + i7 + "/service", schemaPath: "#/properties/services/additionalProperties/properties/dependencies/items/properties/service/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err137]; + vErrors = [err139]; } else { - vErrors.push(err137); + vErrors.push(err139); } errors++; } } } else { - const err138 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/dependencies/" + i7, schemaPath: "#/properties/services/additionalProperties/properties/dependencies/items/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err140 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/dependencies/" + i7, schemaPath: "#/properties/services/additionalProperties/properties/dependencies/items/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err138]; + vErrors = [err140]; } else { - vErrors.push(err138); + vErrors.push(err140); } errors++; } } } else { - const err139 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/dependencies", schemaPath: "#/properties/services/additionalProperties/properties/dependencies/type", keyword: "type", params: { type: "array" }, message: "must be array" }; + const err141 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/dependencies", schemaPath: "#/properties/services/additionalProperties/properties/dependencies/type", keyword: "type", params: { type: "array" }, message: "must be array" }; if (vErrors === null) { - vErrors = [err139]; + vErrors = [err141]; } else { - vErrors.push(err139); + vErrors.push(err141); } errors++; } } - if (data45.env !== void 0) { - let data58 = data45.env; - const _errs197 = errors; + if (data47.env !== void 0) { + let data60 = data47.env; + const _errs201 = errors; let valid64 = false; let passing9 = null; - const _errs198 = errors; - if (Array.isArray(data58)) { - const len8 = data58.length; + const _errs202 = errors; + if (Array.isArray(data60)) { + const len8 = data60.length; for (let i8 = 0; i8 < len8; i8++) { - if (typeof data58[i8] !== "string") { - const err140 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/env/" + i8, schemaPath: "#/definitions/stringArrayOrNull/oneOf/0/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + if (typeof data60[i8] !== "string") { + const err142 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/env/" + i8, schemaPath: "#/definitions/stringArrayOrNull/oneOf/0/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err140]; + vErrors = [err142]; } else { - vErrors.push(err140); + vErrors.push(err142); } errors++; } } } else { - const err141 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/env", schemaPath: "#/definitions/stringArrayOrNull/oneOf/0/type", keyword: "type", params: { type: "array" }, message: "must be array" }; + const err143 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/env", schemaPath: "#/definitions/stringArrayOrNull/oneOf/0/type", keyword: "type", params: { type: "array" }, message: "must be array" }; if (vErrors === null) { - vErrors = [err141]; + vErrors = [err143]; } else { - vErrors.push(err141); + vErrors.push(err143); } errors++; } - var _valid11 = _errs198 === errors; + var _valid11 = _errs202 === errors; if (_valid11) { valid64 = true; passing9 = 0; } - const _errs202 = errors; - if (data58 !== null) { - const err142 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/env", schemaPath: "#/definitions/stringArrayOrNull/oneOf/1/type", keyword: "type", params: { type: "null" }, message: "must be null" }; + const _errs206 = errors; + if (data60 !== null) { + const err144 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/env", schemaPath: "#/definitions/stringArrayOrNull/oneOf/1/type", keyword: "type", params: { type: "null" }, message: "must be null" }; if (vErrors === null) { - vErrors = [err142]; + vErrors = [err144]; } else { - vErrors.push(err142); + vErrors.push(err144); } errors++; } - var _valid11 = _errs202 === errors; + var _valid11 = _errs206 === errors; if (_valid11 && valid64) { valid64 = false; passing9 = [passing9, 1]; @@ -2593,348 +2635,348 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r } } if (!valid64) { - const err143 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/env", schemaPath: "#/definitions/stringArrayOrNull/oneOf", keyword: "oneOf", params: { passingSchemas: passing9 }, message: "must match exactly one schema in oneOf" }; + const err145 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/env", schemaPath: "#/definitions/stringArrayOrNull/oneOf", keyword: "oneOf", params: { passingSchemas: passing9 }, message: "must match exactly one schema in oneOf" }; if (vErrors === null) { - vErrors = [err143]; + vErrors = [err145]; } else { - vErrors.push(err143); + vErrors.push(err145); } errors++; } else { - errors = _errs197; + errors = _errs201; if (vErrors !== null) { - if (_errs197) { - vErrors.length = _errs197; + if (_errs201) { + vErrors.length = _errs201; } else { vErrors = null; } } } } - if (data45.expose !== void 0) { - let data60 = data45.expose; - const _errs205 = errors; + if (data47.expose !== void 0) { + let data62 = data47.expose; + const _errs209 = errors; let valid67 = false; let passing10 = null; - const _errs206 = errors; - if (Array.isArray(data60)) { - const len9 = data60.length; + const _errs210 = errors; + if (Array.isArray(data62)) { + const len9 = data62.length; for (let i9 = 0; i9 < len9; i9++) { - let data61 = data60[i9]; - if (data61 && typeof data61 == "object" && !Array.isArray(data61)) { - if (data61.port === void 0) { - const err144 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/required", keyword: "required", params: { missingProperty: "port" }, message: "must have required property 'port'" }; + let data63 = data62[i9]; + if (data63 && typeof data63 == "object" && !Array.isArray(data63)) { + if (data63.port === void 0) { + const err146 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/required", keyword: "required", params: { missingProperty: "port" }, message: "must have required property 'port'" }; if (vErrors === null) { - vErrors = [err144]; + vErrors = [err146]; } else { - vErrors.push(err144); + vErrors.push(err146); } errors++; } - for (const key26 in data61) { + for (const key26 in data63) { if (!(key26 === "accept" || key26 === "as" || key26 === "http_options" || key26 === "port" || key26 === "proto" || key26 === "to")) { - const err145 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key26 }, message: "must NOT have additional properties" }; + const err147 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key26 }, message: "must NOT have additional properties" }; if (vErrors === null) { - vErrors = [err145]; + vErrors = [err147]; } else { - vErrors.push(err145); + vErrors.push(err147); } errors++; } } - if (data61.accept !== void 0) { - let data62 = data61.accept; - if (Array.isArray(data62)) { - const len10 = data62.length; + if (data63.accept !== void 0) { + let data64 = data63.accept; + if (Array.isArray(data64)) { + const len10 = data64.length; for (let i10 = 0; i10 < len10; i10++) { - if (typeof data62[i10] !== "string") { - const err146 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/accept/" + i10, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/accept/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + if (typeof data64[i10] !== "string") { + const err148 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/accept/" + i10, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/accept/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err146]; + vErrors = [err148]; } else { - vErrors.push(err146); + vErrors.push(err148); } errors++; } } } else { - const err147 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/accept", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/accept/type", keyword: "type", params: { type: "array" }, message: "must be array" }; + const err149 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/accept", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/accept/type", keyword: "type", params: { type: "array" }, message: "must be array" }; if (vErrors === null) { - vErrors = [err147]; + vErrors = [err149]; } else { - vErrors.push(err147); + vErrors.push(err149); } errors++; } } - if (data61.as !== void 0) { - let data64 = data61.as; - if (!(typeof data64 == "number" && (!(data64 % 1) && !isNaN(data64)))) { - const err148 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/as", schemaPath: "#/definitions/portNumber/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; + if (data63.as !== void 0) { + let data66 = data63.as; + if (!(typeof data66 == "number" && (!(data66 % 1) && !isNaN(data66)))) { + const err150 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/as", schemaPath: "#/definitions/portNumber/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; if (vErrors === null) { - vErrors = [err148]; + vErrors = [err150]; } else { - vErrors.push(err148); + vErrors.push(err150); } errors++; } - if (typeof data64 == "number") { - if (data64 > 65535 || isNaN(data64)) { - const err149 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/as", schemaPath: "#/definitions/portNumber/maximum", keyword: "maximum", params: { comparison: "<=", limit: 65535 }, message: "must be <= 65535" }; + if (typeof data66 == "number") { + if (data66 > 65535 || isNaN(data66)) { + const err151 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/as", schemaPath: "#/definitions/portNumber/maximum", keyword: "maximum", params: { comparison: "<=", limit: 65535 }, message: "must be <= 65535" }; if (vErrors === null) { - vErrors = [err149]; + vErrors = [err151]; } else { - vErrors.push(err149); + vErrors.push(err151); } errors++; } - if (data64 < 1 || isNaN(data64)) { - const err150 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/as", schemaPath: "#/definitions/portNumber/minimum", keyword: "minimum", params: { comparison: ">=", limit: 1 }, message: "must be >= 1" }; + if (data66 < 1 || isNaN(data66)) { + const err152 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/as", schemaPath: "#/definitions/portNumber/minimum", keyword: "minimum", params: { comparison: ">=", limit: 1 }, message: "must be >= 1" }; if (vErrors === null) { - vErrors = [err150]; + vErrors = [err152]; } else { - vErrors.push(err150); + vErrors.push(err152); } errors++; } } } - if (data61.http_options !== void 0) { - let data65 = data61.http_options; - if (data65 && typeof data65 == "object" && !Array.isArray(data65)) { - for (const key27 in data65) { + if (data63.http_options !== void 0) { + let data67 = data63.http_options; + if (data67 && typeof data67 == "object" && !Array.isArray(data67)) { + for (const key27 in data67) { if (!(key27 === "max_body_size" || key27 === "next_cases" || key27 === "next_timeout" || key27 === "next_tries" || key27 === "read_timeout" || key27 === "send_timeout")) { - const err151 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key27 }, message: "must NOT have additional properties" }; + const err153 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key27 }, message: "must NOT have additional properties" }; if (vErrors === null) { - vErrors = [err151]; + vErrors = [err153]; } else { - vErrors.push(err151); + vErrors.push(err153); } errors++; } } - if (data65.max_body_size !== void 0) { - let data66 = data65.max_body_size; - if (!(typeof data66 == "number" && (!(data66 % 1) && !isNaN(data66)))) { - const err152 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/max_body_size", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/max_body_size/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; + if (data67.max_body_size !== void 0) { + let data68 = data67.max_body_size; + if (!(typeof data68 == "number" && (!(data68 % 1) && !isNaN(data68)))) { + const err154 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/max_body_size", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/max_body_size/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; if (vErrors === null) { - vErrors = [err152]; + vErrors = [err154]; } else { - vErrors.push(err152); + vErrors.push(err154); } errors++; } - if (typeof data66 == "number") { - if (data66 > 104857600 || isNaN(data66)) { - const err153 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/max_body_size", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/max_body_size/maximum", keyword: "maximum", params: { comparison: "<=", limit: 104857600 }, message: "must be <= 104857600" }; + if (typeof data68 == "number") { + if (data68 > 104857600 || isNaN(data68)) { + const err155 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/max_body_size", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/max_body_size/maximum", keyword: "maximum", params: { comparison: "<=", limit: 104857600 }, message: "must be <= 104857600" }; if (vErrors === null) { - vErrors = [err153]; + vErrors = [err155]; } else { - vErrors.push(err153); + vErrors.push(err155); } errors++; } - if (data66 < 0 || isNaN(data66)) { - const err154 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/max_body_size", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/max_body_size/minimum", keyword: "minimum", params: { comparison: ">=", limit: 0 }, message: "must be >= 0" }; + if (data68 < 0 || isNaN(data68)) { + const err156 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/max_body_size", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/max_body_size/minimum", keyword: "minimum", params: { comparison: ">=", limit: 0 }, message: "must be >= 0" }; if (vErrors === null) { - vErrors = [err154]; + vErrors = [err156]; } else { - vErrors.push(err154); + vErrors.push(err156); } errors++; } } } - if (data65.next_cases !== void 0) { - let data67 = data65.next_cases; - const _errs224 = errors; + if (data67.next_cases !== void 0) { + let data69 = data67.next_cases; + const _errs228 = errors; let valid75 = false; let passing11 = null; - const _errs225 = errors; - if (Array.isArray(data67)) { - if (data67.length > 1) { - const err155 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/0/maxItems", keyword: "maxItems", params: { limit: 1 }, message: "must NOT have more than 1 items" }; + const _errs229 = errors; + if (Array.isArray(data69)) { + if (data69.length > 1) { + const err157 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/0/maxItems", keyword: "maxItems", params: { limit: 1 }, message: "must NOT have more than 1 items" }; if (vErrors === null) { - vErrors = [err155]; + vErrors = [err157]; } else { - vErrors.push(err155); + vErrors.push(err157); } errors++; } - if (data67.length < 1) { - const err156 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/0/minItems", keyword: "minItems", params: { limit: 1 }, message: "must NOT have fewer than 1 items" }; + if (data69.length < 1) { + const err158 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/0/minItems", keyword: "minItems", params: { limit: 1 }, message: "must NOT have fewer than 1 items" }; if (vErrors === null) { - vErrors = [err156]; + vErrors = [err158]; } else { - vErrors.push(err156); + vErrors.push(err158); } errors++; } - const len11 = data67.length; + const len11 = data69.length; for (let i11 = 0; i11 < len11; i11++) { - let data68 = data67[i11]; - if (typeof data68 !== "string") { - const err157 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases/" + i11, schemaPath: "#/definitions/httpErrorCode/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + let data70 = data69[i11]; + if (typeof data70 !== "string") { + const err159 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases/" + i11, schemaPath: "#/definitions/httpErrorCode/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err157]; + vErrors = [err159]; } else { - vErrors.push(err157); + vErrors.push(err159); } errors++; } - if (!(data68 === "error" || data68 === "timeout" || data68 === "500" || data68 === "502" || data68 === "503" || data68 === "504" || data68 === "403" || data68 === "404" || data68 === "429" || data68 === "off")) { - const err158 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases/" + i11, schemaPath: "#/definitions/httpErrorCode/enum", keyword: "enum", params: { allowedValues: schema40.enum }, message: "must be equal to one of the allowed values" }; + if (!(data70 === "error" || data70 === "timeout" || data70 === "500" || data70 === "502" || data70 === "503" || data70 === "504" || data70 === "403" || data70 === "404" || data70 === "429" || data70 === "off")) { + const err160 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases/" + i11, schemaPath: "#/definitions/httpErrorCode/enum", keyword: "enum", params: { allowedValues: schema40.enum }, message: "must be equal to one of the allowed values" }; if (vErrors === null) { - vErrors = [err158]; + vErrors = [err160]; } else { - vErrors.push(err158); + vErrors.push(err160); } errors++; } } - const _errs230 = errors; - const len12 = data67.length; + const _errs234 = errors; + const len12 = data69.length; for (let i12 = 0; i12 < len12; i12++) { - const _errs231 = errors; - if ("off" !== data67[i12]) { - const err159 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases/" + i12, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/0/contains/const", keyword: "const", params: { allowedValue: "off" }, message: "must be equal to constant" }; + const _errs235 = errors; + if ("off" !== data69[i12]) { + const err161 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases/" + i12, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/0/contains/const", keyword: "const", params: { allowedValue: "off" }, message: "must be equal to constant" }; if (vErrors === null) { - vErrors = [err159]; + vErrors = [err161]; } else { - vErrors.push(err159); + vErrors.push(err161); } errors++; } - var valid79 = _errs231 === errors; + var valid79 = _errs235 === errors; if (valid79) { break; } } if (!valid79) { - const err160 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/0/contains", keyword: "contains", params: { minContains: 1 }, message: "must contain at least 1 valid item(s)" }; + const err162 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/0/contains", keyword: "contains", params: { minContains: 1 }, message: "must contain at least 1 valid item(s)" }; if (vErrors === null) { - vErrors = [err160]; + vErrors = [err162]; } else { - vErrors.push(err160); + vErrors.push(err162); } errors++; } else { - errors = _errs230; + errors = _errs234; if (vErrors !== null) { - if (_errs230) { - vErrors.length = _errs230; + if (_errs234) { + vErrors.length = _errs234; } else { vErrors = null; } } } } else { - const err161 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/0/type", keyword: "type", params: { type: "array" }, message: "must be array" }; + const err163 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/0/type", keyword: "type", params: { type: "array" }, message: "must be array" }; if (vErrors === null) { - vErrors = [err161]; + vErrors = [err163]; } else { - vErrors.push(err161); + vErrors.push(err163); } errors++; } - var _valid13 = _errs225 === errors; + var _valid13 = _errs229 === errors; if (_valid13) { valid75 = true; passing11 = 0; } - const _errs232 = errors; - const _errs234 = errors; - const _errs235 = errors; - if (Array.isArray(data67)) { - const _errs236 = errors; - const len13 = data67.length; + const _errs236 = errors; + const _errs238 = errors; + const _errs239 = errors; + if (Array.isArray(data69)) { + const _errs240 = errors; + const len13 = data69.length; for (let i13 = 0; i13 < len13; i13++) { - const _errs237 = errors; - if ("off" !== data67[i13]) { - const err162 = {}; + const _errs241 = errors; + if ("off" !== data69[i13]) { + const err164 = {}; if (vErrors === null) { - vErrors = [err162]; + vErrors = [err164]; } else { - vErrors.push(err162); + vErrors.push(err164); } errors++; } - var valid81 = _errs237 === errors; + var valid81 = _errs241 === errors; if (valid81) { break; } } if (!valid81) { - const err163 = {}; + const err165 = {}; if (vErrors === null) { - vErrors = [err163]; + vErrors = [err165]; } else { - vErrors.push(err163); + vErrors.push(err165); } errors++; } else { - errors = _errs236; + errors = _errs240; if (vErrors !== null) { - if (_errs236) { - vErrors.length = _errs236; + if (_errs240) { + vErrors.length = _errs240; } else { vErrors = null; } } } } - var valid80 = _errs235 === errors; + var valid80 = _errs239 === errors; if (valid80) { - const err164 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/1/not", keyword: "not", params: {}, message: "must NOT be valid" }; + const err166 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/1/not", keyword: "not", params: {}, message: "must NOT be valid" }; if (vErrors === null) { - vErrors = [err164]; + vErrors = [err166]; } else { - vErrors.push(err164); + vErrors.push(err166); } errors++; } else { - errors = _errs234; + errors = _errs238; if (vErrors !== null) { - if (_errs234) { - vErrors.length = _errs234; + if (_errs238) { + vErrors.length = _errs238; } else { vErrors = null; } } } - if (Array.isArray(data67)) { - const len14 = data67.length; + if (Array.isArray(data69)) { + const len14 = data69.length; for (let i14 = 0; i14 < len14; i14++) { - let data71 = data67[i14]; - if (typeof data71 !== "string") { - const err165 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases/" + i14, schemaPath: "#/definitions/httpErrorCode/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + let data73 = data69[i14]; + if (typeof data73 !== "string") { + const err167 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases/" + i14, schemaPath: "#/definitions/httpErrorCode/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err165]; + vErrors = [err167]; } else { - vErrors.push(err165); + vErrors.push(err167); } errors++; } - if (!(data71 === "error" || data71 === "timeout" || data71 === "500" || data71 === "502" || data71 === "503" || data71 === "504" || data71 === "403" || data71 === "404" || data71 === "429" || data71 === "off")) { - const err166 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases/" + i14, schemaPath: "#/definitions/httpErrorCode/enum", keyword: "enum", params: { allowedValues: schema40.enum }, message: "must be equal to one of the allowed values" }; + if (!(data73 === "error" || data73 === "timeout" || data73 === "500" || data73 === "502" || data73 === "503" || data73 === "504" || data73 === "403" || data73 === "404" || data73 === "429" || data73 === "off")) { + const err168 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases/" + i14, schemaPath: "#/definitions/httpErrorCode/enum", keyword: "enum", params: { allowedValues: schema40.enum }, message: "must be equal to one of the allowed values" }; if (vErrors === null) { - vErrors = [err166]; + vErrors = [err168]; } else { - vErrors.push(err166); + vErrors.push(err168); } errors++; } } } else { - const err167 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/1/type", keyword: "type", params: { type: "array" }, message: "must be array" }; + const err169 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf/1/type", keyword: "type", params: { type: "array" }, message: "must be array" }; if (vErrors === null) { - vErrors = [err167]; + vErrors = [err169]; } else { - vErrors.push(err167); + vErrors.push(err169); } errors++; } - var _valid13 = _errs232 === errors; + var _valid13 = _errs236 === errors; if (_valid13 && valid75) { valid75 = false; passing11 = [passing11, 1]; @@ -2945,237 +2987,237 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r } } if (!valid75) { - const err168 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf", keyword: "oneOf", params: { passingSchemas: passing11 }, message: "must match exactly one schema in oneOf" }; + const err170 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_cases", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_cases/oneOf", keyword: "oneOf", params: { passingSchemas: passing11 }, message: "must match exactly one schema in oneOf" }; if (vErrors === null) { - vErrors = [err168]; + vErrors = [err170]; } else { - vErrors.push(err168); + vErrors.push(err170); } errors++; } else { - errors = _errs224; + errors = _errs228; if (vErrors !== null) { - if (_errs224) { - vErrors.length = _errs224; + if (_errs228) { + vErrors.length = _errs228; } else { vErrors = null; } } } } - if (data65.next_timeout !== void 0) { - let data72 = data65.next_timeout; - if (!(typeof data72 == "number" && (!(data72 % 1) && !isNaN(data72)))) { - const err169 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_timeout/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; + if (data67.next_timeout !== void 0) { + let data74 = data67.next_timeout; + if (!(typeof data74 == "number" && (!(data74 % 1) && !isNaN(data74)))) { + const err171 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_timeout/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; if (vErrors === null) { - vErrors = [err169]; + vErrors = [err171]; } else { - vErrors.push(err169); + vErrors.push(err171); } errors++; } - if (typeof data72 == "number") { - if (data72 < 0 || isNaN(data72)) { - const err170 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_timeout/minimum", keyword: "minimum", params: { comparison: ">=", limit: 0 }, message: "must be >= 0" }; + if (typeof data74 == "number") { + if (data74 < 0 || isNaN(data74)) { + const err172 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_timeout/minimum", keyword: "minimum", params: { comparison: ">=", limit: 0 }, message: "must be >= 0" }; if (vErrors === null) { - vErrors = [err170]; + vErrors = [err172]; } else { - vErrors.push(err170); + vErrors.push(err172); } errors++; } } } - if (data65.next_tries !== void 0) { - let data73 = data65.next_tries; - if (!(typeof data73 == "number" && (!(data73 % 1) && !isNaN(data73)))) { - const err171 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_tries", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_tries/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; + if (data67.next_tries !== void 0) { + let data75 = data67.next_tries; + if (!(typeof data75 == "number" && (!(data75 % 1) && !isNaN(data75)))) { + const err173 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_tries", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_tries/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; if (vErrors === null) { - vErrors = [err171]; + vErrors = [err173]; } else { - vErrors.push(err171); + vErrors.push(err173); } errors++; } - if (typeof data73 == "number") { - if (data73 < 0 || isNaN(data73)) { - const err172 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_tries", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_tries/minimum", keyword: "minimum", params: { comparison: ">=", limit: 0 }, message: "must be >= 0" }; + if (typeof data75 == "number") { + if (data75 < 0 || isNaN(data75)) { + const err174 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/next_tries", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/next_tries/minimum", keyword: "minimum", params: { comparison: ">=", limit: 0 }, message: "must be >= 0" }; if (vErrors === null) { - vErrors = [err172]; + vErrors = [err174]; } else { - vErrors.push(err172); + vErrors.push(err174); } errors++; } } } - if (data65.read_timeout !== void 0) { - let data74 = data65.read_timeout; - if (!(typeof data74 == "number" && (!(data74 % 1) && !isNaN(data74)))) { - const err173 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/read_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/read_timeout/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; + if (data67.read_timeout !== void 0) { + let data76 = data67.read_timeout; + if (!(typeof data76 == "number" && (!(data76 % 1) && !isNaN(data76)))) { + const err175 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/read_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/read_timeout/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; if (vErrors === null) { - vErrors = [err173]; + vErrors = [err175]; } else { - vErrors.push(err173); + vErrors.push(err175); } errors++; } - if (typeof data74 == "number") { - if (data74 > 6e4 || isNaN(data74)) { - const err174 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/read_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/read_timeout/maximum", keyword: "maximum", params: { comparison: "<=", limit: 6e4 }, message: "must be <= 60000" }; + if (typeof data76 == "number") { + if (data76 > 6e4 || isNaN(data76)) { + const err176 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/read_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/read_timeout/maximum", keyword: "maximum", params: { comparison: "<=", limit: 6e4 }, message: "must be <= 60000" }; if (vErrors === null) { - vErrors = [err174]; + vErrors = [err176]; } else { - vErrors.push(err174); + vErrors.push(err176); } errors++; } - if (data74 < 0 || isNaN(data74)) { - const err175 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/read_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/read_timeout/minimum", keyword: "minimum", params: { comparison: ">=", limit: 0 }, message: "must be >= 0" }; + if (data76 < 0 || isNaN(data76)) { + const err177 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/read_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/read_timeout/minimum", keyword: "minimum", params: { comparison: ">=", limit: 0 }, message: "must be >= 0" }; if (vErrors === null) { - vErrors = [err175]; + vErrors = [err177]; } else { - vErrors.push(err175); + vErrors.push(err177); } errors++; } } } - if (data65.send_timeout !== void 0) { - let data75 = data65.send_timeout; - if (!(typeof data75 == "number" && (!(data75 % 1) && !isNaN(data75)))) { - const err176 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/send_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/send_timeout/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; + if (data67.send_timeout !== void 0) { + let data77 = data67.send_timeout; + if (!(typeof data77 == "number" && (!(data77 % 1) && !isNaN(data77)))) { + const err178 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/send_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/send_timeout/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; if (vErrors === null) { - vErrors = [err176]; + vErrors = [err178]; } else { - vErrors.push(err176); + vErrors.push(err178); } errors++; } - if (typeof data75 == "number") { - if (data75 > 6e4 || isNaN(data75)) { - const err177 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/send_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/send_timeout/maximum", keyword: "maximum", params: { comparison: "<=", limit: 6e4 }, message: "must be <= 60000" }; + if (typeof data77 == "number") { + if (data77 > 6e4 || isNaN(data77)) { + const err179 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/send_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/send_timeout/maximum", keyword: "maximum", params: { comparison: "<=", limit: 6e4 }, message: "must be <= 60000" }; if (vErrors === null) { - vErrors = [err177]; + vErrors = [err179]; } else { - vErrors.push(err177); + vErrors.push(err179); } errors++; } - if (data75 < 0 || isNaN(data75)) { - const err178 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/send_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/send_timeout/minimum", keyword: "minimum", params: { comparison: ">=", limit: 0 }, message: "must be >= 0" }; + if (data77 < 0 || isNaN(data77)) { + const err180 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options/send_timeout", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/properties/send_timeout/minimum", keyword: "minimum", params: { comparison: ">=", limit: 0 }, message: "must be >= 0" }; if (vErrors === null) { - vErrors = [err178]; + vErrors = [err180]; } else { - vErrors.push(err178); + vErrors.push(err180); } errors++; } } } } else { - const err179 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err181 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/http_options", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/http_options/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err179]; + vErrors = [err181]; } else { - vErrors.push(err179); + vErrors.push(err181); } errors++; } } - if (data61.port !== void 0) { - let data76 = data61.port; - if (!(typeof data76 == "number" && (!(data76 % 1) && !isNaN(data76)))) { - const err180 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/port", schemaPath: "#/definitions/portNumber/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; + if (data63.port !== void 0) { + let data78 = data63.port; + if (!(typeof data78 == "number" && (!(data78 % 1) && !isNaN(data78)))) { + const err182 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/port", schemaPath: "#/definitions/portNumber/type", keyword: "type", params: { type: "integer" }, message: "must be integer" }; if (vErrors === null) { - vErrors = [err180]; + vErrors = [err182]; } else { - vErrors.push(err180); + vErrors.push(err182); } errors++; } - if (typeof data76 == "number") { - if (data76 > 65535 || isNaN(data76)) { - const err181 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/port", schemaPath: "#/definitions/portNumber/maximum", keyword: "maximum", params: { comparison: "<=", limit: 65535 }, message: "must be <= 65535" }; + if (typeof data78 == "number") { + if (data78 > 65535 || isNaN(data78)) { + const err183 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/port", schemaPath: "#/definitions/portNumber/maximum", keyword: "maximum", params: { comparison: "<=", limit: 65535 }, message: "must be <= 65535" }; if (vErrors === null) { - vErrors = [err181]; + vErrors = [err183]; } else { - vErrors.push(err181); + vErrors.push(err183); } errors++; } - if (data76 < 1 || isNaN(data76)) { - const err182 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/port", schemaPath: "#/definitions/portNumber/minimum", keyword: "minimum", params: { comparison: ">=", limit: 1 }, message: "must be >= 1" }; + if (data78 < 1 || isNaN(data78)) { + const err184 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/port", schemaPath: "#/definitions/portNumber/minimum", keyword: "minimum", params: { comparison: ">=", limit: 1 }, message: "must be >= 1" }; if (vErrors === null) { - vErrors = [err182]; + vErrors = [err184]; } else { - vErrors.push(err182); + vErrors.push(err184); } errors++; } } } - if (data61.proto !== void 0) { - let data77 = data61.proto; - if (typeof data77 !== "string") { - const err183 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/proto", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/proto/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + if (data63.proto !== void 0) { + let data79 = data63.proto; + if (typeof data79 !== "string") { + const err185 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/proto", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/proto/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err183]; + vErrors = [err185]; } else { - vErrors.push(err183); + vErrors.push(err185); } errors++; } - if (!(data77 === "TCP" || data77 === "UDP" || data77 === "tcp" || data77 === "udp")) { - const err184 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/proto", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/proto/enum", keyword: "enum", params: { allowedValues: schema28.properties.services.additionalProperties.properties.expose.oneOf[0].items.properties.proto.enum }, message: "must be equal to one of the allowed values" }; + if (!(data79 === "TCP" || data79 === "UDP" || data79 === "tcp" || data79 === "udp")) { + const err186 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/proto", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/proto/enum", keyword: "enum", params: { allowedValues: schema28.properties.services.additionalProperties.properties.expose.oneOf[0].items.properties.proto.enum }, message: "must be equal to one of the allowed values" }; if (vErrors === null) { - vErrors = [err184]; + vErrors = [err186]; } else { - vErrors.push(err184); + vErrors.push(err186); } errors++; } } - if (data61.to !== void 0) { - let data78 = data61.to; - if (Array.isArray(data78)) { - const len15 = data78.length; + if (data63.to !== void 0) { + let data80 = data63.to; + if (Array.isArray(data80)) { + const len15 = data80.length; for (let i15 = 0; i15 < len15; i15++) { - let data79 = data78[i15]; - const _errs260 = errors; + let data81 = data80[i15]; + const _errs264 = errors; let valid90 = true; - const _errs261 = errors; - if (data79 && typeof data79 == "object" && !Array.isArray(data79)) { + const _errs265 = errors; + if (data81 && typeof data81 == "object" && !Array.isArray(data81)) { let missing2; - if (data79.ip === void 0 && (missing2 = "ip")) { - const err185 = {}; + if (data81.ip === void 0 && (missing2 = "ip")) { + const err187 = {}; if (vErrors === null) { - vErrors = [err185]; + vErrors = [err187]; } else { - vErrors.push(err185); + vErrors.push(err187); } errors++; } else { - if (data79.ip !== void 0) { - let data80 = data79.ip; - const _errs262 = errors; - if (errors === _errs262) { - if (typeof data80 === "string") { - if (func2(data80) < 1) { - const err186 = {}; + if (data81.ip !== void 0) { + let data82 = data81.ip; + const _errs266 = errors; + if (errors === _errs266) { + if (typeof data82 === "string") { + if (func2(data82) < 1) { + const err188 = {}; if (vErrors === null) { - vErrors = [err186]; + vErrors = [err188]; } else { - vErrors.push(err186); + vErrors.push(err188); } errors++; } } else { - const err187 = {}; + const err189 = {}; if (vErrors === null) { - vErrors = [err187]; + vErrors = [err189]; } else { - vErrors.push(err187); + vErrors.push(err189); } errors++; } @@ -3183,162 +3225,162 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r } } } - var _valid14 = _errs261 === errors; - errors = _errs260; + var _valid14 = _errs265 === errors; + errors = _errs264; if (vErrors !== null) { - if (_errs260) { - vErrors.length = _errs260; + if (_errs264) { + vErrors.length = _errs264; } else { vErrors = null; } } if (_valid14) { - const _errs264 = errors; - if (data79 && typeof data79 == "object" && !Array.isArray(data79)) { - if (data79.global === void 0) { - const err188 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15, schemaPath: "#/definitions/exposeToWithIpEnforcesGlobal/then/required", keyword: "required", params: { missingProperty: "global" }, message: "must have required property 'global'" }; + const _errs268 = errors; + if (data81 && typeof data81 == "object" && !Array.isArray(data81)) { + if (data81.global === void 0) { + const err190 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15, schemaPath: "#/definitions/exposeToWithIpEnforcesGlobal/then/required", keyword: "required", params: { missingProperty: "global" }, message: "must have required property 'global'" }; if (vErrors === null) { - vErrors = [err188]; + vErrors = [err190]; } else { - vErrors.push(err188); + vErrors.push(err190); } errors++; } - if (data79.global !== void 0) { - if (true !== data79.global) { - const err189 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15 + "/global", schemaPath: "#/definitions/exposeToWithIpEnforcesGlobal/then/properties/global/const", keyword: "const", params: { allowedValue: true }, message: "must be equal to constant" }; + if (data81.global !== void 0) { + if (true !== data81.global) { + const err191 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15 + "/global", schemaPath: "#/definitions/exposeToWithIpEnforcesGlobal/then/properties/global/const", keyword: "const", params: { allowedValue: true }, message: "must be equal to constant" }; if (vErrors === null) { - vErrors = [err189]; + vErrors = [err191]; } else { - vErrors.push(err189); + vErrors.push(err191); } errors++; } } } - var _valid14 = _errs264 === errors; + var _valid14 = _errs268 === errors; valid90 = _valid14; } if (!valid90) { - const err190 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15, schemaPath: "#/definitions/exposeToWithIpEnforcesGlobal/if", keyword: "if", params: { failingKeyword: "then" }, message: 'must match "then" schema' }; + const err192 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15, schemaPath: "#/definitions/exposeToWithIpEnforcesGlobal/if", keyword: "if", params: { failingKeyword: "then" }, message: 'must match "then" schema' }; if (vErrors === null) { - vErrors = [err190]; + vErrors = [err192]; } else { - vErrors.push(err190); + vErrors.push(err192); } errors++; } - if (data79 && typeof data79 == "object" && !Array.isArray(data79)) { - for (const key28 in data79) { + if (data81 && typeof data81 == "object" && !Array.isArray(data81)) { + for (const key28 in data81) { if (!(key28 === "global" || key28 === "ip" || key28 === "service")) { - const err191 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/items/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key28 }, message: "must NOT have additional properties" }; + const err193 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/items/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key28 }, message: "must NOT have additional properties" }; if (vErrors === null) { - vErrors = [err191]; + vErrors = [err193]; } else { - vErrors.push(err191); + vErrors.push(err193); } errors++; } } - if (data79.global !== void 0) { - if (typeof data79.global !== "boolean") { - const err192 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15 + "/global", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/items/properties/global/type", keyword: "type", params: { type: "boolean" }, message: "must be boolean" }; + if (data81.global !== void 0) { + if (typeof data81.global !== "boolean") { + const err194 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15 + "/global", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/items/properties/global/type", keyword: "type", params: { type: "boolean" }, message: "must be boolean" }; if (vErrors === null) { - vErrors = [err192]; + vErrors = [err194]; } else { - vErrors.push(err192); + vErrors.push(err194); } errors++; } } - if (data79.ip !== void 0) { - let data83 = data79.ip; - if (typeof data83 === "string") { - if (func2(data83) < 1) { - const err193 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15 + "/ip", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/items/properties/ip/minLength", keyword: "minLength", params: { limit: 1 }, message: "must NOT have fewer than 1 characters" }; + if (data81.ip !== void 0) { + let data85 = data81.ip; + if (typeof data85 === "string") { + if (func2(data85) < 1) { + const err195 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15 + "/ip", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/items/properties/ip/minLength", keyword: "minLength", params: { limit: 1 }, message: "must NOT have fewer than 1 characters" }; if (vErrors === null) { - vErrors = [err193]; + vErrors = [err195]; } else { - vErrors.push(err193); + vErrors.push(err195); } errors++; } } else { - const err194 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15 + "/ip", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/items/properties/ip/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + const err196 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15 + "/ip", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/items/properties/ip/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err194]; + vErrors = [err196]; } else { - vErrors.push(err194); + vErrors.push(err196); } errors++; } } - if (data79.service !== void 0) { - if (typeof data79.service !== "string") { - const err195 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15 + "/service", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/items/properties/service/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + if (data81.service !== void 0) { + if (typeof data81.service !== "string") { + const err197 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15 + "/service", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/items/properties/service/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err195]; + vErrors = [err197]; } else { - vErrors.push(err195); + vErrors.push(err197); } errors++; } } } else { - const err196 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/items/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err198 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to/" + i15, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/items/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err196]; + vErrors = [err198]; } else { - vErrors.push(err196); + vErrors.push(err198); } errors++; } } } else { - const err197 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/type", keyword: "type", params: { type: "array" }, message: "must be array" }; + const err199 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9 + "/to", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/properties/to/type", keyword: "type", params: { type: "array" }, message: "must be array" }; if (vErrors === null) { - vErrors = [err197]; + vErrors = [err199]; } else { - vErrors.push(err197); + vErrors.push(err199); } errors++; } } } else { - const err198 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err200 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose/" + i9, schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/items/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err198]; + vErrors = [err200]; } else { - vErrors.push(err198); + vErrors.push(err200); } errors++; } } } else { - const err199 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/type", keyword: "type", params: { type: "array" }, message: "must be array" }; + const err201 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/0/type", keyword: "type", params: { type: "array" }, message: "must be array" }; if (vErrors === null) { - vErrors = [err199]; + vErrors = [err201]; } else { - vErrors.push(err199); + vErrors.push(err201); } errors++; } - var _valid12 = _errs206 === errors; + var _valid12 = _errs210 === errors; if (_valid12) { valid67 = true; passing10 = 0; } - const _errs273 = errors; - if (data60 !== null) { - const err200 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/1/type", keyword: "type", params: { type: "null" }, message: "must be null" }; + const _errs277 = errors; + if (data62 !== null) { + const err202 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf/1/type", keyword: "type", params: { type: "null" }, message: "must be null" }; if (vErrors === null) { - vErrors = [err200]; + vErrors = [err202]; } else { - vErrors.push(err200); + vErrors.push(err202); } errors++; } - var _valid12 = _errs273 === errors; + var _valid12 = _errs277 === errors; if (_valid12 && valid67) { valid67 = false; passing10 = [passing10, 1]; @@ -3349,310 +3391,310 @@ function validate27(data, { instancePath = "", parentData, parentDataProperty, r } } if (!valid67) { - const err201 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf", keyword: "oneOf", params: { passingSchemas: passing10 }, message: "must match exactly one schema in oneOf" }; + const err203 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/expose", schemaPath: "#/properties/services/additionalProperties/properties/expose/oneOf", keyword: "oneOf", params: { passingSchemas: passing10 }, message: "must match exactly one schema in oneOf" }; if (vErrors === null) { - vErrors = [err201]; + vErrors = [err203]; } else { - vErrors.push(err201); + vErrors.push(err203); } errors++; } else { - errors = _errs205; + errors = _errs209; if (vErrors !== null) { - if (_errs205) { - vErrors.length = _errs205; + if (_errs209) { + vErrors.length = _errs209; } else { vErrors = null; } } } } - if (data45.image !== void 0) { - let data85 = data45.image; - if (typeof data85 === "string") { - if (func2(data85) < 1) { - const err202 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/image", schemaPath: "#/properties/services/additionalProperties/properties/image/minLength", keyword: "minLength", params: { limit: 1 }, message: "must NOT have fewer than 1 characters" }; + if (data47.image !== void 0) { + let data87 = data47.image; + if (typeof data87 === "string") { + if (func2(data87) < 1) { + const err204 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/image", schemaPath: "#/properties/services/additionalProperties/properties/image/minLength", keyword: "minLength", params: { limit: 1 }, message: "must NOT have fewer than 1 characters" }; if (vErrors === null) { - vErrors = [err202]; + vErrors = [err204]; } else { - vErrors.push(err202); + vErrors.push(err204); } errors++; } } else { - const err203 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/image", schemaPath: "#/properties/services/additionalProperties/properties/image/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + const err205 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/image", schemaPath: "#/properties/services/additionalProperties/properties/image/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err203]; + vErrors = [err205]; } else { - vErrors.push(err203); + vErrors.push(err205); } errors++; } } - if (data45.params !== void 0) { - let data86 = data45.params; - if (data86 && typeof data86 == "object" && !Array.isArray(data86)) { - for (const key29 in data86) { + if (data47.params !== void 0) { + let data88 = data47.params; + if (data88 && typeof data88 == "object" && !Array.isArray(data88)) { + for (const key29 in data88) { if (!(key29 === "storage" || key29 === "permissions")) { - const err204 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params", schemaPath: "#/properties/services/additionalProperties/properties/params/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key29 }, message: "must NOT have additional properties" }; + const err206 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params", schemaPath: "#/properties/services/additionalProperties/properties/params/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key29 }, message: "must NOT have additional properties" }; if (vErrors === null) { - vErrors = [err204]; + vErrors = [err206]; } else { - vErrors.push(err204); + vErrors.push(err206); } errors++; } } - if (data86.storage !== void 0) { - let data87 = data86.storage; - if (data87 && typeof data87 == "object" && !Array.isArray(data87)) { - for (const key30 in data87) { - let data88 = data87[key30]; - if (data88 && typeof data88 == "object" && !Array.isArray(data88)) { - for (const key31 in data88) { + if (data88.storage !== void 0) { + let data89 = data88.storage; + if (data89 && typeof data89 == "object" && !Array.isArray(data89)) { + for (const key30 in data89) { + let data90 = data89[key30]; + if (data90 && typeof data90 == "object" && !Array.isArray(data90)) { + for (const key31 in data90) { if (!(key31 === "mount" || key31 === "readOnly")) { - const err205 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage/" + key30.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/services/additionalProperties/properties/params/properties/storage/additionalProperties/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key31 }, message: "must NOT have additional properties" }; + const err207 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage/" + key30.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/services/additionalProperties/properties/params/properties/storage/additionalProperties/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key31 }, message: "must NOT have additional properties" }; if (vErrors === null) { - vErrors = [err205]; + vErrors = [err207]; } else { - vErrors.push(err205); + vErrors.push(err207); } errors++; } } - if (data88.mount !== void 0) { - let data89 = data88.mount; - if (typeof data89 === "string") { - if (func2(data89) < 1) { - const err206 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage/" + key30.replace(/~/g, "~0").replace(/\//g, "~1") + "/mount", schemaPath: "#/definitions/absolutePath/minLength", keyword: "minLength", params: { limit: 1 }, message: "must NOT have fewer than 1 characters" }; + if (data90.mount !== void 0) { + let data91 = data90.mount; + if (typeof data91 === "string") { + if (func2(data91) < 1) { + const err208 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage/" + key30.replace(/~/g, "~0").replace(/\//g, "~1") + "/mount", schemaPath: "#/definitions/absolutePath/minLength", keyword: "minLength", params: { limit: 1 }, message: "must NOT have fewer than 1 characters" }; if (vErrors === null) { - vErrors = [err206]; + vErrors = [err208]; } else { - vErrors.push(err206); + vErrors.push(err208); } errors++; } - if (!pattern11.test(data89)) { - const err207 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage/" + key30.replace(/~/g, "~0").replace(/\//g, "~1") + "/mount", schemaPath: "#/definitions/absolutePath/pattern", keyword: "pattern", params: { pattern: "^/" }, message: 'must match pattern "^/"' }; + if (!pattern11.test(data91)) { + const err209 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage/" + key30.replace(/~/g, "~0").replace(/\//g, "~1") + "/mount", schemaPath: "#/definitions/absolutePath/pattern", keyword: "pattern", params: { pattern: "^/" }, message: 'must match pattern "^/"' }; if (vErrors === null) { - vErrors = [err207]; + vErrors = [err209]; } else { - vErrors.push(err207); + vErrors.push(err209); } errors++; } } else { - const err208 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage/" + key30.replace(/~/g, "~0").replace(/\//g, "~1") + "/mount", schemaPath: "#/definitions/absolutePath/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + const err210 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage/" + key30.replace(/~/g, "~0").replace(/\//g, "~1") + "/mount", schemaPath: "#/definitions/absolutePath/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err208]; + vErrors = [err210]; } else { - vErrors.push(err208); + vErrors.push(err210); } errors++; } } - if (data88.readOnly !== void 0) { - if (typeof data88.readOnly !== "boolean") { - const err209 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage/" + key30.replace(/~/g, "~0").replace(/\//g, "~1") + "/readOnly", schemaPath: "#/properties/services/additionalProperties/properties/params/properties/storage/additionalProperties/properties/readOnly/type", keyword: "type", params: { type: "boolean" }, message: "must be boolean" }; + if (data90.readOnly !== void 0) { + if (typeof data90.readOnly !== "boolean") { + const err211 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage/" + key30.replace(/~/g, "~0").replace(/\//g, "~1") + "/readOnly", schemaPath: "#/properties/services/additionalProperties/properties/params/properties/storage/additionalProperties/properties/readOnly/type", keyword: "type", params: { type: "boolean" }, message: "must be boolean" }; if (vErrors === null) { - vErrors = [err209]; + vErrors = [err211]; } else { - vErrors.push(err209); + vErrors.push(err211); } errors++; } } } else { - const err210 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage/" + key30.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/services/additionalProperties/properties/params/properties/storage/additionalProperties/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err212 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage/" + key30.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/services/additionalProperties/properties/params/properties/storage/additionalProperties/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err210]; + vErrors = [err212]; } else { - vErrors.push(err210); + vErrors.push(err212); } errors++; } } } else { - const err211 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage", schemaPath: "#/properties/services/additionalProperties/properties/params/properties/storage/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err213 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/storage", schemaPath: "#/properties/services/additionalProperties/properties/params/properties/storage/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err211]; + vErrors = [err213]; } else { - vErrors.push(err211); + vErrors.push(err213); } errors++; } } - if (data86.permissions !== void 0) { - let data91 = data86.permissions; - if (data91 && typeof data91 == "object" && !Array.isArray(data91)) { - for (const key32 in data91) { + if (data88.permissions !== void 0) { + let data93 = data88.permissions; + if (data93 && typeof data93 == "object" && !Array.isArray(data93)) { + for (const key32 in data93) { if (!(key32 === "read")) { - const err212 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/permissions", schemaPath: "#/properties/services/additionalProperties/properties/params/properties/permissions/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key32 }, message: "must NOT have additional properties" }; + const err214 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/permissions", schemaPath: "#/properties/services/additionalProperties/properties/params/properties/permissions/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key32 }, message: "must NOT have additional properties" }; if (vErrors === null) { - vErrors = [err212]; + vErrors = [err214]; } else { - vErrors.push(err212); + vErrors.push(err214); } errors++; } } - if (data91.read !== void 0) { - let data92 = data91.read; - if (Array.isArray(data92)) { - const len16 = data92.length; + if (data93.read !== void 0) { + let data94 = data93.read; + if (Array.isArray(data94)) { + const len16 = data94.length; for (let i16 = 0; i16 < len16; i16++) { - let data93 = data92[i16]; - if (typeof data93 !== "string") { - const err213 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/permissions/read/" + i16, schemaPath: "#/properties/services/additionalProperties/properties/params/properties/permissions/properties/read/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + let data95 = data94[i16]; + if (typeof data95 !== "string") { + const err215 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/permissions/read/" + i16, schemaPath: "#/properties/services/additionalProperties/properties/params/properties/permissions/properties/read/items/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err213]; + vErrors = [err215]; } else { - vErrors.push(err213); + vErrors.push(err215); } errors++; } - if (!(data93 === "deployment" || data93 === "logs" || data93 === "events")) { - const err214 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/permissions/read/" + i16, schemaPath: "#/properties/services/additionalProperties/properties/params/properties/permissions/properties/read/items/enum", keyword: "enum", params: { allowedValues: schema28.properties.services.additionalProperties.properties.params.properties.permissions.properties.read.items.enum }, message: "must be equal to one of the allowed values" }; + if (!(data95 === "deployment" || data95 === "logs" || data95 === "events")) { + const err216 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/permissions/read/" + i16, schemaPath: "#/properties/services/additionalProperties/properties/params/properties/permissions/properties/read/items/enum", keyword: "enum", params: { allowedValues: schema28.properties.services.additionalProperties.properties.params.properties.permissions.properties.read.items.enum }, message: "must be equal to one of the allowed values" }; if (vErrors === null) { - vErrors = [err214]; + vErrors = [err216]; } else { - vErrors.push(err214); + vErrors.push(err216); } errors++; } } } else { - const err215 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/permissions/read", schemaPath: "#/properties/services/additionalProperties/properties/params/properties/permissions/properties/read/type", keyword: "type", params: { type: "array" }, message: "must be array" }; + const err217 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/permissions/read", schemaPath: "#/properties/services/additionalProperties/properties/params/properties/permissions/properties/read/type", keyword: "type", params: { type: "array" }, message: "must be array" }; if (vErrors === null) { - vErrors = [err215]; + vErrors = [err217]; } else { - vErrors.push(err215); + vErrors.push(err217); } errors++; } } } else { - const err216 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/permissions", schemaPath: "#/properties/services/additionalProperties/properties/params/properties/permissions/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err218 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params/permissions", schemaPath: "#/properties/services/additionalProperties/properties/params/properties/permissions/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err216]; + vErrors = [err218]; } else { - vErrors.push(err216); + vErrors.push(err218); } errors++; } } } else { - const err217 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params", schemaPath: "#/properties/services/additionalProperties/properties/params/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err219 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1") + "/params", schemaPath: "#/properties/services/additionalProperties/properties/params/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err217]; + vErrors = [err219]; } else { - vErrors.push(err217); + vErrors.push(err219); } errors++; } } } else { - const err218 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/services/additionalProperties/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err220 = { instancePath: instancePath + "/services/" + key22.replace(/~/g, "~0").replace(/\//g, "~1"), schemaPath: "#/properties/services/additionalProperties/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err218]; + vErrors = [err220]; } else { - vErrors.push(err218); + vErrors.push(err220); } errors++; } } } else { - const err219 = { instancePath: instancePath + "/services", schemaPath: "#/properties/services/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err221 = { instancePath: instancePath + "/services", schemaPath: "#/properties/services/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err219]; + vErrors = [err221]; } else { - vErrors.push(err219); + vErrors.push(err221); } errors++; } } if (data.reclamation !== void 0) { - let data94 = data.reclamation; - if (data94 && typeof data94 == "object" && !Array.isArray(data94)) { - if (data94.min_window === void 0) { - const err220 = { instancePath: instancePath + "/reclamation", schemaPath: "#/properties/reclamation/required", keyword: "required", params: { missingProperty: "min_window" }, message: "must have required property 'min_window'" }; + let data96 = data.reclamation; + if (data96 && typeof data96 == "object" && !Array.isArray(data96)) { + if (data96.min_window === void 0) { + const err222 = { instancePath: instancePath + "/reclamation", schemaPath: "#/properties/reclamation/required", keyword: "required", params: { missingProperty: "min_window" }, message: "must have required property 'min_window'" }; if (vErrors === null) { - vErrors = [err220]; + vErrors = [err222]; } else { - vErrors.push(err220); + vErrors.push(err222); } errors++; } - for (const key33 in data94) { + for (const key33 in data96) { if (!(key33 === "min_window")) { - const err221 = { instancePath: instancePath + "/reclamation", schemaPath: "#/properties/reclamation/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key33 }, message: "must NOT have additional properties" }; + const err223 = { instancePath: instancePath + "/reclamation", schemaPath: "#/properties/reclamation/additionalProperties", keyword: "additionalProperties", params: { additionalProperty: key33 }, message: "must NOT have additional properties" }; if (vErrors === null) { - vErrors = [err221]; + vErrors = [err223]; } else { - vErrors.push(err221); + vErrors.push(err223); } errors++; } } - if (data94.min_window !== void 0) { - let data95 = data94.min_window; - if (typeof data95 === "string") { - if (!pattern12.test(data95)) { - const err222 = { instancePath: instancePath + "/reclamation/min_window", schemaPath: "#/properties/reclamation/properties/min_window/pattern", keyword: "pattern", params: { pattern: "^[1-9][0-9]*(s|m|h)$" }, message: 'must match pattern "^[1-9][0-9]*(s|m|h)$"' }; + if (data96.min_window !== void 0) { + let data97 = data96.min_window; + if (typeof data97 === "string") { + if (!pattern12.test(data97)) { + const err224 = { instancePath: instancePath + "/reclamation/min_window", schemaPath: "#/properties/reclamation/properties/min_window/pattern", keyword: "pattern", params: { pattern: "^[1-9][0-9]*(s|m|h)$" }, message: 'must match pattern "^[1-9][0-9]*(s|m|h)$"' }; if (vErrors === null) { - vErrors = [err222]; + vErrors = [err224]; } else { - vErrors.push(err222); + vErrors.push(err224); } errors++; } } else { - const err223 = { instancePath: instancePath + "/reclamation/min_window", schemaPath: "#/properties/reclamation/properties/min_window/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + const err225 = { instancePath: instancePath + "/reclamation/min_window", schemaPath: "#/properties/reclamation/properties/min_window/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err223]; + vErrors = [err225]; } else { - vErrors.push(err223); + vErrors.push(err225); } errors++; } } } else { - const err224 = { instancePath: instancePath + "/reclamation", schemaPath: "#/properties/reclamation/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err226 = { instancePath: instancePath + "/reclamation", schemaPath: "#/properties/reclamation/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err224]; + vErrors = [err226]; } else { - vErrors.push(err224); + vErrors.push(err226); } errors++; } } if (data.version !== void 0) { - let data96 = data.version; - if (typeof data96 !== "string") { - const err225 = { instancePath: instancePath + "/version", schemaPath: "#/properties/version/type", keyword: "type", params: { type: "string" }, message: "must be string" }; + let data98 = data.version; + if (typeof data98 !== "string") { + const err227 = { instancePath: instancePath + "/version", schemaPath: "#/properties/version/type", keyword: "type", params: { type: "string" }, message: "must be string" }; if (vErrors === null) { - vErrors = [err225]; + vErrors = [err227]; } else { - vErrors.push(err225); + vErrors.push(err227); } errors++; } - if (!(data96 === "2.0" || data96 === "2.1")) { - const err226 = { instancePath: instancePath + "/version", schemaPath: "#/properties/version/enum", keyword: "enum", params: { allowedValues: schema28.properties.version.enum }, message: "must be equal to one of the allowed values" }; + if (!(data98 === "2.0" || data98 === "2.1")) { + const err228 = { instancePath: instancePath + "/version", schemaPath: "#/properties/version/enum", keyword: "enum", params: { allowedValues: schema28.properties.version.enum }, message: "must be equal to one of the allowed values" }; if (vErrors === null) { - vErrors = [err226]; + vErrors = [err228]; } else { - vErrors.push(err226); + vErrors.push(err228); } errors++; } } } else { - const err227 = { instancePath, schemaPath: "#/type", keyword: "type", params: { type: "object" }, message: "must be object" }; + const err229 = { instancePath, schemaPath: "#/type", keyword: "type", params: { type: "object" }, message: "must be object" }; if (vErrors === null) { - vErrors = [err227]; + vErrors = [err229]; } else { - vErrors.push(err227); + vErrors.push(err229); } errors++; }