Skip to content

Commit

Permalink
datalakestore/paths: reintroducing the Resource ID Parser
Browse files Browse the repository at this point in the history
  • Loading branch information
hc-github-team-tf-azure committed Dec 14, 2023
1 parent 91b1b4a commit fd7fe5d
Show file tree
Hide file tree
Showing 4 changed files with 498 additions and 0 deletions.
80 changes: 80 additions & 0 deletions storage/2020-08-04/datalakestore/paths/resource_id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package paths

import (
"fmt"
"net/url"
"strings"

"github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids"
"github.com/tombuildsstuff/giovanni/storage/2020-08-04/blob/accounts"
)

// TODO: update this to implement `resourceids.ResourceId` once
// https://github.com/hashicorp/go-azure-helpers/issues/187 is fixed
var _ resourceids.Id = PathId{}

type PathId struct {
// AccountId specifies the ID of the Storage Account where this path exists.
AccountId accounts.AccountId

// FileSystemName specifies the name of the Data Lake FileSystem where this Path exists.
FileSystemName string

// Path specifies the path in question.
Path string
}

func NewPathID(accountId accounts.AccountId, fileSystemName, path string) PathId {
return PathId{
AccountId: accountId,
FileSystemName: fileSystemName,
Path: path,
}
}

func (b PathId) ID() string {
return fmt.Sprintf("%s/%s/%s", b.AccountId.ID(), b.FileSystemName, b.Path)
}

func (b PathId) String() string {
components := []string{
fmt.Sprintf("File System %q", b.FileSystemName),
fmt.Sprintf("Account %q", b.AccountId.String()),
}
return fmt.Sprintf("Path %q (%s)", b.Path, strings.Join(components, " / "))
}

// ParsePathID parses `input` into a Path ID using a known `domainSuffix`
func ParsePathID(input, domainSuffix string) (*PathId, error) {
// example: https://foo.dfs.core.windows.net/Bar/some/path
if input == "" {
return nil, fmt.Errorf("`input` was empty")
}

account, err := accounts.ParseAccountID(input, domainSuffix)
if err != nil {
return nil, fmt.Errorf("parsing account %q: %+v", input, err)
}

if account.SubDomainType != accounts.DataLakeStoreSubDomainType {
return nil, fmt.Errorf("expected the subdomain type to be %q but got %q", string(accounts.DataLakeStoreSubDomainType), string(account.SubDomainType))
}

uri, err := url.Parse(input)
if err != nil {
return nil, fmt.Errorf("parsing %q as a uri: %+v", input, err)
}

uriPath := strings.TrimPrefix(uri.Path, "/")
segments := strings.Split(uriPath, "/")
if len(segments) < 2 {
return nil, fmt.Errorf("expected the path to contain at least 2 segments but got %d", len(segments))
}
fileSystemName := segments[0]
path := strings.Join(segments[1:], "/")
return &PathId{
AccountId: *account,
FileSystemName: fileSystemName,
Path: path,
}, nil
}
169 changes: 169 additions & 0 deletions storage/2020-08-04/datalakestore/paths/resource_id_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package paths

import (
"testing"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/tombuildsstuff/giovanni/storage/2020-08-04/blob/accounts"
)

func TestParsePathIDStandard(t *testing.T) {
input := "https://example1.dfs.core.windows.net/fileSystem1/some/path"
expected := PathId{
AccountId: accounts.AccountId{
AccountName: "example1",
SubDomainType: accounts.DataLakeStoreSubDomainType,
DomainSuffix: "core.windows.net",
},
FileSystemName: "fileSystem1",
Path: "some/path",
}
actual, err := ParsePathID(input, "core.windows.net")
if err != nil {
t.Fatalf(err.Error())
}
if actual.AccountId.AccountName != expected.AccountId.AccountName {
t.Fatalf("expected AccountName to be %q but got %q", expected.AccountId.AccountName, actual.AccountId.AccountName)
}
if actual.AccountId.SubDomainType != expected.AccountId.SubDomainType {
t.Fatalf("expected SubDomainType to be %q but got %q", expected.AccountId.SubDomainType, actual.AccountId.SubDomainType)
}
if actual.AccountId.DomainSuffix != expected.AccountId.DomainSuffix {
t.Fatalf("expected DomainSuffix to be %q but got %q", expected.AccountId.DomainSuffix, actual.AccountId.DomainSuffix)
}
if actual.FileSystemName != expected.FileSystemName {
t.Fatalf("expected FileSystemName to be %q but got %q", expected.FileSystemName, actual.FileSystemName)
}
if actual.Path != expected.Path {
t.Fatalf("expected Path to be %q but got %q", expected.Path, actual.Path)
}
}

