From aebc94074fefe56a88f12dc05a7da3081224bbbc Mon Sep 17 00:00:00 2001 From: Matthew Rich Date: Sun, 5 May 2024 00:14:57 -0700 Subject: [PATCH] add identity --- identity/address.pb.go | 146 ++++++++++++++++++++++++++++++++++++ identity/address.proto | 8 ++ identity/identifier.go | 109 +++++++++++++++++++++++++++ identity/identifier_test.go | 58 ++++++++++++++ 4 files changed, 321 insertions(+) create mode 100644 identity/address.pb.go create mode 100644 identity/address.proto create mode 100644 identity/identifier.go create mode 100644 identity/identifier_test.go diff --git a/identity/address.pb.go b/identity/address.pb.go new file mode 100644 index 0000000..4d0e6c2 --- /dev/null +++ b/identity/address.pb.go @@ -0,0 +1,146 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.23.0 +// protoc v3.12.2 +// source: address.proto + +package identity + +import ( + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type Address struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +} + +func (x *Address) Reset() { + *x = Address{} + if protoimpl.UnsafeEnabled { + mi := &file_address_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Address) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Address) ProtoMessage() {} + +func (x *Address) ProtoReflect() protoreflect.Message { + mi := &file_address_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Address.ProtoReflect.Descriptor instead. +func (*Address) Descriptor() ([]byte, []int) { + return file_address_proto_rawDescGZIP(), []int{0} +} + +func (x *Address) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +var File_address_proto protoreflect.FileDescriptor + +var file_address_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x22, 0x23, 0x0a, 0x07, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_address_proto_rawDescOnce sync.Once + file_address_proto_rawDescData = file_address_proto_rawDesc +) + +func file_address_proto_rawDescGZIP() []byte { + file_address_proto_rawDescOnce.Do(func() { + file_address_proto_rawDescData = protoimpl.X.CompressGZIP(file_address_proto_rawDescData) + }) + return file_address_proto_rawDescData +} + +var file_address_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_address_proto_goTypes = []interface{}{ + (*Address)(nil), // 0: identity.Address +} +var file_address_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_address_proto_init() } +func file_address_proto_init() { + if File_address_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_address_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Address); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_address_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_address_proto_goTypes, + DependencyIndexes: file_address_proto_depIdxs, + MessageInfos: file_address_proto_msgTypes, + }.Build() + File_address_proto = out.File + file_address_proto_rawDesc = nil + file_address_proto_goTypes = nil + file_address_proto_depIdxs = nil +} diff --git a/identity/address.proto b/identity/address.proto new file mode 100644 index 0000000..01aff32 --- /dev/null +++ b/identity/address.proto @@ -0,0 +1,8 @@ +// Copyright 2024 Matthew Rich . All rights reserved. + +syntax = "proto3"; +package identity; + +message Address { + string address = 1; +} diff --git a/identity/identifier.go b/identity/identifier.go new file mode 100644 index 0000000..d2339a6 --- /dev/null +++ b/identity/identifier.go @@ -0,0 +1,109 @@ +// Copyright 2024 Matthew Rich . All rights reserved. + +package identity + +import ( + "os" + "fmt" + "strings" + "net/url" + "feudal/sequence" +) + +var Serial sequence.Serial32 + +var spid uint64 +var serial uint64 +var hostname string + +type Addresser interface { + Address() string +} + +type Identifier interface { + Addresser + Instance() string + Path() string + Hostname() string + String() string + Parent() Identifier + CreateChild(string) Identifier +} + +type Id struct { + parent Identifier + Name string + url *url.URL + instance string +} + +func init() { + Serial = sequence.NewSerial32() + spid = uint64(os.Getpid()) << 32 + hostname, _ = os.Hostname() +} + +func New(parent Identifier, name string) Identifier { + url,_ := url.Parse(parent.Address()) + url.Path += "/" + name + i := &Id{ parent: parent, Name: name, url: url, instance: InstanceId() } + return i +} + +func NewRoot(name string) Identifier { + return &Id{ parent: nil, Name: name, url: &url.URL{ Scheme: "feudal", User: url.User(name), Host: hostname, Path: "" }, instance: InstanceId() } +} + +func NewFromAddress(address string) Identifier { + id := &Id{ parent: nil, instance: InstanceId() } + id.url,_ = url.Parse(address) + + elements := strings.Split(id.url.Path, "/") + le := len(elements) + + if le == 0 { + id.Name = id.url.User.Username() + } else { + id.Name = elements[le - 1] + } + return id +} + +func InstanceId() string { + return fmt.Sprintf("%x", spid | uint64(Serial())) +} + +func (i *Id) Instance() string { + return i.instance +} + +func (i *Id) Path() string { + return i.url.Path +} + +func (i *Id) Hostname() string { + return i.url.Hostname() +} + +func (parent *Id) CreateChild(name string) Identifier { + url,_ := url.Parse(parent.Address()) + url.Path += "/" + name + i := &Id{ parent: parent, Name: name, url: url, instance: InstanceId() } + return i +} + +func (i *Id) Parent() Identifier { + return i.parent +} + +func (i *Id) Elements() []string { + return strings.Split(i.url.Path, "/") +} + +func (i *Id) String() string { + return i.Address() + "#" + i.instance +} + +func (i *Id) Address() string { + return i.url.String() +} diff --git a/identity/identifier_test.go b/identity/identifier_test.go new file mode 100644 index 0000000..81e2d08 --- /dev/null +++ b/identity/identifier_test.go @@ -0,0 +1,58 @@ +// Copyright 2024 Matthew Rich . All rights reserved. + +package identity + +import ( + "net/url" + "testing" +) + + +func setupIdentifier() Identifier { + root := NewRoot("sys") + return New(root, "test") +} + +func TestNewIdentifier(t *testing.T) { + i := setupIdentifier() + if i == nil { + t.Errorf("Failed creating new identifier") + } +} + +func TestNewChildIdentifier(t *testing.T) { + i := setupIdentifier() + c := i.CreateChild("newchild") + a := c.Address() + if a != i.Address() + "/newchild" { + t.Errorf("CreateChild() failed creating new child identifier: %s", a) + } +} + +func TestIdentifierAddress(t *testing.T) { + i := setupIdentifier() + a := i.Address() + u,e := url.Parse(a) + if e != nil { + t.Errorf("Failed parsing identifier address %s", e) + } + if u.Scheme != "feudal" { + t.Errorf("Invalid identifier address scheme %s", u.Scheme) + } +} + +func TestIdentifierParent(t *testing.T) { + i := setupIdentifier() + c := i.CreateChild("task") + u,e := url.Parse(c.Parent().Address()) + if e != nil || u.Path != "/test" { + t.Errorf("Parent() returned incorrect path %s %s", u.Path, e) + } +} + +func TestNewFromAddress(t *testing.T) { + i := NewFromAddress("feudal://test@hostfoo/workerbar") + if i.Hostname() != "hostfoo" { + t.Errorf("Failed generating identifier from url - hostname mismatch") + } +}