Skip to content

Commit

Permalink
Update doc for workload sync
Browse files Browse the repository at this point in the history
remove auto-generated apibinding
add a validation on sync command to avoid setting imported-apis
in --api-export flag

Signed-off-by: Jian Qiu <jqiu@redhat.com>
  • Loading branch information
qiujian16 committed Mar 28, 2023
1 parent 7644e05 commit e584b2f
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,17 @@ kubectl kcp workload sync <mycluster> --syncer-image <image name> --resources ro
```

And apply the generated manifests to the physical cluster. The syncer will then import the API schema of foo.bar
to the workspace of the synctarget, following up with an auto generated kubernetes APIExport/APIBinding in the same workspace.
You can then create foo.bar in this workspace, or create an APIBinding in another workspace to bind this APIExport.
to the workspace of the synctarget, following up with an auto generated kubernetes APIExport in the same workspace.
The auto generated APIExport name is `imported-apis`, and you can then create an APIBinding to bind this APIExport.

To sync resource from another existing APIExport in the KCP server, run

```sh
kubectl kcp workload sync <mycluster> --syncer-image <image name> --apiexports another-workspace:another-apiexport -o syncer.yaml
```

Syncer will start syncing the resources in this `APIExport` as long as the `SyncTarget` has compatible API schemas.
Syncer will start syncing the resources in this `APIExport` as long as the `SyncTarget` has compatible API schemas. The auto generated
APIExport `imported-apis` is a reserved APIExport name and should not be set in the `--apiexports`

To see if a certain resource is supported to be synced by the syncer, you can check the state of the `syncedResources` in `SyncTarget`
status.
Expand All @@ -118,8 +119,13 @@ After the `SyncTarget` is ready, switch to any workspace containing some workloa
kubectl kcp bind compute <workspace of synctarget>
```

This command will create a `Placement` in the workspace. By default, it will also create `APIBinding`s for global kubernetes `APIExport` and
kubernetes `APIExport` in workspace of `SyncTarget`, if any of these `APIExport`s are supported by the `SyncTarget`.
This command will create a `Placement` in the workspace. By default, it will also create `APIBinding`s for global kubernetes `APIExport`.

You can also bind to the auto generated `imported-apis` APIExport by running

```sh
kubectl kcp bind compute <workspace of synctarget> --apiexports <workspace of synctarget>:imported-apis
```

Alternatively, if you would like to bind other `APIExport`s which are supported by the `SyncerTarget`, run:

Expand Down
13 changes: 8 additions & 5 deletions pkg/cliplugins/workload/plugin/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,13 @@ func (o *SyncOptions) Validate() error {
}
}

for _, apiExport := range o.APIExports {
_, name := logicalcluster.NewPath(apiExport).Split()
if name == workloadv1alpha1.ImportedAPISExportName {
errs = append(errs, fmt.Errorf("%s is a reversed APIExport name and should not be set", workloadv1alpha1.ImportedAPISExportName))
}
}

return utilerrors.NewAggregate(errs)
}

Expand Down Expand Up @@ -458,12 +465,8 @@ func (o *SyncOptions) getResourcesForPermission(ctx context.Context, config *res
}

// skip if there is only the local kubernetes APIExport in the synctarget workspace, since we may not get syncedResources yet.
clusterName := logicalcluster.From(syncTarget)