func TestParsePathIDInADNSZone(t *testing.T) {
input := "https://example1.zone1.dfs.storage.azure.net/fileSystem1/some/path"
expected := PathId{
AccountId: accounts.AccountId{
AccountName: "example1",
SubDomainType: accounts.DataLakeStoreSubDomainType,
DomainSuffix: "storage.azure.net",
ZoneName: pointer.To("zone1"),
},
FileSystemName: "fileSystem1",
Path: "some/path",
}
actual, err := ParsePathID(input, "storage.azure.net")
if err != nil {
t.Fatalf(err.Error())
}
if actual.AccountId.AccountName != expected.AccountId.AccountName {
t.Fatalf("expected AccountName to be %q but got %q", expected.AccountId.AccountName, actual.AccountId.AccountName)
}
if actual.AccountId.SubDomainType != expected.AccountId.SubDomainType {
t.Fatalf("expected SubDomainType to be %q but got %q", expected.AccountId.SubDomainType, actual.AccountId.SubDomainType)
}
if actual.AccountId.DomainSuffix != expected.AccountId.DomainSuffix {
t.Fatalf("expected DomainSuffix to be %q but got %q", expected.AccountId.DomainSuffix, actual.AccountId.DomainSuffix)
}
if pointer.From(actual.AccountId.ZoneName) != pointer.From(expected.AccountId.ZoneName) {
t.Fatalf("expected ZoneName to be %q but got %q", pointer.From(expected.AccountId.ZoneName), pointer.From(actual.AccountId.ZoneName))
}
if actual.FileSystemName != expected.FileSystemName {
t.Fatalf("expected FileSystemName to be %q but got %q", expected.FileSystemName, actual.FileSystemName)
}
if actual.Path != expected.Path {
t.Fatalf("expected Path to be %q but got %q", expected.Path, actual.Path)
}
}

func TestParsePathIDInAnEdgeZone(t *testing.T) {
input := "https://example1.dfs.zone1.edgestorage.azure.net/fileSystem1/some/path"
expected := PathId{
AccountId: accounts.AccountId{
AccountName: "example1",
SubDomainType: accounts.DataLakeStoreSubDomainType,
DomainSuffix: "edgestorage.azure.net",
ZoneName: pointer.To("zone1"),
IsEdgeZone: true,
},
FileSystemName: "fileSystem1",
Path: "some/path",
}
actual, err := ParsePathID(input, "edgestorage.azure.net")
if err != nil {
t.Fatalf(err.Error())
}
if actual.AccountId.AccountName != expected.AccountId.AccountName {
t.Fatalf("expected AccountName to be %q but got %q", expected.AccountId.AccountName, actual.AccountId.AccountName)
}
if actual.AccountId.SubDomainType != expected.AccountId.SubDomainType {
t.Fatalf("expected SubDomainType to be %q but got %q", expected.AccountId.SubDomainType, actual.AccountId.SubDomainType)
}
if actual.AccountId.DomainSuffix != expected.AccountId.DomainSuffix {
t.Fatalf("expected DomainSuffix to be %q but got %q", expected.AccountId.DomainSuffix, actual.AccountId.DomainSuffix)
}
if pointer.From(actual.AccountId.ZoneName) != pointer.From(expected.AccountId.ZoneName) {
t.Fatalf("expected ZoneName to be %q but got %q", pointer.From(expected.AccountId.ZoneName), pointer.From(actual.AccountId.ZoneName))
}
if !actual.AccountId.IsEdgeZone {
t.Fatalf("expected the Account to be in an Edge Zone but it wasn't")
}
if actual.FileSystemName != expected.FileSystemName {
t.Fatalf("expected FileSystemName to be %q but got %q", expected.FileSystemName, actual.FileSystemName)
}
if actual.Path != expected.Path {
t.Fatalf("expected Path to be %q but got %q", expected.Path, actual.Path)
}
}

