Contributing to Meshery Server Events

Meshery incorporates an internal events publication mechanism that provides users with real-time updates on the processes occurring within the Meshery server when interacting with its endpoints. It ensures that users are kept in the loop regarding the ongoing activities within the API, and guides users towards future steps to resolve issues. This guide will provide step-by-step instructions on sending events from the server, including when to trigger events and what information to include.

First, let’s take a look at how the event object is constructed,

 
eventBuilder := events.NewEvent() .ActedUpon(UUID) .FromUser(UUID) .FromSystem(UUID) .WithCategory(string) .WithAction(string) .WithSeverity(EventSeverity) .WithDescription(string) .WithMetadata(map[string]interface{}) .Build()

  

Note: events is a package github.com/meshery/meshkit/models/events from MeshKit containing source code of event related functionality

The event mechanism utilizes builder pattern to construct event objects, events.NewEvent() creates an instance of EventBuilder type, which functions as a builder class for constructing Event objects.

  • ActedUpon(UUID) : it takes UUID of the resource being provisioned on the server, and the events are associated with that resource.
  • FromUser() : it takes UUID of the user initiating the HTTP request to the server. This enables the server to publish events intended for that specific user in response.
  • FromSystem(UUID) : it takes UUID of the running meshery instance (Handler instance contains it h.SystemID).
  • WithCategory : The string argument to specify under which category this event falls, For example, when a user is provisioning a Kubernetes connection, the specified category for the event is “connection.
  • WithAction : The string argument to specify the type of action the server is performing, for instance, when registering a Kubernetes connection, the action would be “register”.

Note: categories and actions must be in snake case, example categories: “signup_request”, “github_artifacts”. example actions: “remove_from_organization”, “add_to_organization”.

  • WithSeverity: it takes EventSeverity (underlying type is string), to specify severity of the event to bring user’s attention to the event accordingly.

Note: In certain conditions you must add some fields with specific keys:

  • If the severity is “Error”, include a field in Metadata using the key err with the corresponding error value.
  • When you want to add a link to meshery docs, include a field in Metadata using the key doc with the corresponding link value.

  • WithDescription : The string argument provides a detailed, descriptive message that depicts the specific operation or action being executed on the server.
  • WithMetadata: it takes a Map map[string]interface{} data structure containing any supplementary information that the developer/contributor deems essential for the user to be informed about.
  • Build : returns the Event instance constructed upon the previously described functions and prepares it for publication through the Broadcast, which is responsible for disseminating events.

An Example in Code

 
func (h *Handler) KubernetesPingHandler(w http.ResponseWriter, req *http.Request, _ *models.Preference, user *models.User, provider models.Provider) { userID := uuid.FromStringOrNil(user.ID) token, ok := req.Context().Value(models.TokenCtxKey).(string) if !ok { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "failed to get the token for the user") return } connectionID := req.URL.Query().Get("connection_id") eventBuilder := events.NewEvent().FromUser(userID).FromSystem(*h.SystemID).WithCategory("connection").WithAction("ping") if connectionID != "" { // Get the context associated with this ID k8sContext, err := provider.GetK8sContext(token, connectionID) eventBuilder.ActedUpon(uuid.FromStringOrNil(connectionID)) if err != nil { eventBuilder.WithSeverity(events.Error).WithDescription(fmt.Sprintf("Encountered an issue while retrieving kubernetes context for connection ID `%s`", connectionID)). WithMetadata(map[string]interface{}{ "error": err, }) event := eventBuilder.Build() _ = provider.PersistEvent(event) go h.config.EventBroadcaster.Publish(userID, event) w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "failed to get kubernetes context for the given ID") return } eventBuilder.WithSeverity(events.Success).WithDescription("Retrieved kubernetes context successfully"). WithMetadata(map[string]interface{}{ "Context Name": k8sContext.Name, "Server Address": k8sContext.Server, }) event := eventBuilder.Build() _ = provider.PersistEvent(event) go h.config.EventBroadcaster.Publish(userID, event) // Remaining code ... } }

  

In the given code block, the “resource” refers to the connection on which the server is performing the “ping” action. The “ActedUpon” field is supplied with the “connectionID” value obtained from the query parameter. provider.GetK8sContext returns the K8sContext object which serves as a representation of a Kubernetes context within the Meshery. if provider.GetK8sContext returns an error, the event is constructed with severity Error, description and enriched with metadata fields to provide the user with a complete context about the event.

If provider.GetK8sContext succeeds, an event with a Success severity level is constructed. The event includes a success message and metadata containing the context name and the address of the Kubernetes API server, obtained from the operation. This information aims to provide users with relevant details about the successful operation.

after constructing the Event object using the Build function, event is stored into database for records and subsequently published in a new goroutine through EventBroadcaster member variable of HandlerConfig, which is part of Handler instance.

To gain a deeper understanding and examine events more closely, you can explore the source code files k8config_handler.go and design_engine_handler.go within the Meshery project’s codebase.

Sending Events to Meshery Server

Meshery has two primary clients: Mesheryctl and Meshery UI. Both clients will produce multiple events throughout their lifecycle. Meshery Server is designed to accept these client-generated events and broadcast them using the Broadcaster.

Clients can send HTTP POST requests to api/events with the event encapsulated in a JSON request body. Upon receiving an event, the server processes, validates, and broadcasts it using the Broadcaster. This ensures that all relevant events generated by Mesheryctl and Meshery UI are effectively communicated and managed by the Meshery Server.

Example

```javascript async function sendClientEvent(clientId, eventPayload) { // Create a new event const event = { category: “client_event”, action: “create”, severity: “info”, description: “Client event creation”, metadata: eventPayload };

try {
    // Send event to Meshery Server
    const response = await fetch('http://localhost:9081/api/events', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(event)
    });

    if (!response.ok) {
        throw new Error(`Server returned non-200 status: ${response.status}`);
    }

    console.log('Event sent successfully');
} catch (error) {
    console.error('Failed to send event:', error);
} }

sendClientEvent(clientId, eventPayload);

Suggested Reading