Skip to content

Commit

Permalink
add document for resource bundle API.
Browse files Browse the repository at this point in the history
Signed-off-by: morvencao <[email protected]>
  • Loading branch information
morvencao committed Jun 3, 2024
1 parent 90674e6 commit 013e605
Show file tree
Hide file tree
Showing 3 changed files with 365 additions and 0 deletions.
214 changes: 214 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,220 @@ ocm get /api/maestro/v1/resources
}
```

#### Create a resource bundle with multiple resources with gRPC client

1. Enable gRPC server by passing `--enable-grpc-server=true` to the maestro server start command, for example:

```shell
$ oc -n maestro patch deploy/maestro --type=json -p='[{"op": "add", "path": "/spec/template/spec/containers/0/command/-", "value": "--enable-grpc-server=true"}]'
```

2. Port-forward the gRPC service to your local machine, for example:

```shell
$ oc -n maestro port-forward svc/maestro-grpc 8090 &
```

3. Create a resource bundle with multiple resources using the gRPC client, for example:

```shell
go run ./examples/grpcclient.go -cloudevents_json_file ./examples/cloudevent-bundle.json -grpc_server localhost:8090
```

4. Get the resource bundle with multiple resources, for example:

```shell
ocm get /api/maestro/v1/resource-bundles
{
"items": [
{
"consumer_name": "cluster1",
"created_at": "2024-05-30T05:03:08.493083Z",
"delete_option": {
"propagationPolicy": "Foreground"
},
"href": "/api/maestro/v1/resource-bundles/68ebf474-6709-48bb-b760-386181268060",
"id": "68ebf474-6709-48bb-b760-386181268060",
"kind": "ResourceBundle",
"manifest_configs": [
{
"feedbackRules": [
{
"jsonPaths": [
{
"name": "status",
"path": ".status"
}
],
"type": "JSONPaths"
}
],
"resourceIdentifier": {
"group": "apps",
"name": "web",
"namespace": "default",
"resource": "deployments"
},
"updateStrategy": {
"type": "ServerSideApply"
}
}
],
"manifests": [
{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {
"name": "web",
"namespace": "default"
}
},
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "web",
"namespace": "default"
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"app": "web"
}
},
"template": {
"metadata": {
"labels": {
"app": "web"
}
},
"spec": {
"containers": [
{
"image": "nginxinc/nginx-unprivileged",
"name": "nginx"
}
]
}
}
}
}
],
"name": "68ebf474-6709-48bb-b760-386181268060",
"status": {
"ObservedVersion": 1,
"SequenceID": "1796044690592632832",
"conditions": [
{
"lastTransitionTime": "2024-05-30T05:03:08Z",
"message": "Apply manifest work complete",
"reason": "AppliedManifestWorkComplete",
"status": "True",
"type": "Applied"
},
{
"lastTransitionTime": "2024-05-30T05:03:08Z",
"message": "All resources are available",
"reason": "ResourcesAvailable",
"status": "True",
"type": "Available"
}
],
"resourceStatus": [
{
"conditions": [
{
"lastTransitionTime": "2024-05-30T05:03:08Z",
"message": "Apply manifest complete",
"reason": "AppliedManifestComplete",
"status": "True",
"type": "Applied"
},
{
"lastTransitionTime": "2024-05-30T05:03:08Z",
"message": "Resource is available",
"reason": "ResourceAvailable",
"status": "True",
"type": "Available"
},
{
"lastTransitionTime": "2024-05-30T05:03:08Z",
"message": "",
"reason": "NoStatusFeedbackSynced",
"status": "True",
"type": "StatusFeedbackSynced"
}
],
"resourceMeta": {
"group": "",
"kind": "ConfigMap",
"name": "web",
"namespace": "default",
"ordinal": 0,
"resource": "configmaps",
"version": "v1"
},
"statusFeedback": {}
},
{
"conditions": [
{
"lastTransitionTime": "2024-05-30T05:03:08Z",
"message": "Apply manifest complete",
"reason": "AppliedManifestComplete",
"status": "True",
"type": "Applied"
},
{
"lastTransitionTime": "2024-05-30T05:03:08Z",
"message": "Resource is available",
"reason": "ResourceAvailable",
"status": "True",
"type": "Available"
},
{
"lastTransitionTime": "2024-05-30T05:03:08Z",
"message": "",
"reason": "StatusFeedbackSynced",
"status": "True",
"type": "StatusFeedbackSynced"
}
],
"resourceMeta": {
"group": "apps",
"kind": "Deployment",
"name": "web",
"namespace": "default",
"ordinal": 1,
"resource": "deployments",
"version": "v1"
},
"statusFeedback": {
"values": [
{
"fieldValue": {
"jsonRaw": "{\"availableReplicas\":1,\"conditions\":[{\"lastTransitionTime\":\"2024-05-30T05:03:13Z\",\"lastUpdateTime\":\"2024-05-30T05:03:13Z\",\"message\":\"Deployment has minimum availability.\",\"reason\":\"MinimumReplicasAvailable\",\"status\":\"True\",\"type\":\"Available\"},{\"lastTransitionTime\":\"2024-05-30T05:03:08Z\",\"lastUpdateTime\":\"2024-05-30T05:03:13Z\",\"message\":\"ReplicaSet \\\"web-dcffc4f85\\\" has successfully progressed.\",\"reason\":\"NewReplicaSetAvailable\",\"status\":\"True\",\"type\":\"Progressing\"}],\"observedGeneration\":1,\"readyReplicas\":1,\"replicas\":1,\"updatedReplicas\":1}",
"type": "JsonRaw"
},
"name": "status"
}
]
}
}
]
},
"updated_at": "2024-05-30T05:03:17.796496Z",
"version": 1
}
],
"kind": "ResourceBundleList",
"page": 1,
"size": 1,
"total": 1
}
```

#### Run in OpenShift

Take OpenShift Local as an example to deploy the maestro. If you want to deploy maestro in an OpenShift cluster, you need to set the `external_apps_domain` environment variable to point your cluster.
Expand Down
80 changes: 80 additions & 0 deletions examples/cloudevent-bundle.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"specversion": "1.0",
"id": "0192bd68-8444-4743-b02b-4a6605ec0413",
"type": "io.open-cluster-management.works.v1alpha1.manifestbundles.spec.create_request",
"source": "grpc",
"clustername": "cluster1",
"resourceid": "68ebf474-6709-48bb-b760-386181268064",
"resourceversion": 1,
"datacontenttype": "application/json",
"data": {
"manifests": [
{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {
"name": "web",
"namespace": "default"
}
},
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "web",
"namespace": "default"
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"app": "web"
}
},
"template": {
"metadata": {
"labels": {
"app": "web"
}
},
"spec": {
"containers": [
{
"image": "nginxinc/nginx-unprivileged",
"name": "nginx"
}
]
}
}
}
}
],
"deleteOption": {
"propagationPolicy": "Foreground"
},
"manifestConfigs": [
{
"resourceIdentifier": {
"group": "apps",
"resource": "deployments",
"namespace": "default",
"name": "web"
},
"feedbackRules": [
{
"type": "JSONPaths",
"jsonPaths": [
{
"name": "status",
"path": ".status"
}
]
}
],
"updateStrategy": {
"type": "ServerSideApply"
}
}
]
}
}
71 changes: 71 additions & 0 deletions examples/grpcclient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package main

import (
"context"
"encoding/json"
"flag"
"log"
"os"

cloudevents "github.com/cloudevents/sdk-go/v2"
"github.com/cloudevents/sdk-go/v2/binding"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
pbv1 "open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/grpc/protobuf/v1"
grpcprotocol "open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/grpc/protocol"
)

var (
tls = flag.Bool("tls", false, "Connection uses TLS if true, else plain TCP")
caFile = flag.String("ca_file", "", "The absolute file path containing the CA root cert file")
serverAddr = flag.String("grpc_server", "localhost:8090", "The server address in the format of host:port")
serverHostOverride = flag.String("server_host_override", "x.test.example.com", "The server name used to verify the hostname returned by the TLS handshake")
cloudEventFile = flag.String("cloudevents_json_file", "", "The absolute file path containing the CloudEvent resource")
)

func main() {
flag.Parse()
var opts []grpc.DialOption
if *tls {
creds, err := credentials.NewClientTLSFromFile(*caFile, *serverHostOverride)
if err != nil {
log.Fatalf("Failed to create TLS credentials: %v", err)
}
opts = append(opts, grpc.WithTransportCredentials(creds))
} else {
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
}

conn, err := grpc.Dial(*serverAddr, opts...)
if err != nil {
log.Fatalf("fail to dial: %v", err)
}
defer conn.Close()

client := pbv1.NewCloudEventServiceClient(conn)

cloudeventJSON, err := os.ReadFile(*cloudEventFile)
if err != nil {
log.Fatalf("failed to read cloudevent file: %v", err)
}

evt := &cloudevents.Event{}
if err := json.Unmarshal(cloudeventJSON, evt); err != nil {
log.Fatalf("failed to unmarshal cloudevent: %v", err)
}

ctx := context.TODO()
pbEvt := &pbv1.CloudEvent{}
if err = grpcprotocol.WritePBMessage(ctx, binding.ToMessage(evt), pbEvt); err != nil {
log.Fatalf("failed to convert spec from cloudevent to protobuf: %v", err)
}

if _, err = client.Publish(ctx, &pbv1.PublishRequest{Event: pbEvt}); err != nil {
log.Fatalf("failed to publish: %v", err)
}

log.Printf("=======================================")
log.Printf("Published spec with cloudevent:\n%v\n\n", evt)
log.Printf("=======================================")
}

0 comments on commit 013e605

Please sign in to comment.