@ -69,7 +69,7 @@ func TestIngressPGReconciler(t *testing.T) {
expectReconciled ( t , ingPGR , "default" , "test-ingress" )
expectReconciled ( t , ingPGR , "default" , "test-ingress" )
verifyServeConfig ( t , fc , "svc:my-svc" , false )
verifyServeConfig ( t , fc , "svc:my-svc" , false )
verifyTailscaleService ( t , ft , "svc:my-svc" , [ ] string { "tcp:443" } )
verifyTailscaleService ( t , ft , "svc:my-svc" , [ ] string { "tcp:443" } )
verifyTailscaledConfig ( t , fc , [ ] string { "svc:my-svc" } )
verifyTailscaledConfig ( t , fc , "test-pg" , [ ] string { "svc:my-svc" } )
// Verify that Role and RoleBinding have been created for the first Ingress.
// Verify that Role and RoleBinding have been created for the first Ingress.
// Do not verify the cert Secret as that was already verified implicitly above.
// Do not verify the cert Secret as that was already verified implicitly above.
@ -132,7 +132,7 @@ func TestIngressPGReconciler(t *testing.T) {
verifyServeConfig ( t , fc , "svc:my-other-svc" , false )
verifyServeConfig ( t , fc , "svc:my-other-svc" , false )
verifyTailscaleService ( t , ft , "svc:my-other-svc" , [ ] string { "tcp:443" } )
verifyTailscaleService ( t , ft , "svc:my-other-svc" , [ ] string { "tcp:443" } )
// Verify that Role and RoleBinding have been created for the first Ingress.
// Verify that Role and RoleBinding have been created for the second Ingress.
// Do not verify the cert Secret as that was already verified implicitly above.
// Do not verify the cert Secret as that was already verified implicitly above.
expectEqual ( t , fc , certSecretRole ( "test-pg" , "operator-ns" , "my-other-svc.ts.net" ) )
expectEqual ( t , fc , certSecretRole ( "test-pg" , "operator-ns" , "my-other-svc.ts.net" ) )
expectEqual ( t , fc , certSecretRoleBinding ( "test-pg" , "operator-ns" , "my-other-svc.ts.net" ) )
expectEqual ( t , fc , certSecretRoleBinding ( "test-pg" , "operator-ns" , "my-other-svc.ts.net" ) )
@ -141,7 +141,7 @@ func TestIngressPGReconciler(t *testing.T) {
verifyServeConfig ( t , fc , "svc:my-svc" , false )
verifyServeConfig ( t , fc , "svc:my-svc" , false )
verifyTailscaleService ( t , ft , "svc:my-svc" , [ ] string { "tcp:443" } )
verifyTailscaleService ( t , ft , "svc:my-svc" , [ ] string { "tcp:443" } )
verifyTailscaledConfig ( t , fc , [ ] string { "svc:my-svc" , "svc:my-other-svc" } )
verifyTailscaledConfig ( t , fc , "test-pg" , [ ] string { "svc:my-svc" , "svc:my-other-svc" } )
// Delete second Ingress
// Delete second Ingress
if err := fc . Delete ( context . Background ( ) , ing2 ) ; err != nil {
if err := fc . Delete ( context . Background ( ) , ing2 ) ; err != nil {
@ -172,11 +172,20 @@ func TestIngressPGReconciler(t *testing.T) {
t . Error ( "second Ingress service config was not cleaned up" )
t . Error ( "second Ingress service config was not cleaned up" )
}
}
verifyTailscaledConfig ( t , fc , [ ] string { "svc:my-svc" } )
verifyTailscaledConfig ( t , fc , "test-pg" , [ ] string { "svc:my-svc" } )
expectMissing [ corev1 . Secret ] ( t , fc , "operator-ns" , "my-other-svc.ts.net" )
expectMissing [ corev1 . Secret ] ( t , fc , "operator-ns" , "my-other-svc.ts.net" )
expectMissing [ rbacv1 . Role ] ( t , fc , "operator-ns" , "my-other-svc.ts.net" )
expectMissing [ rbacv1 . Role ] ( t , fc , "operator-ns" , "my-other-svc.ts.net" )
expectMissing [ rbacv1 . RoleBinding ] ( t , fc , "operator-ns" , "my-other-svc.ts.net" )
expectMissing [ rbacv1 . RoleBinding ] ( t , fc , "operator-ns" , "my-other-svc.ts.net" )
// Test Ingress ProxyGroup change
createPGResources ( t , fc , "test-pg-second" )
mustUpdate ( t , fc , "default" , "test-ingress" , func ( ing * networkingv1 . Ingress ) {
ing . Annotations [ "tailscale.com/proxy-group" ] = "test-pg-second"
} )
expectReconciled ( t , ingPGR , "default" , "test-ingress" )
expectEqual ( t , fc , certSecretRole ( "test-pg-second" , "operator-ns" , "my-svc.ts.net" ) )
expectEqual ( t , fc , certSecretRoleBinding ( "test-pg-second" , "operator-ns" , "my-svc.ts.net" ) )
// Delete the first Ingress and verify cleanup
// Delete the first Ingress and verify cleanup
if err := fc . Delete ( context . Background ( ) , ing ) ; err != nil {
if err := fc . Delete ( context . Background ( ) , ing ) ; err != nil {
t . Fatalf ( "deleting Ingress: %v" , err )
t . Fatalf ( "deleting Ingress: %v" , err )
@ -187,7 +196,7 @@ func TestIngressPGReconciler(t *testing.T) {
// Verify the ConfigMap was cleaned up
// Verify the ConfigMap was cleaned up
cm = & corev1 . ConfigMap { }
cm = & corev1 . ConfigMap { }
if err := fc . Get ( context . Background ( ) , types . NamespacedName {
if err := fc . Get ( context . Background ( ) , types . NamespacedName {
Name : "test-pg- ingress-config",
Name : "test-pg- second- ingress-config",
Namespace : "operator-ns" ,
Namespace : "operator-ns" ,
} , cm ) ; err != nil {
} , cm ) ; err != nil {
t . Fatalf ( "getting ConfigMap: %v" , err )
t . Fatalf ( "getting ConfigMap: %v" , err )
@ -201,7 +210,7 @@ func TestIngressPGReconciler(t *testing.T) {
if len ( cfg . Services ) > 0 {
if len ( cfg . Services ) > 0 {
t . Error ( "serve config not cleaned up" )
t . Error ( "serve config not cleaned up" )
}
}
verifyTailscaledConfig ( t , fc , nil )
verifyTailscaledConfig ( t , fc , "test-pg-second" , nil )
// Add verification that cert resources were cleaned up
// Add verification that cert resources were cleaned up
expectMissing [ corev1 . Secret ] ( t , fc , "operator-ns" , "my-svc.ts.net" )
expectMissing [ corev1 . Secret ] ( t , fc , "operator-ns" , "my-svc.ts.net" )
@ -245,7 +254,7 @@ func TestIngressPGReconciler_UpdateIngressHostname(t *testing.T) {
expectReconciled ( t , ingPGR , "default" , "test-ingress" )
expectReconciled ( t , ingPGR , "default" , "test-ingress" )
verifyServeConfig ( t , fc , "svc:my-svc" , false )
verifyServeConfig ( t , fc , "svc:my-svc" , false )
verifyTailscaleService ( t , ft , "svc:my-svc" , [ ] string { "tcp:443" } )
verifyTailscaleService ( t , ft , "svc:my-svc" , [ ] string { "tcp:443" } )
verifyTailscaledConfig ( t , fc , [ ] string { "svc:my-svc" } )
verifyTailscaledConfig ( t , fc , "test-pg" , [ ] string { "svc:my-svc" } )
// Update the Ingress hostname and make sure the original Tailscale Service is deleted.
// Update the Ingress hostname and make sure the original Tailscale Service is deleted.
mustUpdate ( t , fc , "default" , "test-ingress" , func ( ing * networkingv1 . Ingress ) {
mustUpdate ( t , fc , "default" , "test-ingress" , func ( ing * networkingv1 . Ingress ) {
@ -256,7 +265,7 @@ func TestIngressPGReconciler_UpdateIngressHostname(t *testing.T) {
expectReconciled ( t , ingPGR , "default" , "test-ingress" )
expectReconciled ( t , ingPGR , "default" , "test-ingress" )
verifyServeConfig ( t , fc , "svc:updated-svc" , false )
verifyServeConfig ( t , fc , "svc:updated-svc" , false )
verifyTailscaleService ( t , ft , "svc:updated-svc" , [ ] string { "tcp:443" } )
verifyTailscaleService ( t , ft , "svc:updated-svc" , [ ] string { "tcp:443" } )
verifyTailscaledConfig ( t , fc , [ ] string { "svc:updated-svc" } )
verifyTailscaledConfig ( t , fc , "test-pg" , [ ] string { "svc:updated-svc" } )
_ , err := ft . GetVIPService ( context . Background ( ) , tailcfg . ServiceName ( "svc:my-svc" ) )
_ , err := ft . GetVIPService ( context . Background ( ) , tailcfg . ServiceName ( "svc:my-svc" ) )
if err == nil {
if err == nil {
@ -550,6 +559,117 @@ func TestIngressPGReconciler_HTTPEndpoint(t *testing.T) {
}
}
}
}
func TestIngressPGReconciler_MultiCluster ( t * testing . T ) {
ingPGR , fc , ft := setupIngressTest ( t )
ingPGR . operatorID = "operator-1"
// Create initial Ingress
ing := & networkingv1 . Ingress {
TypeMeta : metav1 . TypeMeta { Kind : "Ingress" , APIVersion : "networking.k8s.io/v1" } ,
ObjectMeta : metav1 . ObjectMeta {
Name : "test-ingress" ,
Namespace : "default" ,
UID : types . UID ( "1234-UID" ) ,
Annotations : map [ string ] string {
"tailscale.com/proxy-group" : "test-pg" ,
} ,
} ,
Spec : networkingv1 . IngressSpec {
IngressClassName : ptr . To ( "tailscale" ) ,
TLS : [ ] networkingv1 . IngressTLS {
{ Hosts : [ ] string { "my-svc" } } ,
} ,
} ,
}
mustCreate ( t , fc , ing )
// Simulate existing Tailscale Service from another cluster
existingVIPSvc := & tailscale . VIPService {
Name : "svc:my-svc" ,
Annotations : map [ string ] string {
ownerAnnotation : ` { "ownerrefs":[ { "operatorID":"operator-2"}]} ` ,
} ,
}
ft . vipServices = map [ tailcfg . ServiceName ] * tailscale . VIPService {
"svc:my-svc" : existingVIPSvc ,
}
// Verify reconciliation adds our operator reference
expectReconciled ( t , ingPGR , "default" , "test-ingress" )
tsSvc , err := ft . GetVIPService ( context . Background ( ) , "svc:my-svc" )
if err != nil {
t . Fatalf ( "getting Tailscale Service: %v" , err )
}
if tsSvc == nil {
t . Fatal ( "Tailscale Service not found" )
}
o , err := parseOwnerAnnotation ( tsSvc )
if err != nil {
t . Fatalf ( "parsing owner annotation: %v" , err )
}
wantOwnerRefs := [ ] OwnerRef {
{ OperatorID : "operator-2" } ,
{ OperatorID : "operator-1" } ,
}
if ! reflect . DeepEqual ( o . OwnerRefs , wantOwnerRefs ) {
t . Errorf ( "incorrect owner refs\ngot: %+v\nwant: %+v" , o . OwnerRefs , wantOwnerRefs )
}
// Delete the Ingress and verify Tailscale Service still exists with one owner ref
if err := fc . Delete ( context . Background ( ) , ing ) ; err != nil {
t . Fatalf ( "deleting Ingress: %v" , err )
}
expectRequeue ( t , ingPGR , "default" , "test-ingress" )
tsSvc , err = ft . GetVIPService ( context . Background ( ) , "svc:my-svc" )
if err != nil {
t . Fatalf ( "getting Tailscale Service after deletion: %v" , err )
}
if tsSvc == nil {
t . Fatal ( "Tailscale Service was incorrectly deleted" )
}
o , err = parseOwnerAnnotation ( tsSvc )
if err != nil {
t . Fatalf ( "parsing owner annotation: %v" , err )
}
wantOwnerRefs = [ ] OwnerRef {
{ OperatorID : "operator-2" } ,
}
if ! reflect . DeepEqual ( o . OwnerRefs , wantOwnerRefs ) {
t . Errorf ( "incorrect owner refs after deletion\ngot: %+v\nwant: %+v" , o . OwnerRefs , wantOwnerRefs )
}
}
func populateTLSSecret ( ctx context . Context , c client . Client , pgName , domain string ) error {
secret := & corev1 . Secret {
ObjectMeta : metav1 . ObjectMeta {
Name : domain ,
Namespace : "operator-ns" ,
Labels : map [ string ] string {
kubetypes . LabelManaged : "true" ,
labelProxyGroup : pgName ,
labelDomain : domain ,
kubetypes . LabelSecretType : "certs" ,
} ,
} ,
Type : corev1 . SecretTypeTLS ,
Data : map [ string ] [ ] byte {
corev1 . TLSCertKey : [ ] byte ( "fake-cert" ) ,
corev1 . TLSPrivateKeyKey : [ ] byte ( "fake-key" ) ,
} ,
}
_ , err := createOrUpdate ( ctx , c , "operator-ns" , secret , func ( s * corev1 . Secret ) {
s . Data = secret . Data
} )
return err
}
func verifyTailscaleService ( t * testing . T , ft * fakeTSClient , serviceName string , wantPorts [ ] string ) {
func verifyTailscaleService ( t * testing . T , ft * fakeTSClient , serviceName string , wantPorts [ ] string ) {
t . Helper ( )
t . Helper ( )
tsSvc , err := ft . GetVIPService ( context . Background ( ) , tailcfg . ServiceName ( serviceName ) )
tsSvc , err := ft . GetVIPService ( context . Background ( ) , tailcfg . ServiceName ( serviceName ) )
@ -618,7 +738,7 @@ func verifyServeConfig(t *testing.T, fc client.Client, serviceName string, wantH
}
}
}
}
func verifyTailscaledConfig ( t * testing . T , fc client . Client , expectedServices [ ] string ) {
func verifyTailscaledConfig ( t * testing . T , fc client . Client , pgName string , expectedServices [ ] string ) {
t . Helper ( )
t . Helper ( )
var expected string
var expected string
if expectedServices != nil && len ( expectedServices ) > 0 {
if expectedServices != nil && len ( expectedServices ) > 0 {
@ -630,9 +750,9 @@ func verifyTailscaledConfig(t *testing.T, fc client.Client, expectedServices []s
}
}
expectEqual ( t , fc , & corev1 . Secret {
expectEqual ( t , fc , & corev1 . Secret {
ObjectMeta : metav1 . ObjectMeta {
ObjectMeta : metav1 . ObjectMeta {
Name : pgConfigSecretName ( "test-pg" , 0 ) ,
Name : pgConfigSecretName ( pgName , 0 ) ,
Namespace : "operator-ns" ,
Namespace : "operator-ns" ,
Labels : pgSecretLabels ( "test-pg" , "config" ) ,
Labels : pgSecretLabels ( pgName , "config" ) ,
} ,
} ,
Data : map [ string ] [ ] byte {
Data : map [ string ] [ ] byte {
tsoperator . TailscaledConfigFileName ( 106 ) : [ ] byte ( fmt . Sprintf ( ` { "Version":""%s} ` , expected ) ) ,
tsoperator . TailscaledConfigFileName ( 106 ) : [ ] byte ( fmt . Sprintf ( ` { "Version":""%s} ` , expected ) ) ,
@ -640,53 +760,44 @@ func verifyTailscaledConfig(t *testing.T, fc client.Client, expectedServices []s
} )
} )
}
}
func setupIngressTest ( t * testing . T ) ( * HAIngressReconciler , client . Client , * fakeTSClient ) {
func createPGResources ( t * testing . T , fc client . Client , pgName string ) {
tsIngressClass := & networkingv1 . IngressClass {
t . Helper ( )
ObjectMeta : metav1 . ObjectMeta { Name : "tailscale" } ,
Spec : networkingv1 . IngressClassSpec { Controller : "tailscale.com/ts-ingress" } ,
}
// Pre-create the ProxyGroup
// Pre-create the ProxyGroup
pg := & tsapi . ProxyGroup {
pg := & tsapi . ProxyGroup {
ObjectMeta : metav1 . ObjectMeta {
ObjectMeta : metav1 . ObjectMeta {
Name : "test-pg" ,
Name : pgName ,
Generation : 1 ,
Generation : 1 ,
} ,
} ,
Spec : tsapi . ProxyGroupSpec {
Spec : tsapi . ProxyGroupSpec {
Type : tsapi . ProxyGroupTypeIngress ,
Type : tsapi . ProxyGroupTypeIngress ,
} ,
} ,
}
}
mustCreate ( t , fc , pg )
// Pre-create the ConfigMap for the ProxyGroup
// Pre-create the ConfigMap for the ProxyGroup
pgConfigMap := & corev1 . ConfigMap {
pgConfigMap := & corev1 . ConfigMap {
ObjectMeta : metav1 . ObjectMeta {
ObjectMeta : metav1 . ObjectMeta {
Name : "test-pg-ingress-config" ,
Name : fmt . Sprintf ( "%s-ingress-config" , pgName ) ,
Namespace : "operator-ns" ,
Namespace : "operator-ns" ,
} ,
} ,
BinaryData : map [ string ] [ ] byte {
BinaryData : map [ string ] [ ] byte {
"serve-config.json" : [ ] byte ( ` { "Services": { }} ` ) ,
"serve-config.json" : [ ] byte ( ` { "Services": { }} ` ) ,
} ,
} ,
}
}
mustCreate ( t , fc , pgConfigMap )
// Pre-create a config Secret for the ProxyGroup
// Pre-create a config Secret for the ProxyGroup
pgCfgSecret := & corev1 . Secret {
pgCfgSecret := & corev1 . Secret {
ObjectMeta : metav1 . ObjectMeta {
ObjectMeta : metav1 . ObjectMeta {
Name : pgConfigSecretName ( "test-pg" , 0 ) ,
Name : pgConfigSecretName ( pgName , 0 ) ,
Namespace : "operator-ns" ,
Namespace : "operator-ns" ,
Labels : pgSecretLabels ( "test-pg" , "config" ) ,
Labels : pgSecretLabels ( pgName , "config" ) ,
} ,
} ,
Data : map [ string ] [ ] byte {
Data : map [ string ] [ ] byte {
tsoperator . TailscaledConfigFileName ( 106 ) : [ ] byte ( "{}" ) ,
tsoperator . TailscaledConfigFileName ( 106 ) : [ ] byte ( "{}" ) ,
} ,
} ,
}
}
mustCreate ( t , fc , pgCfgSecret )
fc := fake . NewClientBuilder ( ) .
WithScheme ( tsapi . GlobalScheme ) .
WithObjects ( pg , pgCfgSecret , pgConfigMap , tsIngressClass ) .
WithStatusSubresource ( pg ) .
Build ( )
// Set ProxyGroup status to ready
pg . Status . Conditions = [ ] metav1 . Condition {
pg . Status . Conditions = [ ] metav1 . Condition {
{
{
Type : string ( tsapi . ProxyGroupReady ) ,
Type : string ( tsapi . ProxyGroupReady ) ,
@ -697,6 +808,22 @@ func setupIngressTest(t *testing.T) (*HAIngressReconciler, client.Client, *fakeT
if err := fc . Status ( ) . Update ( context . Background ( ) , pg ) ; err != nil {
if err := fc . Status ( ) . Update ( context . Background ( ) , pg ) ; err != nil {
t . Fatal ( err )
t . Fatal ( err )
}
}
}
func setupIngressTest ( t * testing . T ) ( * HAIngressReconciler , client . Client , * fakeTSClient ) {
tsIngressClass := & networkingv1 . IngressClass {
ObjectMeta : metav1 . ObjectMeta { Name : "tailscale" } ,
Spec : networkingv1 . IngressClassSpec { Controller : "tailscale.com/ts-ingress" } ,
}
fc := fake . NewClientBuilder ( ) .
WithScheme ( tsapi . GlobalScheme ) .
WithObjects ( tsIngressClass ) .
WithStatusSubresource ( & tsapi . ProxyGroup { } ) .
Build ( )
createPGResources ( t , fc , "test-pg" )
fakeTsnetServer := & fakeTSNetServer { certDomains : [ ] string { "foo.com" } }
fakeTsnetServer := & fakeTSNetServer { certDomains : [ ] string { "foo.com" } }
ft := & fakeTSClient { }
ft := & fakeTSClient { }
@ -726,114 +853,3 @@ func setupIngressTest(t *testing.T) (*HAIngressReconciler, client.Client, *fakeT
return ingPGR , fc , ft
return ingPGR , fc , ft
}
}
func TestIngressPGReconciler_MultiCluster ( t * testing . T ) {
ingPGR , fc , ft := setupIngressTest ( t )
ingPGR . operatorID = "operator-1"
// Create initial Ingress
ing := & networkingv1 . Ingress {
TypeMeta : metav1 . TypeMeta { Kind : "Ingress" , APIVersion : "networking.k8s.io/v1" } ,
ObjectMeta : metav1 . ObjectMeta {
Name : "test-ingress" ,
Namespace : "default" ,
UID : types . UID ( "1234-UID" ) ,
Annotations : map [ string ] string {
"tailscale.com/proxy-group" : "test-pg" ,
} ,
} ,
Spec : networkingv1 . IngressSpec {
IngressClassName : ptr . To ( "tailscale" ) ,
TLS : [ ] networkingv1 . IngressTLS {
{ Hosts : [ ] string { "my-svc" } } ,
} ,
} ,
}
mustCreate ( t , fc , ing )
// Simulate existing Tailscale Service from another cluster
existingVIPSvc := & tailscale . VIPService {
Name : "svc:my-svc" ,
Annotations : map [ string ] string {
ownerAnnotation : ` { "ownerrefs":[ { "operatorID":"operator-2"}]} ` ,
} ,
}
ft . vipServices = map [ tailcfg . ServiceName ] * tailscale . VIPService {
"svc:my-svc" : existingVIPSvc ,
}
// Verify reconciliation adds our operator reference
expectReconciled ( t , ingPGR , "default" , "test-ingress" )
tsSvc , err := ft . GetVIPService ( context . Background ( ) , "svc:my-svc" )
if err != nil {
t . Fatalf ( "getting Tailscale Service: %v" , err )
}
if tsSvc == nil {
t . Fatal ( "Tailscale Service not found" )
}
o , err := parseOwnerAnnotation ( tsSvc )
if err != nil {
t . Fatalf ( "parsing owner annotation: %v" , err )
}
wantOwnerRefs := [ ] OwnerRef {
{ OperatorID : "operator-2" } ,
{ OperatorID : "operator-1" } ,
}
if ! reflect . DeepEqual ( o . OwnerRefs , wantOwnerRefs ) {
t . Errorf ( "incorrect owner refs\ngot: %+v\nwant: %+v" , o . OwnerRefs , wantOwnerRefs )
}
// Delete the Ingress and verify Tailscale Service still exists with one owner ref
if err := fc . Delete ( context . Background ( ) , ing ) ; err != nil {
t . Fatalf ( "deleting Ingress: %v" , err )
}
expectRequeue ( t , ingPGR , "default" , "test-ingress" )
tsSvc , err = ft . GetVIPService ( context . Background ( ) , "svc:my-svc" )
if err != nil {
t . Fatalf ( "getting Tailscale Service after deletion: %v" , err )
}
if tsSvc == nil {
t . Fatal ( "Tailscale Service was incorrectly deleted" )
}
o , err = parseOwnerAnnotation ( tsSvc )
if err != nil {
t . Fatalf ( "parsing owner annotation: %v" , err )
}
wantOwnerRefs = [ ] OwnerRef {
{ OperatorID : "operator-2" } ,
}
if ! reflect . DeepEqual ( o . OwnerRefs , wantOwnerRefs ) {
t . Errorf ( "incorrect owner refs after deletion\ngot: %+v\nwant: %+v" , o . OwnerRefs , wantOwnerRefs )
}
}
func populateTLSSecret ( ctx context . Context , c client . Client , pgName , domain string ) error {
secret := & corev1 . Secret {
ObjectMeta : metav1 . ObjectMeta {
Name : domain ,
Namespace : "operator-ns" ,
Labels : map [ string ] string {
kubetypes . LabelManaged : "true" ,
labelProxyGroup : pgName ,
labelDomain : domain ,
kubetypes . LabelSecretType : "certs" ,
} ,
} ,
Type : corev1 . SecretTypeTLS ,
Data : map [ string ] [ ] byte {
corev1 . TLSCertKey : [ ] byte ( "fake-cert" ) ,
corev1 . TLSPrivateKeyKey : [ ] byte ( "fake-key" ) ,
} ,
}
_ , err := createOrUpdate ( ctx , c , "operator-ns" , secret , func ( s * corev1 . Secret ) {
s . Data = secret . Data
} )
return err
}