if len(syncTarget.Spec.SupportedAPIExports) == 1 &&
syncTarget.Spec.SupportedAPIExports[0].Export == workloadv1alpha1.ImportedAPISExportName &&
(len(syncTarget.Spec.SupportedAPIExports[0].Path) == 0 ||
syncTarget.Spec.SupportedAPIExports[0].Path == clusterName.String()) {
syncTarget.Spec.SupportedAPIExports[0].Export == workloadv1alpha1.ImportedAPISExportName {
return true, nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,9 @@ import (

apisv1alpha1 "github.com/kcp-dev/kcp/pkg/apis/apis/v1alpha1"
schedulingv1alpha1 "github.com/kcp-dev/kcp/pkg/apis/scheduling/v1alpha1"
workloadv1alpha1 "github.com/kcp-dev/kcp/pkg/apis/workload/v1alpha1"
kcpclientset "github.com/kcp-dev/kcp/pkg/client/clientset/versioned/cluster"
apisv1alpha1informers "github.com/kcp-dev/kcp/pkg/client/informers/externalversions/apis/v1alpha1"
schedulingv1alpha1informers "github.com/kcp-dev/kcp/pkg/client/informers/externalversions/scheduling/v1alpha1"
workloadv1alpha1informers "github.com/kcp-dev/kcp/pkg/client/informers/externalversions/workload/v1alpha1"
apisv1alpha1listers "github.com/kcp-dev/kcp/pkg/client/listers/apis/v1alpha1"
schedulingv1alpha1listers "github.com/kcp-dev/kcp/pkg/client/listers/scheduling/v1alpha1"
workloadv1alpha1listers "github.com/kcp-dev/kcp/pkg/client/listers/workload/v1alpha1"
"github.com/kcp-dev/kcp/pkg/logging"
Expand All @@ -56,8 +53,6 @@ const (
func NewController(
kcpClusterClient kcpclientset.ClusterInterface,
syncTargetInformer workloadv1alpha1informers.SyncTargetClusterInformer,
apiExportInformer apisv1alpha1informers.APIExportClusterInformer,
apiBindingInformer apisv1alpha1informers.APIBindingClusterInformer,
locationInformer schedulingv1alpha1informers.LocationClusterInformer,
) (*controller, error) {
queue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), ControllerName)
Expand All @@ -74,31 +69,10 @@ func NewController(
},

kcpClusterClient: kcpClusterClient,
apiBindingLister: apiBindingInformer.Lister(),
apiExportsLister: apiExportInformer.Lister(),
syncTargetLister: syncTargetInformer.Lister(),
locationLister: locationInformer.Lister(),
}

apiExportInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{
FilterFunc: func(obj interface{}) bool {
switch t := obj.(type) {
case *apisv1alpha1.APIExport:
return t.Name == workloadv1alpha1.ImportedAPISExportName
}
return false
},
Handler: cache.ResourceEventHandlerFuncs{
DeleteFunc: func(obj interface{}) { c.enqueue(obj) },
},
})

apiBindingInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) { c.enqueue(obj) },
UpdateFunc: func(_, obj interface{}) { c.enqueue(obj) },
DeleteFunc: func(obj interface{}) { c.enqueue(obj) },
})

syncTargetInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) { c.enqueue(obj) },
DeleteFunc: func(obj interface{}) { c.enqueue(obj) },
Expand Down Expand Up @@ -129,8 +103,6 @@ type controller struct {
kcpClusterClient kcpclientset.ClusterInterface

syncTargetLister workloadv1alpha1listers.SyncTargetClusterLister
apiExportsLister apisv1alpha1listers.APIExportClusterLister
apiBindingLister apisv1alpha1listers.APIBindingClusterLister
locationLister schedulingv1alpha1listers.LocationClusterLister
}

Expand Down Expand Up @@ -245,60 +217,5 @@ func (c *controller) process(ctx context.Context, key string) error {
}
}

// check that export exists, and create APIBinding if it is not.
_, err = c.apiExportsLister.Cluster(clusterName).Get(workloadv1alpha1.ImportedAPISExportName)
if apierrors.IsNotFound(err) {
return nil
}
if err != nil {
return err
}

// check that binding exists, and create it if not
bindings, err := c.apiBindingLister.Cluster(clusterName).List(labels.Everything())
if err != nil {
logger.Error(err, "failed to list APIBindings")
return err
}
for _, binding := range bindings {
if binding.Spec.Reference.Export == nil {
continue
}
if binding.Spec.Reference.Export.Path != "" {
continue
}
if binding.Spec.Reference.Export.Name != workloadv1alpha1.ImportedAPISExportName {
continue
}
logging.WithObject(logger, binding).V(3).Info("APIBinding found pointing to APIExport")
return nil // binding found
}