func TestFormatPathIDStandard(t *testing.T) {
actual := PathId{
AccountId: accounts.AccountId{
AccountName: "example1",
SubDomainType: accounts.DataLakeStoreSubDomainType,
DomainSuffix: "core.windows.net",
IsEdgeZone: false,
},
FileSystemName: "fileSystem1",
Path: "some/path",
}.ID()
expected := "https://example1.dfs.core.windows.net/fileSystem1/some/path"
if actual != expected {
t.Fatalf("expected %q but got %q", expected, actual)
}
}

func TestFormatPathIDInDNSZone(t *testing.T) {
actual := PathId{
AccountId: accounts.AccountId{
AccountName: "example1",
ZoneName: pointer.To("zone2"),
SubDomainType: accounts.DataLakeStoreSubDomainType,
DomainSuffix: "storage.azure.net",
IsEdgeZone: false,
},
FileSystemName: "fileSystem1",
Path: "some/path",
}.ID()
expected := "https://example1.zone2.dfs.storage.azure.net/fileSystem1/some/path"
if actual != expected {
t.Fatalf("expected %q but got %q", expected, actual)
}
}

func TestFormatPathIDInEdgeZone(t *testing.T) {
actual := PathId{
AccountId: accounts.AccountId{
AccountName: "example1",
ZoneName: pointer.To("zone2"),
SubDomainType: accounts.DataLakeStoreSubDomainType,
DomainSuffix: "edgestorage.azure.net",
IsEdgeZone: true,
},
FileSystemName: "fileSystem1",
Path: "some",
}.ID()
expected := "https://example1.dfs.zone2.edgestorage.azure.net/fileSystem1/some"
if actual != expected {
t.Fatalf("expected %q but got %q", expected, actual)
}
}
80 changes: 80 additions & 0 deletions storage/2023-11-03/datalakestore/paths/resource_id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package paths

import (
"fmt"
"net/url"
"strings"

"github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids"
"github.com/tombuildsstuff/giovanni/storage/2023-11-03/blob/accounts"
)

// TODO: update this to implement `resourceids.ResourceId` once
// https://github.com/hashicorp/go-azure-helpers/issues/187 is fixed
var _ resourceids.Id = PathId{}

type PathId struct {
// AccountId specifies the ID of the Storage Account where this path exists.
AccountId accounts.AccountId

// FileSystemName specifies the name of the Data Lake FileSystem where this Path exists.
FileSystemName string

// Path specifies the path in question.
Path string
}

func NewPathID(accountId accounts.AccountId, fileSystemName, path string) PathId {
return PathId{
AccountId: accountId,
FileSystemName: fileSystemName,
Path: path,
}
}

func (b PathId) ID() string {
return fmt.Sprintf("%s/%s/%s", b.AccountId.ID(), b.FileSystemName, b.Path)
}

func (b PathId) String() string {
components := []string{
fmt.Sprintf("File System %q", b.FileSystemName),
fmt.Sprintf("Account %q", b.AccountId.String()),
}
return fmt.Sprintf("Path %q (%s)", b.Path, strings.Join(components, " / "))
}

// ParsePathID parses `input` into a Path ID using a known `domainSuffix`
func ParsePathID(input, domainSuffix string) (*PathId, error) {
// example: https://foo.dfs.core.windows.net/Bar/some/path
if input == "" {
return nil, fmt.Errorf("`input` was empty")
}

account, err := accounts.ParseAccountID(input, domainSuffix)
if err != nil {
return nil, fmt.Errorf("parsing account %q: %+v", input, err)
}

if account.SubDomainType != accounts.DataLakeStoreSubDomainType {
return nil, fmt.Errorf("expected the subdomain type to be %q but got %q", string(accounts.DataLakeStoreSubDomainType), string(account.SubDomainType))
}

uri, err := url.Parse(input)
if err != nil {
return nil, fmt.Errorf("parsing %q as a uri: %+v", input, err)
}

uriPath := strings.TrimPrefix(uri.Path, "/")
segments := strings.Split(uriPath, "/")
if len(segments) < 2 {
return nil, fmt.Errorf("expected the path to contain at least 2 segments but got %d", len(segments))
}
fileSystemName := segments[0]
path := strings.Join(segments[1:], "/")
return &PathId{
AccountId: *account,
FileSystemName: fileSystemName,
Path: path,
}, nil
}
Loading

0 comments on commit fd7fe5d

Please sign in to comment.