forked from TrueCloudLab/frostfs-s3-gw
[#260] Use namespace as domain when create bucket
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
9ebfca654b
commit
ff1ec56d24
12 changed files with 87 additions and 37 deletions
15
api/cache/buckets.go
vendored
15
api/cache/buckets.go
vendored
|
@ -40,8 +40,7 @@ func NewBucketCache(config *Config) *BucketCache {
|
||||||
|
|
||||||
// Get returns a cached object.
|
// Get returns a cached object.
|
||||||
func (o *BucketCache) Get(ns, bktName string) *data.BucketInfo {
|
func (o *BucketCache) Get(ns, bktName string) *data.BucketInfo {
|
||||||
key := ns + "/" + bktName
|
entry, err := o.cache.Get(formKey(ns, bktName))
|
||||||
entry, err := o.cache.Get(key)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -57,11 +56,15 @@ func (o *BucketCache) Get(ns, bktName string) *data.BucketInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put puts an object to cache.
|
// Put puts an object to cache.
|
||||||
func (o *BucketCache) Put(ns string, bkt *data.BucketInfo) error {
|
func (o *BucketCache) Put(bkt *data.BucketInfo) error {
|
||||||
return o.cache.Set(ns+"/"+bkt.Name, bkt)
|
return o.cache.Set(formKey(bkt.Zone, bkt.Name), bkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes an object from cache.
|
// Delete deletes an object from cache.
|
||||||
func (o *BucketCache) Delete(ns, bktName string) bool {
|
func (o *BucketCache) Delete(bkt *data.BucketInfo) bool {
|
||||||
return o.cache.Remove(ns + "/" + bktName)
|
return o.cache.Remove(formKey(bkt.Zone, bkt.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func formKey(ns, name string) string {
|
||||||
|
return name + "." + ns
|
||||||
}
|
}
|
||||||
|
|
6
api/cache/cache_test.go
vendored
6
api/cache/cache_test.go
vendored
|
@ -36,15 +36,15 @@ func TestBucketsCacheType(t *testing.T) {
|
||||||
|
|
||||||
bktInfo := &data.BucketInfo{Name: "bucket"}
|
bktInfo := &data.BucketInfo{Name: "bucket"}
|
||||||
|
|
||||||
err := cache.Put("", bktInfo)
|
err := cache.Put(bktInfo)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
val := cache.Get("", bktInfo.Name)
|
val := cache.Get("", bktInfo.Name)
|
||||||
require.Equal(t, bktInfo, val)
|
require.Equal(t, bktInfo, val)
|
||||||
require.Equal(t, 0, observedLog.Len())
|
require.Equal(t, 0, observedLog.Len())
|
||||||
|
|
||||||
err = cache.cache.Set("/"+bktInfo.Name, "tmp")
|
err = cache.cache.Set(bktInfo.Name+"."+bktInfo.Zone, "tmp")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assertInvalidCacheEntry(t, cache.Get("", bktInfo.Name), observedLog)
|
assertInvalidCacheEntry(t, cache.Get(bktInfo.Zone, bktInfo.Name), observedLog)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObjectNamesCacheType(t *testing.T) {
|
func TestObjectNamesCacheType(t *testing.T) {
|
||||||
|
|
|
@ -748,7 +748,8 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
reqInfo := middleware.GetReqInfo(ctx)
|
reqInfo := middleware.GetReqInfo(ctx)
|
||||||
p := &layer.CreateBucketParams{
|
p := &layer.CreateBucketParams{
|
||||||
Name: reqInfo.BucketName,
|
Name: reqInfo.BucketName,
|
||||||
|
Namespace: reqInfo.Namespace,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkBucketName(reqInfo.BucketName); err != nil {
|
if err := checkBucketName(reqInfo.BucketName); err != nil {
|
||||||
|
|
|
@ -380,6 +380,26 @@ func TestCreateBucket(t *testing.T) {
|
||||||
createBucketAssertS3Error(hc, bktName, box2, s3errors.ErrBucketAlreadyExists)
|
createBucketAssertS3Error(hc, bktName, box2, s3errors.ErrBucketAlreadyExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateNamespacedBucket(t *testing.T) {
|
||||||
|
hc := prepareHandlerContext(t)
|
||||||
|
bktName := "bkt-name"
|
||||||
|
namespace := "yabloko"
|
||||||
|
|
||||||
|
box, _ := createAccessBox(t)
|
||||||
|
w, r := prepareTestRequest(hc, bktName, "", nil)
|
||||||
|
ctx := middleware.SetBoxData(r.Context(), box)
|
||||||
|
reqInfo := middleware.GetReqInfo(ctx)
|
||||||
|
reqInfo.Namespace = namespace
|
||||||
|
r = r.WithContext(middleware.SetReqInfo(ctx, reqInfo))
|
||||||
|
hc.Handler().CreateBucketHandler(w, r)
|
||||||
|
assertStatus(t, w, http.StatusOK)
|
||||||
|
|
||||||
|
bktInfo, err := hc.Layer().GetBucketInfo(middleware.SetReqInfo(hc.Context(), reqInfo), bktName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, namespace+".ns", bktInfo.Zone)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPutObjectClientCut(t *testing.T) {
|
func TestPutObjectClientCut(t *testing.T) {
|
||||||
hc := prepareHandlerContext(t)
|
hc := prepareHandlerContext(t)
|
||||||
bktName, objName1, objName2 := "bkt-name", "obj-name1", "obj-name2"
|
bktName, objName1, objName2 := "bkt-name", "obj-name1", "obj-name2"
|
||||||
|
|
|
@ -60,18 +60,18 @@ func (c *Cache) GetBucket(ns, name string) *data.BucketInfo {
|
||||||
return c.bucketCache.Get(ns, name)
|
return c.bucketCache.Get(ns, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) PutBucket(ns string, bktInfo *data.BucketInfo) {
|
func (c *Cache) PutBucket(bktInfo *data.BucketInfo) {
|
||||||
if err := c.bucketCache.Put(ns, bktInfo); err != nil {
|
if err := c.bucketCache.Put(bktInfo); err != nil {
|
||||||
c.logger.Warn(logs.CouldntPutBucketInfoIntoCache,
|
c.logger.Warn(logs.CouldntPutBucketInfoIntoCache,
|
||||||
zap.String("namespace", ns),
|
zap.String("zone", bktInfo.Zone),
|
||||||
zap.String("bucket name", bktInfo.Name),
|
zap.String("bucket name", bktInfo.Name),
|
||||||
zap.Stringer("bucket cid", bktInfo.CID),
|
zap.Stringer("bucket cid", bktInfo.CID),
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) DeleteBucket(ns, name string) {
|
func (c *Cache) DeleteBucket(bktInfo *data.BucketInfo) {
|
||||||
c.bucketCache.Delete(ns, name)
|
c.bucketCache.Delete(bktInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) CleanListCacheEntriesContainingObject(objectName string, cnrID cid.ID) {
|
func (c *Cache) CleanListCacheEntriesContainingObject(objectName string, cnrID cid.ID) {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||||
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||||
|
@ -75,7 +74,12 @@ func (n *layer) containerInfo(ctx context.Context, idCnr cid.ID) (*data.BucketIn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
n.cache.PutBucket(reqInfo.Namespace, info)
|
zone, _ := n.features.FormContainerZone(reqInfo.Namespace)
|
||||||
|
if zone != info.Zone {
|
||||||
|
return nil, fmt.Errorf("ns '%s' and zone '%s' are mismatched", zone, info.Zone)
|
||||||
|
}
|
||||||
|
|
||||||
|
n.cache.PutBucket(info)
|
||||||
|
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
@ -105,9 +109,12 @@ func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da
|
||||||
if p.LocationConstraint == "" {
|
if p.LocationConstraint == "" {
|
||||||
p.LocationConstraint = api.DefaultLocationConstraint // s3tests_boto3.functional.test_s3:test_bucket_get_location
|
p.LocationConstraint = api.DefaultLocationConstraint // s3tests_boto3.functional.test_s3:test_bucket_get_location
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zone, _ := n.features.FormContainerZone(p.Namespace)
|
||||||
|
|
||||||
bktInfo := &data.BucketInfo{
|
bktInfo := &data.BucketInfo{
|
||||||
Name: p.Name,
|
Name: p.Name,
|
||||||
Zone: v2container.SysAttributeZoneDefault,
|
Zone: zone,
|
||||||
Owner: n.BearerOwner(ctx),
|
Owner: n.BearerOwner(ctx),
|
||||||
Created: TimeNow(ctx),
|
Created: TimeNow(ctx),
|
||||||
LocationConstraint: p.LocationConstraint,
|
LocationConstraint: p.LocationConstraint,
|
||||||
|
@ -130,6 +137,7 @@ func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da
|
||||||
Creator: bktInfo.Owner,
|
Creator: bktInfo.Owner,
|
||||||
Policy: p.Policy,
|
Policy: p.Policy,
|
||||||
Name: p.Name,
|
Name: p.Name,
|
||||||
|
Zone: zone,
|
||||||
SessionToken: p.SessionContainerCreation,
|
SessionToken: p.SessionContainerCreation,
|
||||||
CreationTime: bktInfo.Created,
|
CreationTime: bktInfo.Created,
|
||||||
AdditionalAttributes: attributes,
|
AdditionalAttributes: attributes,
|
||||||
|
@ -145,7 +153,7 @@ func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da
|
||||||
return nil, fmt.Errorf("set container eacl: %w", err)
|
return nil, fmt.Errorf("set container eacl: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
n.cache.PutBucket(bktInfo.Zone, bktInfo)
|
n.cache.PutBucket(bktInfo)
|
||||||
|
|
||||||
return bktInfo, nil
|
return bktInfo, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,9 @@ type PrmContainerCreate struct {
|
||||||
// Name for the container.
|
// Name for the container.
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
|
// Zone for container registration.
|
||||||
|
Zone string
|
||||||
|
|
||||||
// CreationTime value for Timestamp attribute
|
// CreationTime value for Timestamp attribute
|
||||||
CreationTime time.Time
|
CreationTime time.Time
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
objectv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
objectv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||||
|
@ -50,6 +51,14 @@ func (k *FeatureSettingsMock) SetMD5Enabled(md5Enabled bool) {
|
||||||
k.md5Enabled = md5Enabled
|
k.md5Enabled = md5Enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k *FeatureSettingsMock) FormContainerZone(ns string) (zone string, isDefault bool) {
|
||||||
|
if ns == "" {
|
||||||
|
return v2container.SysAttributeZoneDefault, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return ns + ".ns", false
|
||||||
|
}
|
||||||
|
|
||||||
type TestFrostFS struct {
|
type TestFrostFS struct {
|
||||||
FrostFS
|
FrostFS
|
||||||
|
|
||||||
|
@ -143,6 +152,7 @@ func (t *TestFrostFS) CreateContainer(_ context.Context, prm PrmContainerCreate)
|
||||||
if prm.Name != "" {
|
if prm.Name != "" {
|
||||||
var d container.Domain
|
var d container.Domain
|
||||||
d.SetName(prm.Name)
|
d.SetName(prm.Name)
|
||||||
|
d.SetZone(prm.Zone)
|
||||||
|
|
||||||
container.WriteDomain(&cnr, d)
|
container.WriteDomain(&cnr, d)
|
||||||
container.SetName(&cnr, prm.Name)
|
container.SetName(&cnr, prm.Name)
|
||||||
|
|
|
@ -51,6 +51,7 @@ type (
|
||||||
ClientCut() bool
|
ClientCut() bool
|
||||||
BufferMaxSizeForPut() uint64
|
BufferMaxSizeForPut() uint64
|
||||||
MD5Enabled() bool
|
MD5Enabled() bool
|
||||||
|
FormContainerZone(ns string) (zone string, isDefault bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
layer struct {
|
layer struct {
|
||||||
|
@ -172,6 +173,7 @@ type (
|
||||||
// CreateBucketParams stores bucket create request parameters.
|
// CreateBucketParams stores bucket create request parameters.
|
||||||
CreateBucketParams struct {
|
CreateBucketParams struct {
|
||||||
Name string
|
Name string
|
||||||
|
Namespace string
|
||||||
Policy netmap.PlacementPolicy
|
Policy netmap.PlacementPolicy
|
||||||
EACL *eacl.Table
|
EACL *eacl.Table
|
||||||
SessionContainerCreation *session.Container
|
SessionContainerCreation *session.Container
|
||||||
|
@ -816,8 +818,6 @@ func (n *layer) DeleteBucket(ctx context.Context, p *DeleteBucketParams) error {
|
||||||
return errors.GetAPIError(errors.ErrBucketNotEmpty)
|
return errors.GetAPIError(errors.ErrBucketNotEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqInfo := middleware.GetReqInfo(ctx)
|
n.cache.DeleteBucket(p.BktInfo)
|
||||||
|
|
||||||
n.cache.DeleteBucket(reqInfo.Namespace, p.BktInfo.Name)
|
|
||||||
return n.frostFS.DeleteContainer(ctx, p.BktInfo.CID, p.SessionToken)
|
return n.frostFS.DeleteContainer(ctx, p.BktInfo.CID, p.SessionToken)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ns"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ns"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -18,8 +17,6 @@ const (
|
||||||
DNSResolver = "dns"
|
DNSResolver = "dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
const nsDomain = ".ns"
|
|
||||||
|
|
||||||
// ErrNoResolvers returns when trying to resolve container without any resolver.
|
// ErrNoResolvers returns when trying to resolve container without any resolver.
|
||||||
var ErrNoResolvers = errors.New("no resolvers")
|
var ErrNoResolvers = errors.New("no resolvers")
|
||||||
|
|
||||||
|
@ -33,7 +30,7 @@ type FrostFS interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Settings interface {
|
type Settings interface {
|
||||||
DefaultNamespaces() []string
|
FormContainerZone(ns string) (zone string, isDefault bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
@ -176,15 +173,16 @@ func NewDNSResolver(frostFS FrostFS, settings Settings) (*Resolver, error) {
|
||||||
resolveFunc := func(ctx context.Context, name string) (cid.ID, error) {
|
resolveFunc := func(ctx context.Context, name string) (cid.ID, error) {
|
||||||
var err error
|
var err error
|
||||||
reqInfo := middleware.GetReqInfo(ctx)
|
reqInfo := middleware.GetReqInfo(ctx)
|
||||||
domain := reqInfo.Namespace + nsDomain
|
|
||||||
if slices.Contains(settings.DefaultNamespaces(), domain) {
|
zone, isDefault := settings.FormContainerZone(reqInfo.Namespace)
|
||||||
domain, err = frostFS.SystemDNS(ctx)
|
if isDefault {
|
||||||
|
zone, err = frostFS.SystemDNS(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cid.ID{}, fmt.Errorf("read system DNS parameter of the FrostFS: %w", err)
|
return cid.ID{}, fmt.Errorf("read system DNS parameter of the FrostFS: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
domain = name + "." + domain
|
domain := name + "." + zone
|
||||||
cnrID, err := dns.ResolveContainerName(domain)
|
cnrID, err := dns.ResolveContainerName(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cid.ID{}, fmt.Errorf("couldn't resolve container '%s' as '%s': %w", name, domain, err)
|
return cid.ID{}, fmt.Errorf("couldn't resolve container '%s' as '%s': %w", name, domain, err)
|
||||||
|
@ -217,9 +215,8 @@ func NewNNSResolver(address string, settings Settings) (*Resolver, error) {
|
||||||
d.SetName(name)
|
d.SetName(name)
|
||||||
|
|
||||||
reqInfo := middleware.GetReqInfo(ctx)
|
reqInfo := middleware.GetReqInfo(ctx)
|
||||||
if !slices.Contains(settings.DefaultNamespaces(), reqInfo.Namespace) {
|
zone, _ := settings.FormContainerZone(reqInfo.Namespace)
|
||||||
d.SetZone(reqInfo.Namespace + nsDomain)
|
d.SetZone(zone)
|
||||||
}
|
|
||||||
|
|
||||||
cnrID, err := nns.ResolveContainerDomain(d)
|
cnrID, err := nns.ResolveContainerDomain(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
||||||
grpctracing "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
|
grpctracing "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||||
|
@ -41,6 +42,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -341,14 +343,19 @@ func (s *appSettings) setNamespaceHeader(nsHeader string) {
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *appSettings) DefaultNamespaces() []string {
|
func (s *appSettings) FormContainerZone(ns string) (zone string, isDefault bool) {
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
namespaces := s.defaultNamespaces
|
||||||
return s.defaultNamespaces
|
s.mu.RUnlock()
|
||||||
|
if slices.Contains(namespaces, ns) {
|
||||||
|
return v2container.SysAttributeZoneDefault, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return ns + ".ns", false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *appSettings) setDefaultNamespaces(namespaces []string) {
|
func (s *appSettings) setDefaultNamespaces(namespaces []string) {
|
||||||
for i := range namespaces { // to be set namespaces in evn variable as `S3_GW_KLUDGE_DEFAULT_NAMESPACES="" "root"`
|
for i := range namespaces { // to be set namespaces in env variable as `S3_GW_KLUDGE_DEFAULT_NAMESPACES="" "root"`
|
||||||
namespaces[i] = strings.Trim(namespaces[i], "\"")
|
namespaces[i] = strings.Trim(namespaces[i], "\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,7 @@ func (x *FrostFS) CreateContainer(ctx context.Context, prm layer.PrmContainerCre
|
||||||
if prm.Name != "" {
|
if prm.Name != "" {
|
||||||
var d container.Domain
|
var d container.Domain
|
||||||
d.SetName(prm.Name)
|
d.SetName(prm.Name)
|
||||||
|
d.SetZone(prm.Zone)
|
||||||
|
|
||||||
container.WriteDomain(&cnr, d)
|
container.WriteDomain(&cnr, d)
|
||||||
container.SetName(&cnr, prm.Name)
|
container.SetName(&cnr, prm.Name)
|
||||||
|
|
Loading…
Reference in a new issue