// bind to local export
binding := &apisv1alpha1.APIBinding{
ObjectMeta: metav1.ObjectMeta{
Name: workloadv1alpha1.ImportedAPISExportName,
Annotations: map[string]string{
logicalcluster.AnnotationKey: clusterName.String(),
workloadv1alpha1.ComputeAPIExportAnnotationKey: "true",
},
},
Spec: apisv1alpha1.APIBindingSpec{
Reference: apisv1alpha1.BindingReference{
Export: &apisv1alpha1.ExportBindingReference{
Name: workloadv1alpha1.ImportedAPISExportName,
},
},
},
}
logger = logging.WithObject(logger, binding)
logger.V(2).Info("creating APIBinding")
_, err = c.kcpClusterClient.Cluster(clusterName.Path()).ApisV1alpha1().APIBindings().Create(ctx, binding, metav1.CreateOptions{})

if err != nil && !apierrors.IsAlreadyExists(err) {
logger.Error(err, "failed to create APIBinding")
return err
}

return nil
}
11 changes: 5 additions & 6 deletions test/e2e/syncer/syncer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ import (
"k8s.io/client-go/rest"
"k8s.io/utils/pointer"

apisv1alpha1 "github.com/kcp-dev/kcp/pkg/apis/apis/v1alpha1"
workloadv1alpha1 "github.com/kcp-dev/kcp/pkg/apis/workload/v1alpha1"
kcpclientset "github.com/kcp-dev/kcp/pkg/client/clientset/versioned/cluster"
"github.com/kcp-dev/kcp/pkg/syncer/shared"
Expand Down Expand Up @@ -93,17 +92,17 @@ func TestSyncerLifecycle(t *testing.T) {

kcpClient, err := kcpclientset.NewForConfig(upstreamServer.BaseConfig(t))
require.NoError(t, err)
t.Logf("Waiting for imported-apis APIBinding to be ready...")
t.Logf("Waiting for negotiaged api to be generated...")
require.Eventually(t, func() bool {
binding, err := kcpClient.Cluster(wsPath).ApisV1alpha1().APIBindings().Get(ctx, workloadv1alpha1.ImportedAPISExportName, metav1.GetOptions{})
negotiatedAPIs, err := kcpClient.Cluster(wsPath).ApiresourceV1alpha1().NegotiatedAPIResources().List(ctx, metav1.ListOptions{})
if err != nil {
return false
}
return binding.Status.Phase == apisv1alpha1.APIBindingPhaseBound
}, wait.ForeverTestTimeout, time.Millisecond*100, "imprted-apis APIBinding is not bound")
return len(negotiatedAPIs.Items) > 0
}, wait.ForeverTestTimeout, time.Millisecond*100, "negotiaged apis are not generated")

t.Logf("Bind location workspace")
framework.NewBindCompute(t, wsPath, upstreamServer).Bind(t)
framework.NewBindCompute(t, wsPath, upstreamServer, framework.WithAPIExportsWorkloadBindOption("root:compute:kubernetes", workloadv1alpha1.ImportedAPISExportName)).Bind(t)

upstreamConfig := upstreamServer.BaseConfig(t)
upstreamKubeClusterClient, err := kcpkubernetesclientset.NewForConfig(upstreamConfig)
Expand Down
2 changes: 0 additions & 2 deletions tmc/pkg/server/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,6 @@ func (s *Server) installWorkloadsDefaultLocationController(ctx context.Context,
c, err := workloadsdefaultlocation.NewController(
kcpClusterClient,
s.Core.KcpSharedInformerFactory.Workload().V1alpha1().SyncTargets(),
s.Core.KcpSharedInformerFactory.Apis().V1alpha1().APIExports(),
s.Core.KcpSharedInformerFactory.Apis().V1alpha1().APIBindings(),
s.Core.KcpSharedInformerFactory.Scheduling().V1alpha1().Locations(),
)
if err != nil {
Expand Down

0 comments on commit e584b2f

Please sign in to comment.