Primaza
Multi Cluster Service Consumption Framework
The Problem
- Modern application stacks are increasingly complex
- Managing connections to all your supporting Services becomes more and more challenging… and that’s before you introduce additional clouds and hybrid environments
- No platform enables easy consumption of hybrid cloud services
- Needed to provide Scalability, Security and Portability
- Previous solutions (OSBAPI, Service Binding, etc.) gained moderate traction
- Replace one complexity with another
- Heavyweight, onerous, exacting implementations
- Customers are left to implement ad-hoc solutions
- Manual steps to provision and consume cloud service (not scalable)
- Custom IaC to provide the scale required (boilerplate imperative code)
- Too much time spent on non-value added activities
The Solution
- Separated logical environments that can span across different Kubernetes clusters
- An automatically collated Catalog that appropriately exposes available Services to Applications
- Leverages industry best practices to securely deliver service endpoint data to applications
- A platform that enables hybrid cloud services consumption in a portable, secured and scalable way
Benefits to Service Providers
- No action required!
- Continue to focus on delivering their cloud service with well-defined APIs
- Continue to work with Service Kubernetes APIs and Controllers
Benefits to Administrators
- Automatic addition of services to the catalog (via Service Class) - manual catalog management still possible (via Registered Service)
- Service Classes are easy to create, and can be built by the service provider, administrators, or third-parties
- Popular Service Classes delivered by Primaza out of the box
Benefits to Developer
- Service Catalog makes it easy to discover new services and see what's available in the environment
- Developers can automatically and easily claim services from the catalog
- Claims can be requested for multiple environments (CI/CD capable)
- Developers can discover/learn via provided sandbox
Implementation
Primaza's architecture is composed by the following elements:
- Primaza's Control Plane: manages environments, services and claims
- Application agents: binds applications to services
- Service agents: discover services
Primaza defines the following entities and controllers to provide the above described features.
Entities:
- Cluster Environment: represents an development environment on a Kubernetes Cluster.
- Registered Service: represents running instance of a software service.
- Service Binding: projects secrets referenced by ServiceBinding resources to application compute resources.
- Service Class: defines how a registered service can be automatically generated from a service
- Service Claim: represents a claim for Registered Service.
- Service Catalog: represents group of Registered Services.
Architecture
Primaza's architecture is composed by the following elements:
- Primaza's Control Plane: manages environments, services and claims
- Application agents: binds applications to services
- Service agents: discover services
To better describe the Primaza architecture the following three concepts need to be introduced:
- Primaza's Control Plane Namespace: a namespace where Primaza is installed
- Application Namespace: a namespace configured to host the Primaza's Application Agent
- Service Namespace: a namespace configured to host the Primaza's Service Agent
For how Primaza is designed, these three concepts may apply at the same time to a single namespace. In other words, you can install Primaza, Primaza's Application Agent, and Primaza's Service Agent in the same Kubernetes namespace.
In the following picture you find a simplified diagrams of the agents-based architecture.
To allow agents to perform operations in the namespace, they need an Service Account with the right permissions.
primazactl is an in-development companion tool to help administrators configuring clusters and namespaces.
In the following picture, you find a detailed representation of all the resources created in a Primaza environment.
Control Plane
The Primaza's Control Plane is the core of Primaza.
It contains all the logic for the management of the following resources:
- Cluster Environment: represents an development environment on a Kubernetes Cluster.
- Registered Service: represents running instance of a software service.
- Service Binding: projects secrets referenced by ServiceBinding resources to application compute resources.
- Service Class: defines how a registered service can be automatically generated from a service
- Service Claim: represents a claim for Registered Service.
- Service Catalog: represents group of Registered Services.
Agents
Primaza Agents are pushed into namespaces by Primaza. Two kinds of Primaza Agents are defined:
- Application agent: is published into application namespaces and binds applications to services.
- Service agent: is published into service namespace and discovers services.
To allow agents to perform operations in the namespace, they need an identity (Service Account) with the right permissions.
primazactl is an companion tool to help administrators configuring clusters and namespaces.
Configure agent
Application agent and Service agent deployment are configured in Primaza's Control Plane at runtime.
There exists a ConfigMap named primaza-manager-config
in Primaza's Control Plane namespace.
This ConfigMap consists of following keys:
agentapp-image
: Application agent imageagentsvc-image
: Service agent imageagentapp-manifest
: Application agent manifestagentsvc-manifest
: Service agent manifest
By default the ConfigMap is populated using the make target agents-configmap
executed at runtime.
This ConfigMap is used in primaza-controller-manager
deployment as environment variables.
An user can modify the values of primaza-manager-config
and then delete the primaza-controller-manager
pod in Primaza's Control Plane namespace.
This would reset the environment variables defined in primaza-manager-config
deployment.
Application Agent
Application Agents are installed into ClusterEnvironment's Application Namespaces. Target namespaces need to be already configured to allow agents to run.
Application Agents need to access resources in the namespace they're published into. More specifically, an Application Agent requires the following resources to exists into the namespace:
- A Role granting
- full access to
leases.coordination.k8s.io
- read access to
servicebindings.primaza.io
- read access and update rights for
deployments.apps
- create right for
events
- full access to
- A Service Account for the agent
- A RoleBinding that binds the ServiceAccount to the Role
- A Secret with the kubeconfig to communicate back with Primaza's Control Plane
To prepare Application Namespaces you can use primazactl.
When a ServiceBinding is created in an Application Namespace, the Application Agent looks for resources mentioned in its specification.
Primaza Application Agent runs a dynamic informer for Application
resources mentioned in the ServiceBinding's specification.
The informer monitors changes to the Application
matching the ServiceBinding specifications and updates the ServiceBinding's status accordingly.
- If the
Application
Resource mentioned in ServiceBinding specification is updated or created, the secret referenced by ServiceBinding resource will be projected into all the matching applications. - If the
Application
Resource is deleted and no matching workloads are found in the namespace, then the ServiceBinding status conditionReason
is updated toNoMatchingWorkloads
.
Binding a Service
When a ServiceBinding is created (or updated) into an Application namespace, the Application Agent gets the data from the secrets and project them into applications specified in the ServiceBinding instance.
Currently the secret data is being projected as volume mounts.
SERVICE_BINDING_ROOT
points to the environment variable in the container which is used as the volume mount path.
In the absence of this environment variable, /bindings
is used as the volume mount path.
Please refer to https://github.com/servicebinding/spec#reconciler-implementation for more information.
Claiming a Service
Claiming from Primaza's Control Plane Namespace
When a ServiceClaim is created in Primaza's Control Plane's Namespace, Primaza builds the ServiceBinding and Service Endpoint Definition Secrets and pushes them resources to all the application namespaces of matching ClusterEnvironments.
The ServiceClaim controller in Primaza Control Plane watches RegisteredService resources.
Any change made to ServiceEndpointDefinition
values in RegisteredService are propagated to the secret and ServiceBinding resource in application namespace by Primaza.
Claiming from Application Namespace
When a ServiceClaim is created in Primaza's Control Plane's Namespace, the Application Agent forwards this ServiceClaim to Primaza's Control Plane. The Control Plane, in turn, builds the ServiceBinding and Service Endpoint Definition Secrets and pushes them back to the Application Namespace.
Service Agent
Service Agents are installed into ClusterEnvironment's Service Namespaces. Target namespaces need to be already configured to allow agents to run. Service Agents just need to access resources in the namespace they're published into.
More specifically, a Service agent requires the following resources to exists into the namespace:
- A Role granting
- full access to
leases.coordination.k8s.io
- create right for
events
- full access to
- A Service Account for the agent
- A RoleBinding that binds the ServiceAccount to the Role
- A Secret with the kubeconfig to communicate back with Primaza's Control Plane
To prepare Service Namespaces you can use primazactl.
When a ServiceClass is created, the Service Agent looks for resources matching its specification.
A Role needs to be created which allows to retrieve, list and watch ServiceClass resources as Primaza's Service Agent runs a dynamic informer for each resource.
The informer monitors changes to resources matching the ServiceClass specifications and updates the RegisteredServices on Primaza control plane.
Service Discovery
The Service Agent monitors all the resources specified in Service Classes existing in its namespace. When a resource matching a Service Class is created, updated, or deleted, the Service Agent is notified and will create a RegisteredService in Primaza's Control Plane.
Namespace Reconciliation
There two types of Namespaces defined in Primaza, where the reconciliation happens. Namespaces are reconciled by the ClusterEnvironment controllers which runs in the Primaza's Control Plane.
- Application Namespace reconciliation: Creation, Update and Deletion of the resources in application namespace
- Service Namespace reconciliation: Creation, Update and Deletion of the resources in service namespace
During the Namespace Lifecycle, Primaza's permissions in the namespace are checked, permissions for talking with Primaza's Control Plane are set, and then namespace agent is deployed into the namespace.
Later, during Agent deletion, its permissions on Primaza's Control Plane are revoked.
Application Namespace Reconciliation
The Lifecycle of a Application Namespace includes the creation, update and deletion of Primaza's resources.
Application Namespaces are reconciled by the ClusterEnvironment controllers. This controller runs in the Primaza's Control Plane.
Events on Application Namespaces are related to ones on ClusterEnvironments.
-
Application Namespace Creation: when an application namespace is added to the
applicationNamespaces
list of a ClusterEnvironment, the ClusterEnvironment controller checks the permissions in the target namespace and tries to push the Application Agent in it. Finally, it will push in the namespace the ServiceCatalog for the given environment and all the ServiceBinding generated from ServiceClaims declaring alabelMatch
or that are targeting the namespace in theapplicationContext
property. -
Application Namespace Update: when an update event on the Application Namespace parent ClusterEnvironment occurs, all application namespaces are reconciled as described in the
Application Namespace Creation
andApplication Namespace Deletion
. -
Application Namespace Deletion: when an Application Namespace is deleted from its parent ClusterEnvironment's
applicationNamespaces
list or when the ClusterEnvironment itself is deleted, the Application Agent is deleted from the target namespace. As a result, this will trigger a deletion of all the Primaza's resources in the namespace. This deletion is actually relying on Kubernetes' ownership.
Service Namespace Reconciliation
The Lifecycle of a Service Namespace includes the creation, update and deletion of Primaza's resources.
Namespaces are reconciled by the ClusterEnvironment controllers. This controller runs in the Primaza's Control Plane.
Events on Service Namespaces are related to ones on ClusterEnvironments.
-
Service Namespace Creation: when a service namespace is added to the
serviceNamespaces
list of a ClusterEnvironment, the ClusterEnvironment controller checks the permissions in the target namespace and tries to push the Service Agent in it. Finally, it will push in the namespace all the matching ServiceClasses for the given environment. -
Service Namespace Update: when an update event on the Service Namespace parent ClusterEnvironment occurs, all service namespaces are reconciled as described in the
Service Namespace Creation
andService Namespace Deletion
. -
Service Namespace Deletion: when an Service Namespace is deleted from its parent ClusterEnvironment's
serviceNamespaces
list or when the ClusterEnvironment itself is deleted, the Service Agent is deleted from the target namespace. As a result, this will trigger a deletion of all the Primaza's resources in the namespace. This deletion is actually relying on Kubernetes' ownership.
Resources
In the following you find a list of all the resources needed for a Primaza Tenant.
Tenant Namespace
The Tenant namespace is the namespace in which the Primaza Control Plane is installed.
Control Plane
Resources created in the Primaza's Tenant namespace for executing the Primaza Control Plane:
- Primaza Control Plane's namespace is named after the tenant name:
<tenant>
- Application Agent Role:
primaza-claimer
- Service Agent Role:
primaza-reporter
Remote Cluster
Resources created in the Primaza Tenant namespace when a Remote cluster is joined to the Primaza Tenant:
- ClusterEnvironment:
<cluster environment name>
- Kubeconfig Secret for ClusterEnvironment:
primaza-kubeconfig-<cluster environment name>
Application Agent
Resources created in the Primaza Tenant namespace when an Application Namespace is configured:
- Application Agent's Service Account:
primaza-app-<cluster environment name>-<namespace>
- Access Token Secret for Application Agent's Service Account:
primaza-tkn-app-<cluster environment name>-<namespace>
- RoleBinding between Application Agent Service Account and Application Agent Role:
primaza:claimer-<cluster environment name>-<namespace>
Service Agent
Resources created in the Primaza Tenant namespace when a Service Namespace is configured:
- Service Agent's Service Account:
primaza-svc-<cluster environment name>-<namespace>
- Access Token Secret for Service Agent's Service Account:
primaza-tkn-svc-<cluster environment name>-<namespace>
- RoleBinding between Service Agent Service Account and Service Agent Role:
primaza:reporter-<cluster environment name>-<namespace>
Remote Cluster
Resources created in the Remote Cluster when it's joined to the Primaza Tenant:
- ClusterEnvironment's Service Account in namespace
kube-system
:pmz-<tenant>-<cluster environment name>
- Access Token Secret for ClusterEnvironment's Service Account in namespace
kube-system
:tkn-pmz-<tenant>-<cluster environment name>
Application Namespace
Resources created in the Remote Cluster when an Application Namespace is configured:
- Service Account:
primaza-app-agent
- Agent's Deployment:
primaza-app-agent
- Kubeconfig Secret:
primaza-app-kubeconfig
- Leader Election Role:
pmz:app:leader-election
- Manager Role:
pmz:app:manager
- Primaza's Role:
pmz:controlplane:app
- Leader Election RoleBinding:
pmz:app:leader-election
- Manager RoleBinding:
pmz:app:manager
- Primaza's RoleBinding:
pmz:controlplane:app
Service Namespaces
Resources created in the Remote Cluster when a Service Namespace is configured:
- Service Account:
primaza-svc-agent
- Agent's Deployment:
primaza-svc-agent
- Kubeconfig Secret:
primaza-svc-kubeconfig
- Leader Election Role:
pmz:svc:leader-election
- Manager Role:
pmz:svc:manager
- Primaza's Role:
pmz:controlplane:svc
- Leader Election RoleBinding:
pmz:svc:leader-election
- Manager RoleBinding:
pmz:svc:manager
- Primaza's RoleBinding:
pmz:controlplane:svc
Entities
- Cluster Environment: represents an development environment on a Kubernetes Cluster.
- Registered Service: represents running instance of a software service.
- Service Binding: projects secrets referenced by ServiceBinding resources to application compute resources.
- Service Class: defines how a registered service can be automatically generated from a service
- Service Claim: represents a claim for Registered Service.
- Service Catalog: represents group of Registered Services.
ClusterEnvironment
A ClusterEnvironment represents a development environment on a Kubernetes Cluster. Examples of environments are 'app1-prod','app1-dev', or 'app1-uat'.
ClusterEnvironments contain connection information and the namespaces in which Primaza should operate. ClusterEnvironments differentiate among Service and Application namespaces. Application namespaces are the ones in which Primaza pushes the Application Agent that in turn binds applications to services. Service namespaces are the ones in which Primaza pushes the Service Agent and that in turn performs service discovery. Please refer to the Architecture section for more information about Agents and Primaza's architecture.
Specification
The definition of ClusterEnvironments can be obtained directly from ClusterEnvironment CRD.
The ClusterEnvironment's specification contains the following required properties:
clusterContextSecret
: contains the name of the secret that stores the kubeconfig that can be used to connect to the physical target cluster.applicationNamespaces
: contains a list of namespaces where claiming and binding will happen. Applications to be bound to services will be looked for in those namespaces.serviceNamespaces
: contains a list of namespaces where discovery will happen. Services that populate the Service Catalog will be looked for in those namespaces.
A ClusterEnvironment also defines the following optional properties:
contactInfo
Cluster Admin contact informationdescription
: Description of the ClusterEnvironment
Status
The ClusterEnvironment's status can have one of the following values:
Online
Partial
Offline
An Online
ClusterEnvironment is reachable by Primaza, whereas an Offline
one isn't reachable.
A Partial
ClusterEnvironment is also reachable, but not configured correctly.
This can happen if Primaza doesn't have the required permissions on this namespaces.
More details can be found in the ClusterEnvironment's status conditions.
Use Cases
Creation
When a ClusterEnvironment is created, Primaza verifies the connection to the cluster. If it can not connect to the target cluster, it logs an error and retries later. Otherwise, it checks its permissions in application and service namespaces. For each service and application namespace on which permissions are granted, Primaza pushes respectively the service or application agent.
ClusterEnvironment's State and Conditions are updated according to tests and agents' deployment results.
When a ClusterEnvironment is created, Primaza ensures a Service Catalog exists for its environment. The Service Catalog thereby created are also pushed to the ClusterEnvironment application namespace where permissions are granted.
Deletion
When a ClusterEnvironment is deleted, the permissions granted in Primaza's namespace to Service Accounts associated to namespace agents and agent deployments on target cluster's namespaces are removed.
Update
As on creation, Primaza verifies the connection to and its permissions into the target cluster. Finally, it pushes agents in cluster's application and service namespaces. As on deletion, if application or service namespaces are removed, Primaza deletes agent deployments and agents-granted permissions.
RegisteredService
A RegisteredService represents an instance of a running service.
This resource can be created by both a human operator or a piece of software (agent, bot, script). This flexibility allows for the service provisioning and service discovery operations to be decoupled from representation of that service in a Service Catalog.
A RegisteredService can be claimed to be used by a specific application, and once claimed, the RegisteredService can't be claimed by other application. If the application doesn't require the service any longer, it can remove the claim and the registered service is put back in the pool of available services.
Specification
The definition of RegisteredServices can be obtained directly from RegisteredService CRD.
The RegisteredService's specification contains the following required properties:
serviceClassIdentity
: A set of key/value pairs that identify the service class. Examples of service class identity keys include type of service, and provider of service. This property is required.serviceEndpointDefinition
: A set of key/value pairs that provides two pieces of information: the service connectivity and the service authentication. Values can be either a string or a reference to a secret field. This property is required.
A RegisteredService also has the following optional properties, which gives the user more control over the resource:
constraints
: Restrictions of the registered service. A constraint could be a specific environment where the service can't be claimed from. This property is optional, when it's absent, it means there isn't constraints. For more details, look at the Constraints section.healthcheck
: A mechanism to be able to verify the service is online and ready to use. One way this can be accomplished is by providing an image containing a client that can be run to test connectivity and authentication. This property is optional, when it's absent, it means the service will be considered available as soon as it's registered.sla
: Provides multiple levels of resiliency, scalability, fault tolerance and security. This allows claims to consider the robustness of service. This property is optional, when it's absent, it means that there is no distinctions between services given the SLA.
Constraints
The constraints
section of a RegisteredService defines a list of environments.
The environment list can contain explicit environments that are to include the service, or it can also contain a list of environments that are to exclude the service.
The way to depict exclusion is by adding the !
prefix to the environment, i.e !prod
.
Please note that exclusions have precedence over inclusions.
For example, if the list contains !prod
but also includes dev
, then dev
is considered to be in the !prod
set of environments and therefore redundant.
If there is a third environment stage, then !prod
would include both stage
and dev
even if they're not defined in the list explicitly.
Metadata
A Primaza's discovered RegisteredService has the following annotations:
primaza.io/cluster-environment
: the name of the ClusterEnvironemnt it's part ofprimaza.io/service-apiversion
: the APIVersion of the resource represented by the RegisteredServiceprimaza.io/service-kind
: the kind of the resource represented by the RegisteredServiceprimaza.io/service-name
: the name of the resource represented by the RegisteredServiceprimaza.io/service-namespace
: the namespace of the resource represented by the RegisteredServiceprimaza.io/service-uid
: the UID of the resource represented by the RegisteredService
Status
The state of a RegisteredService could be one of the following:
- Registered
- Available
- Claimed
- Unreachable
- Undetermined
As of now, health-check are still not implemented. The only states that are used are
Available
andClaimed
.
The RegisteredService state is Registered
right after creation.
Usually, this will change immediately after the controller gets notified of existence of new resource, unless controller fails even before it tries to run health-check.
If the health check passes or isn't defined, the state will change to Available
.
Once a RegisteredService is claimed, its state moves to Claimed
.
On the other hand, if the health-check comes back as a failure, the state would change to Unreachable
.
Sometimes, the health check could fail before it can determine whether the service is healthy or not.
In those cases, the state of the RegisteredService will go to Undetermined
indicating that there is an issue with the health-check and not with the service itself.
There are some situations when the health-check will fail while the RegisteredService is in Claimed
state.
In those cases the RegisteredService will move to Unreachable
.
If, at a later time, the health-check passes then the controller will check if there is still a claim matching the RegisteredService and move the state back to Claimed
.
However, if there isn't claim matching the RegisteredService the state will move to Available
.
Use Cases
Creation
When a RegisteredService is created, Primaza will first check if the health check can be run and will modify the state accordingly.
The ServiceCatalog will be updated to include the new RegisteredService only if the state can be moved to Available
.
Deletion
When a RegisteredService resource is deleted, the ServiceCatalog entry for the service should be deleted.
Also, if a RegisteredService is Claimed
, the ServiceClaim resource state will be changed to Pending
by the ServiceClaim controller since the matched RegisteredService doesn't exist any longer.
Additionally, when a RegisteredService resource state changes to Claimed
the corresponding entry in the ServiceCatalog resource is removed.
Update
When a RegisteredService is updated, a few things can happen:
- Existing ServiceClaim resources may not be able to match the registered service any longer.
In those case the ServiceClaim controller will try to find another match and, if unsuccessful, the ServiceClaim resource state will change to
Pending
. - Existing service endpoint definition has changed and should trigger a binding secret generation.
ServiceBinding
A ServiceBinding represents the secrets along with the Applications which are to bound with it. ServiceBindings may explicitly request an Application by Name or by LabelSelector. LabelSelector can match more than one resource.
ServiceBinding resource is being created by the ServiceClaim
controller.
ServiceBinding is being performed by Primaza Application Agent, and the agent should be deployed in the Application namespace where the workloads are to be bound. When a ServiceBinding is created (or updated) into an Application namespace, the Application Agent gets the data from the secrets and project them into applications specified in the ServiceBinding instance.
Currently the secret data is being projected as volume mounts.
SERVICE_BINDING_ROOT
points to the environment variable in the container which is used as the volume mount path.
In the absence of this environment variable, /bindings
is used as the volume mount path.
Please refer to https://github.com/servicebinding/spec#reconciler-implementation for more information.
Specification
The definition of ServiceBindings can be obtained directly from ServiceBinding CRD.
The ServiceBinding's specification contains the following required properties:
serviceEndpointDefinitionSecret
: ServiceEndpointDefinitionSecret is the name of the secret to project into the application. This property is required.application
: Application resource to inject the binding info. It could be any process running within a container. A ServiceBinding MAY define the application reference by name or by label selector. Name and label selector are mutually exclusive.
The ServiceBinding's specification also contains the following optional property:
envs
:Envs
declares environment variables based on the ServiceEndpointDefinitionSecret to be projected into the application
Metadata
Each ServiceBinding takes note of its RegisteredService in the following annotations:
primaza.io/registered-service-name
: The RegisteredService Nameprimaza.io/registered-service-uid
: The RegisteredService UID
Status
The ServiceBinding's status contains the properties state
and conditions
.
The state of the service binding can be Malformed
or Ready
.
The default value of service binding state is Malformed
.
The conditions
list of the service binding contains the following properties:
Type
: The service binding condition type isBound
orNotBound
.Bound
means that the secret is projected into the application.NotBound
denotes that the secret isn't projected into the application. This can only occur if the secret isn't found in the application namespace.
Message
: This contains the error logs for the service binding resources. This value will be an empty string if successful.Status
: Status of service binding can beTrue
orFalse
.Reason
: The reason has values defined asNoMatchingWorkloads
,ErrorFetchSecret
,Successful
andBinding Failure
Connections
: The list of workloads the service is bound to
Use Cases
Creation
When a ServiceBinding is created, the secret referenced by the ServiceBinding itself will be projected into all the matching applications.
The EnvironmentVariables envs
declared in the specification will also be projected to the matching application pods.
Matching applications are calculated as defined at in the section Specification
Deletion
If the ServiceBinding is deleted the secret projection from the workloads is removed too.
In case the secret referenced in the ServiceBinding resource is deleted, the projection is removed from the workloads and the ServiceBinding status is updated to Malformed
.
Update
When a ServiceBinding is updated, Primaza Application Agent will update the workload resources with the secret details. If the secret is updated the projection in the workloads will be updated accordingly.
ServiceClass
A Service Class is intended to define how a registered service can be automatically generated from a service. Currently, this service takes the shape of a resource already existing within a cluster: the Service Class controller will recognize these services and create a corresponding registered service.
This resource exists on both primaza and worker environments. The Primaza environment will push these resources to worker environments, where the service agent will capture the state of services within its namespace.
Specification
The definition of ServiceClasses can be obtained directly from ServiceClass CRD.
Within a Service Class, there are two required properties:
resource
defines the APIVersion and Kind of the resources to look for. It also contains a list of Service Endpoint Definition Mappings, which define how binding information for a particular service may be obtained. For more details, have a look at the resource field section.serviceClassIdentity
defines a set of attributes that are sufficient to identify a Service Class. This field is copied to the generated registered services.
A Service Class also contains two optional properties, constraints
and healthCheck
.
Both of these fields correspond exactly to their identically named properties within the Registered Service resource.
For more information on how to use these properties, refer to the Registered Service documentation
resource
field
The resource
's ServiceClass field contains all the information needed for identifying the resources it refers to, that's apiVersion
and kind
.
It also contains the rules for extracting the Service Endpoint Definition secret data, that's serviceEndpointDefinitionMappings
.
Mapping rules apply to the resource specification (resourceFields
) or to a secret (secretRefFields
).
To extract data from the resource, add an entry to the resourceFields
list.
Each entry contains the following properties:
name
: is used as the key in the Registered Service's Service Endpoint DefinitionjsonPath
: a JSONPath rule to extract the value for the current Service Endpoint Definition keysecret
: declares whether this field should be stored in a secret of if it can be embedded in the Registered Service specification
To extract data from a secret, add an entry to the secretRefFields
list.
Data extracted from a secret is always stored in a new secret.
Each entry contains the following properties:
name
: is used as the key in the Registered Service's Service Endpoint DefinitionsecretName
: represents the name of the secret to look for. It also contains two mutually exclusive sub-properties:jsonPath
: a JSONPath rule to extract the name of the secret from the resource specificationconstant
: a constant value for the secret name
secretKey
: represents the secret's key to use. It contains two mutually exclusive sub-properties:jsonPath
: a JSONPath rule to extract the key of the secret from the resource specificationconstant
: a constant value for the secret name
Status
Whenever a Service Class is created or updated, a connection test from the service environment to Primaza is performed.
The status of the Service Class will be updated to contain the results of this test underneath the condition type Connection
.
Use Cases
Creation
When a Service Class is created, Primaza pushes it to Service Namespaces whose Cluster Environment satisfies its constraints. Once created, the service agent will collect all the services corresponding to that Service Class and will push the Registered Services it generates back to Primaza.
Deletion
When a Service Class is deleted, Primaza removes it from Service Namespaces whose Cluster Environment satisfies its constraints. The Service Agent will identify the Registered Services that correspond to that Service Class and will delete them.
Update
When a Service Class is updated, Primaza pushes it to Service Namespaces whose Cluster Environment satisfies its constraints. The Service Agent will then collect the services corresponding to that Service Class and will create or update the Registered Services in Primaza.
ServiceClaim
A ServiceClaim represents a claim for a RegisteredService.
ServiceBindings may explicitly request an Application by Name or by LabelSelector. LabelSelector can match more than one resource.
Specification
The definition of a ServiceClaim can be obtained directly from ServiceClaim CRD.
The specification contains the following properties:
serviceClassIdentity
: A set of key/value pairs that identify the service class. Examples of service class identity keys include type of service, and provider of service. This property is required.serviceEndpointDefinitionKeys
: An array of keys that's required for connectivity. The values corresponding to each of these keys will be extracted from the service. This property is required.application
: identifies application resources through kind, apiVersion, and label selector or name.target
: Field that identifies the ServiceClaim target, may be an application deployed in a specific cluster or an entire environmentenvironmentTag
: A string representing one of the environment.applicationClusterContext
: A combination of ClusterEnvironment resource name and namespace.
envs
: allows projecting Service Endpoint Definition's data as Environment Variables in the Pod
The environmentTag
and applicationClusterContext
are mutually exclusive.
application
field values are passed to the ServiceBinding resource.
The application's label selector and application name are mutually exclusive.
Status
The Status of the ServiceClaim is also defined under the ServiceClaimCRD. It contains a mandatory property to track the state.
The state could be either Pending
or Resolved
or Invalid
.
If the state is Resolved
, the RegisteredService claimed is tracked in the ServiceClaim status.
Indeed, the status field registeredService
takes track of the name
and UID
of the claimed RegisteredService.
The spec of a ServiceClaim isn't meant to be updated.
If a user updates the spec of a ServiceClaim then the status of ServiceClaim is updated as Invalid
when Primaza Application Agent attempts to update the ServiceClaim on Primaza Control Plane.
There is an optional claimID
field with a unique ID for the claim.
Use Cases
Creation
When a ServiceClaim is created, Primaza should find a RegisteredService based on ServiceClassIdentity
and ServiceEndpointDefinitionKeys
and create Secret and ServiceBinding resources.
The Environment Variables envs
declared in the ServiceClaim are also added to the ServiceBinding specification.
The ServiceBinding resource will be marked as the owner for the secret.
Then it will update the state of ServiceClaim to Resolved
.
The state of RegisteredService will be changed to Claimed
.
If no match for RegisteredService is found, the state of ServiceClaim will be set to Pending
.
Deletion
When a ServiceClaim is deleted, Primaza will delete the Service Endpoint Definition Secret and the ServiceBinding.
As ServiceBinding is the owner of the Service Endpoint Definition Secret, deleting it ensures deletion of the secret too.
It also change the state of RegisteredService to Available
.
Update
When a ServiceClaim is updated, Primaza will update the Service Endpoint Definition Secret, the ServiceBinding and the ServiceClaim's state accordingly. The state changes will happen similar to that of creation time.
ServiceCatalog
A ServiceCatalog represents a group of RegisteredServices which are available in a Environment.
The ServiceCatalog can be consumed by Kubernetes applications that are unaware of the lifecycle of the service. Events on RegisteredServices signal an update to the ServiceCatalog. The ServiceCatalog can then be advertised to the application developers.
Specification
The definition of a ServiceCatalog can be obtained directly from ServiceCatalog CRD.
The specification contains a list of services
and claimedByLabels
.
The services
contains the following fields:
name
: Name defines the name of the known serviceserviceClassIdentity
: A set of key/value pairs that identify the service class. Examples of service class identity keys include type of service, and provider of service. This property is required.serviceEndpointDefinitionKeys
: An array of keys that's required for connectivity. The values corresponding to each of these keys will be extracted from the service. This property is required.
The claimedByLabels
contains the following fields:
name
: Name defines the name of the known serviceserviceClassIdentity
: A set of key/value pairs that identify the service class. Examples of service class identity keys include type of service, and provider of service. This property is required.serviceEndpointDefinitionKeys
: An array of keys that's required for connectivity. The values corresponding to each of these keys will be extracted from the service. This property is required.labels
: Labels used to claim the service.
Use Cases
Creation
When a ClusterEnvironment is created, Primaza ensures a ServiceCatalog exists for its environment. The ServiceCatalog thus created is then been pushed to all the Application Namespaces of matching ClusterEnvironment where permission is granted.
Deletion
ServiceCatalog gets deleted when the ClusterEnvironment is being deleted. The Catalog will also be removed from the matching Application Namespaces.
Update
When a RegisteredService is updated, the appropriate ServiceCatalog will get updated as well. The updated ServiceCatalog will be made available to all the Application Namespaces of matching ClusterEnvironments by Primaza.
The ServiceCatalog for each ClusterEnvironment will get updated with a service change if either the service has no constraints or the service has a constraint that matches the environment tag of the cluster environment. Matching means the environment is either included explicitly or it's not excluded. For more details look at the Constraints section in the RegisteredService page.
Monitoring
Primaza's resources are enriched with metadata that can be used to monitor the connections between workloads and services.
- Discovered RegisteredServices takes track of the Service they represent in their
annotations
. - ServiceBindings take note of the RegisteredService they're related in their
annotations
and the workloads it tampered in thestatus
. - ServiceClaims stores info about the claimed RegisteredService in its
status
Releases
Primaza is released via GitHub Releases and GitHub Container Registry (ghcr).
To create a new release you need to push a Tag respecting the Semantic Versioning (SemVer) specification.
Once a SemVer tag is pushed, a GitHub Action will run. The Action builds and push the Primaza docker images, bakes the manifests, and creates a draft release.
The pushed images are the following:
- ghcr.io/primaza/primaza: the Control Plane image
- ghcr.io/primaza/primaza-agentapp: the Application Agent image
- ghcr.io/primaza/primaza-agentsvc: the Service Agent image
The manifests baked are the following:
application_namespace_config_<TAG>.yaml
: manifests for configuring an Application Namespaceservice_namespace_config_<TAG>.yaml
: manifests for configuring a Service Namespacecrds_config_<TAG>.yaml
: manifests for installing Primaza's CRDscontrol_plane_config_<TAG>.yaml
: manifests for installing the Control Plane
The manifests are published as part of the GitHub Release artifacts.
Tutorials
In this section you will find a set of tutorials for running Primaza, use it for discovering services, and for binding a service to an application.
Tenant Setup
Primaza has requirements only at namespace level. The namespaces needed for a working Primaza setup can be spread across different clusters.
In the following Multi Cluster, Single Cluster, and Single Namespace Primaza configuration are detailed.
Multi-Cluster Setup
In this tutorial you will create the following Multi-Cluster environment using primazactl
Prerequisites
Tutorial
-
Create the Main Cluster
kind create cluster --name main
-
Install the Cert-Manager on Main Cluster
kubectl apply \ -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml \ --kubeconfig <(kind get kubeconfig --name main) kubectl rollout status -n cert-manager deploy/cert-manager-webhook -w --timeout=120s \ --kubeconfig <(kind get kubeconfig --name main)
-
Create the Worker Cluster
kind create cluster --name worker
-
Install the Cert-Manager on Worker Cluster
kubectl apply \ -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml \ --kubeconfig <(kind get kubeconfig --name worker) kubectl rollout status -n cert-manager deploy/cert-manager-webhook -w --timeout=120s \ --kubeconfig <(kind get kubeconfig --name worker)
-
Create a Primaza Tenant
primazactl create tenant primaza-mytenant \ --version latest \ --context kind-main
-
Join the Worker cluster
primazactl join cluster \ --version latest \ --tenant primaza-mytenant \ --cluster-environment worker \ --environment demo \ --context kind-worker \ --tenant-context kind-main
-
Create an Application Namespace named "applications" in the Worker Cluster
primazactl create application-namespace applications \ --version latest \ --tenant primaza-mytenant \ --cluster-environment worker \ --context kind-worker \ --tenant-context kind-main
-
Create a Service Namespace named "services" in the Worker Cluster
primazactl create service-namespace services \ --version latest \ --tenant primaza-mytenant \ --cluster-environment worker \ --context kind-worker \ --tenant-context kind-main
Single Cluster Setup
In this tutorial you will create the following Single Cluster Primaza setup using primazactl
Prerequisites
Tutorial
-
Create the Main Cluster
kind create cluster --name main
-
Install the Cert-Manager on Main Cluster
kubectl apply \ -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml \ --kubeconfig <(kind get kubeconfig --name main) kubectl rollout status -n cert-manager deploy/cert-manager-webhook -w --timeout=120s \ --kubeconfig <(kind get kubeconfig --name main)
-
Create a Primaza Tenant
primazactl create tenant primaza-mytenant \ --version latest \ --context kind-main
-
Join the Main cluster
ip=$(docker container inspect main-control-plane --format '{{.NetworkSettings.Networks.kind.IPAddress}}') kind get kubeconfig --name main | \ sed 's/server: .*$/server: https:\/\/'"$ip"':6443/g' > /tmp/kc-primaza-single-setup primazactl join cluster \ --version latest \ --tenant primaza-mytenant \ --cluster-environment main \ --environment demo \ --context kind-main \ --tenant-context kind-main \ --kubeconfig /tmp/kc-primaza-single-setup
-
Create an Application Namespace named "applications" in the Main Cluster
primazactl create application-namespace applications \ --version latest \ --tenant primaza-mytenant \ --cluster-environment main \ --context kind-main \ --tenant-context kind-main
-
Create a Service Namespace named "services" in the Main Cluster
primazactl create service-namespace services \ --version latest \ --tenant primaza-mytenant \ --cluster-environment main \ --context kind-main \ --tenant-context kind-main
Single-Namespace
In this tutorial you will create the following Single-Namespace Primaza setup using primazactl
Tutorial
-
Create the Main Cluster
kind create cluster --name main
-
Install the Cert-Manager on Main Cluster
kubectl apply \ -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml \ --kubeconfig <(kind get kubeconfig --name main) kubectl rollout status -n cert-manager deploy/cert-manager-webhook -w --timeout=120s \ --kubeconfig <(kind get kubeconfig --name main)
-
Create a Primaza Tenant
primazactl create tenant primaza-mytenant \ --version latest \ --context kind-main
-
Join the Main cluster
ip=$(docker container inspect main-control-plane --format '{{.NetworkSettings.Networks.kind.IPAddress}}') kind get kubeconfig --name main | \ sed 's/server: .*$/server: https:\/\/'"$ip"':6443/g' > /tmp/kc-primaza-single-setup primazactl join cluster \ --version latest \ --tenant primaza-mytenant \ --cluster-environment main \ --environment demo \ --context kind-main \ --tenant-context kind-main \ --kubeconfig /tmp/kc-primaza-single-setup
-
Configure the "primaza-mytenant" namespace as an Application Namespace
primazactl create application-namespace primaza-mytenant \ --version latest \ --tenant primaza-mytenant \ --cluster-environment main \ --context kind-main \ --tenant-context kind-main
-
Configure the "primaza-mytenant" namespace as a Service Namespace
primazactl create service-namespace primaza-mytenant \ --version latest \ --tenant primaza-mytenant \ --cluster-environment main \ --context kind-main \ --tenant-context kind-main
Discovery
In this section you can find tutorials for discovering services and registering them in Primaza.
The following approaches can be used for registering services in Primaza:
- Manually Register a service
- Use the Primaza's Service Discovery feature for automatic Service discovery
- Configure a third party mechanism that creates registered services
Manual Registration of a Registered Service
To manually register a service you need to create the RegisteredService and, if needed, a Secret containing service's secret data.
Tutorial
The tutorial is thought to be executed in Multi-Cluster setup. Tune-it as needed if running in a different Primaza setup.
-
Configure the shell
TENANT="primaza-mytenant" TENANT_CLUSTER_CONTEXT="kind-main" SERVICE_NAMESPACE="services" # would be primaza-mytenant in Single-Namespace scenario
-
Create a PostgreSQL Service
cat << EOF | kubectl apply -f - \ --context "$TENANT_CLUSTER_CONTEXT" \ --namespace "$SERVICE_NAMESPACE" apiVersion: v1 kind: Secret metadata: name: postgresql stringData: database: postgresql-db username: postgresql-user password: postgresql-passwd --- apiVersion: apps/v1 kind: Deployment metadata: name: postgresql labels: app: postgresql spec: replicas: 1 selector: matchLabels: app: postgresql template: metadata: labels: app: postgresql spec: containers: - name: postgresql image: postgres:13 imagePullPolicy: IfNotPresent env: - name: POSTGRES_DB_FILE value: /secrets/database - name: POSTGRES_PASSWORD_FILE value: /secrets/password - name: POSTGRES_USER_FILE value: /secrets/username - name: PGDATA value: /tmp/data volumeMounts: - name: postgresql mountPath: "/secrets" readOnly: true ports: - name: postgresql containerPort: 5432 volumes: - name: postgresql secret: secretName: postgresql --- apiVersion: v1 kind: Service metadata: labels: app: postgresql name: postgresql spec: ports: - port: 5432 protocol: TCP targetPort: 5432 selector: app: postgresql EOF
-
Create the RegisteredService and the Service Endpoint Definition Secret for the PostgreSQL Service in Primaza's Control Plane
cat << EOF | kubectl apply -f - \ --context "$TENANT_CLUSTER_CONTEXT" \ --namespace "$TENANT" apiVersion: v1 kind: Secret metadata: name: postgresql stringData: username: postgresql-user password: postgresql-passwd --- apiVersion: primaza.io/v1alpha1 kind: RegisteredService metadata: name: postgres spec: serviceClassIdentity: - name: type value: database - name: engine value: postgres - name: version value: "13" serviceEndpointDefinition: - name: database value: postgres - name: host value: postgresql.$SERVICE_NAMESPACE.svc.cluster.local - name: port value: "5432" - name: password valueFromSecret: name: postgresql key: password - name: username valueFromSecret: name: postgresql key: username EOF
In-cluster Service Discovery
In this tutorial you will configure the Primaza's Service Discovery mechanism to automatically discover services for you.
Tutorial
The tutorial is thought to be executed in Multi-Cluster setup. Tune-it as needed if running in a different Primaza setup.
-
Configure the shell
TENANT="primaza-mytenant" TENANT_CLUSTER_CONTEXT="kind-main" TARGET_CLUSTER_CONTEXT="kind-worker" # would be main in Single-Cluster and Single-Namespace scenarios SERVICE_NAMESPACE="services" # would be primaza-mytenant in Single-Namespace scenario
-
Install the MondoDB Operator
helm repo add mongodb https://mongodb.github.io/helm-charts helm repo update helm install community-operator mongodb/community-operator \ --namespace "$SERVICE_NAMESPACE" \ --kube-context "$TARGET_CLUSTER_CONTEXT" \ --create-namespace
-
Create the MongoDB Database
cat << EOF | kubectl apply -f - \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$SERVICE_NAMESPACE" apiVersion: mongodbcommunity.mongodb.com/v1 kind: MongoDBCommunity metadata: name: mongodb spec: members: 3 type: ReplicaSet version: "6.0.5" security: authentication: modes: ["SCRAM"] users: - name: my-user db: admin passwordSecretRef: # a reference to the secret that will be used to generate the user's password name: mongodb-my-user-password roles: - name: clusterAdmin db: admin - name: userAdminAnyDatabase db: admin scramCredentialsSecretName: my-scram additionalMongodConfig: storage.wiredTiger.engineConfig.journalCompressor: zlib --- apiVersion: v1 kind: Secret metadata: name: mongodb-my-user-password type: Opaque stringData: password: $(tr -cd '[:alnum:]' < /dev/urandom | fold -w16 | head -n1) EOF
-
Grant Primaza's Service Agent the right to look for MongoDB instances
cat << EOF | kubectl apply -f - \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$SERVICE_NAMESPACE" apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: primaza-svc-community-mongodb rules: - apiGroups: - mongodbcommunity.mongodb.com resources: - mongodbcommunity verbs: - get - list - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: primaza-svc-community-mongodb roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: primaza-svc-community-mongodb subjects: - kind: ServiceAccount name: primaza-svc-agent namespace: $SERVICE_NAMESPACE EOF
-
Deploy the ServiceClass
cat << EOF | kubectl apply -f - \ --context "$TENANT_CLUSTER_CONTEXT" \ --namespace "$TENANT" apiVersion: primaza.io/v1alpha1 kind: ServiceClass metadata: name: mongodb spec: serviceClassIdentity: - name: type value: database - name: operator value: community - name: engine value: mongodb resource: apiVersion: mongodbcommunity.mongodb.com/v1 kind: MongoDBCommunity serviceEndpointDefinitionMappings: secretRefFields: - name: password secretName: jsonPath: .spec.users[0].passwordSecretRef.name secretKey: constant: password resourceFields: - name: username jsonPath: .spec.users[0].name secret: true - name: database jsonPath: .spec.users[0].db secret: false - name: host jsonPath: .metadata.name secret: false EOF
-
Wait for the RegisteredService to be created
until kubectl get registeredservices mongodb \ --context "$TENANT_CLUSTER_CONTEXT" \ --namespace "$TENANT" \ --output yaml; \ do sleep 10; done
Amazon Web Services Controllers for Kubernetes Service Discovery
In this tutorial you will use the AWS Controllers for Kubernetes to create an RDS Database and you will configure the Primaza's Service Discovery mechanism to automatically discover the service for you.
Tutorial
The tutorial is thought to be executed in Multi-Cluster setup. Tune-it as needed if running in a different Primaza setup.
-
Configure the shell
TENANT="primaza-mytenant" TENANT_CLUSTER_CONTEXT="kind-main" TARGET_CLUSTER_CONTEXT="kind-worker" # would be kind-main in Single-Cluster and Single-Namespace scenarios SERVICE_NAMESPACE="services" # would be primaza-mytenant in Single-Namespace scenario AWS_REGION=$( aws configure get region ) AWS_RDS_RELEASE_VERSION=$( \ curl -sL https://api.github.com/repos/aws-controllers-k8s/rds-controller/releases/latest | \ grep '"tag_name":' | cut -d'"' -f4 | tr -d "v" )
-
Install the ACK RDS operator
aws ecr-public get-login-password --region "us-east-1" | \ helm registry login --username "AWS" --password-stdin "public.ecr.aws" helm upgrade --install "ack-rds-controller" \ "oci://public.ecr.aws/aws-controllers-k8s/rds-chart" \ --namespace "$SERVICE_NAMESPACE" \ --create-namespace \ --kube-context "$TARGET_CLUSTER_CONTEXT" \ --version="$AWS_RDS_RELEASE_VERSION" \ --set=aws.region="$AWS_REGION" \ --set=installScope=namespace kubectl set env "deployment/ack-rds-controller-rds-chart" \ AWS_ACCESS_KEY_ID="$( aws configure get aws_access_key_id )" \ AWS_SECRET_ACCESS_KEY="$( aws configure get aws_secret_access_key )" \ --namespace "$SERVICE_NAMESPACE" \ --context "$TARGET_CLUSTER_CONTEXT"
-
Create the MongoDB Database
cat << EOF | kubectl apply -f - \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$SERVICE_NAMESPACE" apiVersion: rds.services.k8s.aws/v1alpha1 kind: DBInstance metadata: name: rds spec: allocatedStorage: 20 dbInstanceClass: db.t3.micro dbInstanceIdentifier: primaza-ack-tutorial engine: postgres engineVersion: "14" masterUsername: "postgres" masterUserPassword: namespace: "$SERVICE_NAMESPACE" name: rds-password key: password --- apiVersion: v1 kind: Secret metadata: name: rds-password stringData: password: $(tr -cd '[:alnum:]' < /dev/urandom | fold -w16 | head -n1) EOF
-
Grant Primaza's Service Agent the right to look for ACK DBInstances
cat << EOF | kubectl apply -f - \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$SERVICE_NAMESPACE" apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: primaza-svc-ack-rds rules: - apiGroups: - rds.services.k8s.aws resources: - dbinstances verbs: - get - list - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: primaza-svc-ack-rds roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: primaza-svc-ack-rds subjects: - kind: ServiceAccount name: primaza-svc-agent namespace: $SERVICE_NAMESPACE EOF
-
Deploy the ServiceClass
cat << EOF | kubectl apply -f - \ --context "$TENANT_CLUSTER_CONTEXT" \ --namespace "$TENANT" apiVersion: primaza.io/v1alpha1 kind: ServiceClass metadata: name: rds spec: serviceClassIdentity: - name: type value: database - name: operator value: ack - name: provider value: aws resource: apiVersion: rds.services.k8s.aws/v1alpha1 kind: DBInstance serviceEndpointDefinitionMappings: secretRefFields: - name: password secretName: jsonPath: .spec.masterUserPassword.name secretKey: jsonPath: .spec.masterUserPassword.key resourceFields: - name: username jsonPath: .spec.masterUsername secret: true - name: region jsonPath: .status.ackResourceMetadata.region secret: false - name: port jsonPath: .status.endpoint.port secret: false - name: host jsonPath: .status.endpoint.address EOF
-
Wait for the RegisteredService to be created
until kubectl get registeredservices rds \ --context "$TENANT_CLUSTER_CONTEXT" \ --namespace "$TENANT" \ --output yaml do sleep 10; done
Claiming
Once you have a RegisteredService, you can claim it for one or more workloads. The following examples uses the service from the Manual Registration tutorial.
A ServiceClaim can select the workload to use filtering by name or labels.
Finally, you can claim from the Control Plane or from an Application Namespace.
Workflow | Single Namespace | All Namespaces | By Label | By Name |
---|---|---|---|---|
Control Plane | ✅ | ✅ | ✅ | ✅ |
Application Namespace | ✅ | ❌ | ✅ | ✅ |
Control Plane Workflow
If you create the ServiceClaim in the Control Plane you can define whether it applies to all the Application Namespaces in the Environment or it's just for one specific namespace.
This filter is implemented by the mean of the Claim's property applicationClusterContext
.
For examples refer to Filtering by Name or Filtering by Labels tutorials for Environment wide ServiceClaim examples, and to Application Namespace Scoped tutorial.
Application Namespace Workflow
When a Service Claim is created in an Application Namespace, Primaza pushes the ServiceBinding and Secret just to this namespace.
In this case, indeed, the Application Agent tampers and send the ServiceClaim to the Control Plane. The Control Plane, in turn, pushes the ServiceBinding and Secret only to the ApplicationNamespace in which the ServiceClaim was created.
By Application Name
When claiming a service, you can explicitly declare the target workload for the binding. Primaza's Application Agent will project binding data just into that workload.
To obtain this result, you need to declare in the ServiceClaim how to identify the workload using the application
property.
Tutorial
-
Configure the shell
TENANT="primaza-mytenant" TENANT_CLUSTER_CONTEXT="kind-main" TARGET_CLUSTER_CONTEXT="kind-worker" # it would be main in Single-Cluster and Single-Namespace scenarios APPLICATION_NAMESPACE="applications" # it would be primaza-mytenant in Single-Namespace scenario
-
Create a RegisteredService for the service to bind to our application
cat << EOF | kubectl apply -f - \ --context "$TENANT_CLUSTER_CONTEXT" \ --namespace "$TENANT" apiVersion: primaza.io/v1alpha1 kind: RegisteredService metadata: name: claim-by-name spec: serviceClassIdentity: - name: type value: dummy - name: scope value: claim-by-name serviceEndpointDefinition: - name: url value: https://my-app-by-name-service.dev - name: password value: SomeoneThinksImAPassword EOF
-
Create our target application
kubectl create deployment my-app-name --image bash:latest \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" \ -- sleep infinity
-
Create the ServiceClaim in Primaza's Control Plane
cat << EOF | kubectl apply -f - \ --context "$TENANT_CLUSTER_CONTEXT" \ --namespace "$TENANT" apiVersion: primaza.io/v1alpha1 kind: ServiceClaim metadata: name: my-app-dummy-by-name spec: serviceClassIdentity: - name: type value: dummy - name: scope value: claim-by-name serviceEndpointDefinitionKeys: - url - password target: environmentTag: demo application: kind: Deployment apiVersion: apps/v1 name: my-app-name EOF
-
Wait for Service Binding to be created and Resolved
until kubectl get servicebindings my-app-dummy-by-name \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" do sleep 2; done kubectl wait --for=condition=Bound servicebindings my-app-dummy-by-name \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE"
-
Read data injected in the workload
until kubectl exec "pod/$(kubectl get pod \ -l app=my-app-name \ -o jsonpath="{.items[0].metadata.name}" \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" )" \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" \ -- bash -c '[ -d "/bindings/my-app-dummy-by-name" ] && ( ls /bindings/my-app-dummy-by-name | xargs -I@ bash -c '"'"'echo "$1: $(cat /bindings/my-app-dummy-by-name/$1)"'"'"' -- @ )' do sleep 5; done
By Application Name
When claiming a service, you can identify the target workloads for the binding by declaring the labels they should have. Primaza's Application Agent will project binding data just into all matching workloads.
To obtain this result, you need to declare in the ServiceClaim how to identify the workload using the application
property.
Tutorial
-
Configure the shell
TENANT="primaza-mytenant" TENANT_CLUSTER_CONTEXT="kind-main" TARGET_CLUSTER_CONTEXT="kind-worker" # it would be main in Single-Cluster and Single-Namespace scenarios APPLICATION_NAMESPACE="applications" # it would be primaza-mytenant in Single-Namespace scenario
-
Create a RegisteredService for the service to bind to our application
cat << EOF | kubectl apply -f - \ --context "$TENANT_CLUSTER_CONTEXT" \ --namespace "$TENANT" apiVersion: primaza.io/v1alpha1 kind: RegisteredService metadata: name: claim-by-labels spec: serviceClassIdentity: - name: type value: dummy - name: scope value: claim-by-labels serviceEndpointDefinition: - name: url value: https://my-app-by-labels-service.dev - name: password value: SomeoneThinksImAPassword EOF
-
Create our target application
kubectl create deployment my-app-labels --image bash:latest \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" \ -- sleep infinity
-
Create the ServiceClaim in Primaza's Control Plane
cat << EOF | kubectl apply -f - \ --context "$TENANT_CLUSTER_CONTEXT" \ --namespace "$TENANT" apiVersion: primaza.io/v1alpha1 kind: ServiceClaim metadata: name: my-app-dummy-by-labels spec: serviceClassIdentity: - name: type value: dummy - name: scope value: claim-by-labels serviceEndpointDefinitionKeys: - url - password target: environmentTag: demo application: kind: Deployment apiVersion: apps/v1 selector: matchLabels: app: my-app-labels EOF
-
Wait for Service Binding to be created and Resolved
until kubectl get servicebindings my-app-dummy-by-labels \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" do sleep 2; done kubectl wait --for=condition=Bound servicebindings my-app-dummy-by-labels \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE"
-
Read data injected in the workload
until kubectl exec "pod/$(kubectl get pod \ -l app=my-app-labels \ -o jsonpath="{.items[0].metadata.name}" \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" )" \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" \ -- bash -c '[ -d "/bindings/my-app-dummy-by-labels" ] && ( ls /bindings/my-app-dummy-by-labels | xargs -I@ bash -c '"'"'echo "$1: $(cat /bindings/my-app-dummy-by-labels/$1)"'"'"' -- @ )' do sleep 5; done
Application Namespace Scoped
You can create a ServiceClaim that applies to a single Application Namespace in the Environment.
To obtain this result, you need to declare in the application namespace using the applicationClusterContext
property.
This has no implication on workload filtering, so you can identify workloads in the Application Namespace either by Name or by Labels.
Tutorial
-
Configure the shell
TENANT="primaza-mytenant" TENANT_CLUSTER_CONTEXT="kind-main" TARGET_CLUSTER_CONTEXT="kind-worker" # it would be main in Single-Cluster and Single-Namespace scenarios APPLICATION_NAMESPACE="applications" # it would be primaza-mytenant in Single-Namespace scenario
-
Create a RegisteredService for the service to bind to our application
cat << EOF | kubectl apply -f - \ --context "$TENANT_CLUSTER_CONTEXT" \ --namespace "$TENANT" apiVersion: primaza.io/v1alpha1 kind: RegisteredService metadata: name: claim-for-appns spec: serviceClassIdentity: - name: type value: dummy - name: scope value: claim-for-appns serviceEndpointDefinition: - name: url value: https://my-app-for-appns-service.dev - name: password value: SomeoneThinksImAPassword EOF
-
Create our target application
kubectl create deployment my-app-appns --image bash:latest \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" \ -- sleep infinity
-
Create the ServiceClaim in Primaza's Control Plane
cat << EOF | kubectl apply -f - \ --context "$TENANT_CLUSTER_CONTEXT" \ --namespace "$TENANT" apiVersion: primaza.io/v1alpha1 kind: ServiceClaim metadata: name: my-app-dummy-for-appns spec: serviceClassIdentity: - name: type value: dummy - name: scope value: claim-for-appns serviceEndpointDefinitionKeys: - url - password target: applicationClusterContext: clusterEnvironmentName: worker namespace: applications application: kind: Deployment apiVersion: apps/v1 selector: matchLabels: app: my-app-appns EOF
-
Wait for Service Binding to be created and Resolved
until kubectl get servicebindings my-app-dummy-for-appns \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" do sleep 2; done kubectl wait --for=condition=Bound servicebindings my-app-dummy-for-appns \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE"
-
Read data injected in the workload
until kubectl exec "pod/$(kubectl get pod \ -l app=my-app-appns \ -o jsonpath="{.items[0].metadata.name}" \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" )" \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" \ -- bash -c '[ -d "/bindings/my-app-dummy-for-appns" ] && ( ls /bindings/my-app-dummy-for-appns | xargs -I@ bash -c '"'"'echo "$1: $(cat /bindings/my-app-dummy-for-appns/$1)"'"'"' -- @ )' do sleep 5; done
From Application Namespace
You can create a ServiceClaim directly from an Application Namespace. When a Service Claim is created in an Application Namespace, Primaza pushes the ServiceBinding and Secret just to this namespace.
In this case, indeed, the Application Agent tampers and send the ServiceClaim to the Control Plane. The Control Plane, in turn, pushes the ServiceBinding and Secret only to the ApplicationNamespace in which the ServiceClaim was created.
This has no implication on workload filtering, so you can identify workloads in the Application Namespace either by Name or by Labels.
Tutorial
-
Configure the shell
TENANT="primaza-mytenant" TENANT_CLUSTER_CONTEXT="kind-main" TARGET_CLUSTER_CONTEXT="kind-worker" # it would be main in Single-Cluster and Single-Namespace scenarios APPLICATION_NAMESPACE="applications" # it would be primaza-mytenant in Single-Namespace scenario
-
Create a RegisteredService for the service to bind to our application
cat << EOF | kubectl apply -f - \ --context "$TENANT_CLUSTER_CONTEXT" \ --namespace "$TENANT" apiVersion: primaza.io/v1alpha1 kind: RegisteredService metadata: name: claim-from-appns spec: serviceClassIdentity: - name: type value: dummy - name: scope value: claim-from-appns serviceEndpointDefinition: - name: url value: https://my-app-from-appns-service.dev - name: password value: SomeoneThinksImAPassword EOF
-
Create our target application
kubectl create deployment my-app-appns --image bash:latest \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" \ -- sleep infinity
-
Create the ServiceClaim in Primaza's Control Plane
cat << EOF | kubectl apply -f - \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" apiVersion: primaza.io/v1alpha1 kind: ServiceClaim metadata: name: my-app-dummy-from-appns spec: serviceClassIdentity: - name: type value: dummy - name: scope value: claim-from-appns serviceEndpointDefinitionKeys: - url - password application: kind: Deployment apiVersion: apps/v1 selector: matchLabels: app: my-app-appns EOF
-
Wait for Service Binding to be created and Resolved
until kubectl get servicebindings my-app-dummy-from-appns \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" do sleep 2; done kubectl wait --for=condition=Bound servicebindings my-app-dummy-from-appns \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE"
-
Read data injected in the workload.
until kubectl exec "pod/$(kubectl get pod \ -l app=my-app-appns \ -o jsonpath="{.items[0].metadata.name}" \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" )" \ --context "$TARGET_CLUSTER_CONTEXT" \ --namespace "$APPLICATION_NAMESPACE" \ -- bash -c '[ -d "/bindings/my-app-dummy-from-appns" ] && ( ls /bindings/my-app-dummy-from-appns | xargs -I@ bash -c '"'"'echo "$1: $(cat /bindings/my-app-dummy-from-appns/$1)"'"'"' -- @ )' do sleep 5; done