mirror of https://github.com/portainer/portainer
feat(edge/stacks): use namespace in manifest [EE-4507] (#8145)
parent
8936ae9b7a
commit
930d9e5628
|
@ -92,6 +92,8 @@ type swarmStackFromFileContentPayload struct {
|
||||||
DeploymentType portainer.EdgeStackDeploymentType `example:"0" enums:"0,1,2"`
|
DeploymentType portainer.EdgeStackDeploymentType `example:"0" enums:"0,1,2"`
|
||||||
// List of Registries to use for this stack
|
// List of Registries to use for this stack
|
||||||
Registries []portainer.RegistryID
|
Registries []portainer.RegistryID
|
||||||
|
// Uses the manifest's namespaces instead of the default one
|
||||||
|
UseManifestNamespaces bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (payload *swarmStackFromFileContentPayload) Validate(r *http.Request) error {
|
func (payload *swarmStackFromFileContentPayload) Validate(r *http.Request) error {
|
||||||
|
@ -114,7 +116,7 @@ func (handler *Handler) createSwarmStackFromFileContent(r *http.Request, dryrun
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, err := handler.edgeStacksService.BuildEdgeStack(payload.Name, payload.DeploymentType, payload.EdgeGroups, payload.Registries)
|
stack, err := handler.edgeStacksService.BuildEdgeStack(payload.Name, payload.DeploymentType, payload.EdgeGroups, payload.Registries, payload.UseManifestNamespaces)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to create Edge stack object")
|
return nil, errors.Wrap(err, "failed to create Edge stack object")
|
||||||
}
|
}
|
||||||
|
@ -197,6 +199,8 @@ type swarmStackFromGitRepositoryPayload struct {
|
||||||
DeploymentType portainer.EdgeStackDeploymentType `example:"0" enums:"0,1,2"`
|
DeploymentType portainer.EdgeStackDeploymentType `example:"0" enums:"0,1,2"`
|
||||||
// List of Registries to use for this stack
|
// List of Registries to use for this stack
|
||||||
Registries []portainer.RegistryID
|
Registries []portainer.RegistryID
|
||||||
|
// Uses the manifest's namespaces instead of the default one
|
||||||
|
UseManifestNamespaces bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (payload *swarmStackFromGitRepositoryPayload) Validate(r *http.Request) error {
|
func (payload *swarmStackFromGitRepositoryPayload) Validate(r *http.Request) error {
|
||||||
|
@ -230,7 +234,7 @@ func (handler *Handler) createSwarmStackFromGitRepository(r *http.Request, dryru
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, err := handler.edgeStacksService.BuildEdgeStack(payload.Name, payload.DeploymentType, payload.EdgeGroups, payload.Registries)
|
stack, err := handler.edgeStacksService.BuildEdgeStack(payload.Name, payload.DeploymentType, payload.EdgeGroups, payload.Registries, payload.UseManifestNamespaces)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to create edge stack object")
|
return nil, errors.Wrap(err, "failed to create edge stack object")
|
||||||
}
|
}
|
||||||
|
@ -268,6 +272,8 @@ type swarmStackFromFileUploadPayload struct {
|
||||||
// nomad deploytype is enabled only for nomad environments(endpoints)
|
// nomad deploytype is enabled only for nomad environments(endpoints)
|
||||||
DeploymentType portainer.EdgeStackDeploymentType `example:"0" enums:"0,1,2"`
|
DeploymentType portainer.EdgeStackDeploymentType `example:"0" enums:"0,1,2"`
|
||||||
Registries []portainer.RegistryID
|
Registries []portainer.RegistryID
|
||||||
|
// Uses the manifest's namespaces instead of the default one
|
||||||
|
UseManifestNamespaces bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (payload *swarmStackFromFileUploadPayload) Validate(r *http.Request) error {
|
func (payload *swarmStackFromFileUploadPayload) Validate(r *http.Request) error {
|
||||||
|
@ -303,6 +309,9 @@ func (payload *swarmStackFromFileUploadPayload) Validate(r *http.Request) error
|
||||||
}
|
}
|
||||||
payload.Registries = registries
|
payload.Registries = registries
|
||||||
|
|
||||||
|
useManifestNamespaces, _ := request.RetrieveBooleanMultiPartFormValue(r, "UseManifestNamespaces", true)
|
||||||
|
payload.UseManifestNamespaces = useManifestNamespaces
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,7 +322,7 @@ func (handler *Handler) createSwarmStackFromFileUpload(r *http.Request, dryrun b
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, err := handler.edgeStacksService.BuildEdgeStack(payload.Name, payload.DeploymentType, payload.EdgeGroups, payload.Registries)
|
stack, err := handler.edgeStacksService.BuildEdgeStack(payload.Name, payload.DeploymentType, payload.EdgeGroups, payload.Registries, payload.UseManifestNamespaces)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to create edge stack object")
|
return nil, errors.Wrap(err, "failed to create edge stack object")
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ type updateEdgeStackPayload struct {
|
||||||
Version *int
|
Version *int
|
||||||
EdgeGroups []portainer.EdgeGroupID
|
EdgeGroups []portainer.EdgeGroupID
|
||||||
DeploymentType portainer.EdgeStackDeploymentType
|
DeploymentType portainer.EdgeStackDeploymentType
|
||||||
|
// Uses the manifest's namespaces instead of the default one
|
||||||
|
UseManifestNamespaces bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (payload *updateEdgeStackPayload) Validate(r *http.Request) error {
|
func (payload *updateEdgeStackPayload) Validate(r *http.Request) error {
|
||||||
|
@ -168,6 +170,8 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request)
|
||||||
stack.ManifestPath = filesystem.ManifestFileDefaultName
|
stack.ManifestPath = filesystem.ManifestFileDefaultName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stack.UseManifestNamespaces = payload.UseManifestNamespaces
|
||||||
|
|
||||||
hasDockerEndpoint, err := hasDockerEndpoint(handler.DataStore.Endpoint(), relatedEndpointIds)
|
hasDockerEndpoint, err := hasDockerEndpoint(handler.DataStore.Endpoint(), relatedEndpointIds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return httperror.InternalServerError("Unable to check for existence of docker environment", err)
|
return httperror.InternalServerError("Unable to check for existence of docker environment", err)
|
||||||
|
|
|
@ -10,11 +10,14 @@ import (
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
"github.com/portainer/portainer/api/http/middlewares"
|
"github.com/portainer/portainer/api/http/middlewares"
|
||||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||||
|
"github.com/portainer/portainer/api/kubernetes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type configResponse struct {
|
type configResponse struct {
|
||||||
StackFileContent string
|
StackFileContent string
|
||||||
Name string
|
Name string
|
||||||
|
// Namespace to use for Kubernetes manifests, leave empty to use the namespaces defined in the manifest
|
||||||
|
Namespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
// @summary Inspect an Edge Stack for an Environment(Endpoint)
|
// @summary Inspect an Edge Stack for an Environment(Endpoint)
|
||||||
|
@ -59,12 +62,18 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace := ""
|
||||||
|
if !edgeStack.UseManifestNamespaces {
|
||||||
|
namespace = kubernetes.DefaultNamespace
|
||||||
|
}
|
||||||
|
|
||||||
if endpointutils.IsKubernetesEndpoint(endpoint) {
|
if endpointutils.IsKubernetesEndpoint(endpoint) {
|
||||||
fileName = edgeStack.ManifestPath
|
fileName = edgeStack.ManifestPath
|
||||||
|
|
||||||
if fileName == "" {
|
if fileName == "" {
|
||||||
return httperror.BadRequest("Kubernetes is not supported by this stack", errors.New("Kubernetes is not supported by this stack"))
|
return httperror.BadRequest("Kubernetes is not supported by this stack", errors.New("Kubernetes is not supported by this stack"))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stackFileContent, err := handler.FileService.GetFileContent(edgeStack.ProjectPath, fileName)
|
stackFileContent, err := handler.FileService.GetFileContent(edgeStack.ProjectPath, fileName)
|
||||||
|
@ -75,5 +84,6 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http.
|
||||||
return response.JSON(w, configResponse{
|
return response.JSON(w, configResponse{
|
||||||
StackFileContent: string(stackFileContent),
|
StackFileContent: string(stackFileContent),
|
||||||
Name: edgeStack.Name,
|
Name: edgeStack.Name,
|
||||||
|
Namespace: namespace,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,9 @@ func NewService(dataStore dataservices.DataStore) *Service {
|
||||||
func (service *Service) BuildEdgeStack(name string,
|
func (service *Service) BuildEdgeStack(name string,
|
||||||
deploymentType portainer.EdgeStackDeploymentType,
|
deploymentType portainer.EdgeStackDeploymentType,
|
||||||
edgeGroups []portainer.EdgeGroupID,
|
edgeGroups []portainer.EdgeGroupID,
|
||||||
registries []portainer.RegistryID) (*portainer.EdgeStack, error) {
|
registries []portainer.RegistryID,
|
||||||
|
useManifestNamespaces bool,
|
||||||
|
) (*portainer.EdgeStack, error) {
|
||||||
edgeStacksService := service.dataStore.EdgeStack()
|
edgeStacksService := service.dataStore.EdgeStack()
|
||||||
|
|
||||||
err := validateUniqueName(edgeStacksService.EdgeStacks, name)
|
err := validateUniqueName(edgeStacksService.EdgeStacks, name)
|
||||||
|
@ -40,13 +42,14 @@ func (service *Service) BuildEdgeStack(name string,
|
||||||
|
|
||||||
stackID := edgeStacksService.GetNextIdentifier()
|
stackID := edgeStacksService.GetNextIdentifier()
|
||||||
return &portainer.EdgeStack{
|
return &portainer.EdgeStack{
|
||||||
ID: portainer.EdgeStackID(stackID),
|
ID: portainer.EdgeStackID(stackID),
|
||||||
Name: name,
|
Name: name,
|
||||||
DeploymentType: deploymentType,
|
DeploymentType: deploymentType,
|
||||||
CreationDate: time.Now().Unix(),
|
CreationDate: time.Now().Unix(),
|
||||||
EdgeGroups: edgeGroups,
|
EdgeGroups: edgeGroups,
|
||||||
Status: make(map[portainer.EndpointID]portainer.EdgeStackStatus),
|
Status: make(map[portainer.EndpointID]portainer.EdgeStackStatus),
|
||||||
Version: 1,
|
Version: 1,
|
||||||
|
UseManifestNamespaces: useManifestNamespaces,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package kubernetes
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultNamespace is the default namespace used when no namespace is specified
|
||||||
|
DefaultNamespace = "default"
|
||||||
|
)
|
|
@ -281,6 +281,8 @@ type (
|
||||||
Version int `json:"Version"`
|
Version int `json:"Version"`
|
||||||
ManifestPath string
|
ManifestPath string
|
||||||
DeploymentType EdgeStackDeploymentType
|
DeploymentType EdgeStackDeploymentType
|
||||||
|
// Uses the manifest's namespaces instead of the default one
|
||||||
|
UseManifestNamespaces bool
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
Prune bool `json:"Prune"`
|
Prune bool `json:"Prune"`
|
||||||
|
|
|
@ -48,21 +48,33 @@
|
||||||
</editor-description>
|
</editor-description>
|
||||||
</web-editor-form>
|
</web-editor-form>
|
||||||
|
|
||||||
<web-editor-form
|
<div ng-if="$ctrl.model.DeploymentType === 1">
|
||||||
ng-if="$ctrl.model.DeploymentType === 1"
|
<div class="form-group">
|
||||||
value="$ctrl.model.StackFileContent"
|
<div class="col-sm-12">
|
||||||
yml="true"
|
<por-switch-field
|
||||||
identifier="kube-manifest-editor"
|
label="'Use namespace(s) specified from manifest'"
|
||||||
placeholder="# Define or paste the content of your manifest here"
|
tooltip="'If you have defined namespaces in your deployment file turning this on will enforce the use of those only in the deployment'"
|
||||||
on-change="($ctrl.onChangeKubeManifest)"
|
checked="$ctrl.formValues.UseManifestNamespaces"
|
||||||
>
|
on-change="($ctrl.onChangeUseManifestNamespaces)"
|
||||||
<editor-description>
|
></por-switch-field>
|
||||||
<p>
|
</div>
|
||||||
You can get more information about Kubernetes file format in the
|
</div>
|
||||||
<a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/" target="_blank">official documentation</a>.
|
|
||||||
</p>
|
<web-editor-form
|
||||||
</editor-description>
|
value="$ctrl.model.StackFileContent"
|
||||||
</web-editor-form>
|
yml="true"
|
||||||
|
identifier="kube-manifest-editor"
|
||||||
|
placeholder="# Define or paste the content of your manifest here"
|
||||||
|
on-change="($ctrl.onChangeKubeManifest)"
|
||||||
|
>
|
||||||
|
<editor-description>
|
||||||
|
<p>
|
||||||
|
You can get more information about Kubernetes file format in the
|
||||||
|
<a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/" target="_blank">official documentation</a>.
|
||||||
|
</p>
|
||||||
|
</editor-description>
|
||||||
|
</web-editor-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- actions -->
|
<!-- actions -->
|
||||||
<div class="col-sm-12 form-section-title"> Actions </div>
|
<div class="col-sm-12 form-section-title"> Actions </div>
|
||||||
|
|
|
@ -22,6 +22,13 @@ export class EditEdgeStackFormController {
|
||||||
this.onChangeDeploymentType = this.onChangeDeploymentType.bind(this);
|
this.onChangeDeploymentType = this.onChangeDeploymentType.bind(this);
|
||||||
this.removeLineBreaks = this.removeLineBreaks.bind(this);
|
this.removeLineBreaks = this.removeLineBreaks.bind(this);
|
||||||
this.onChangeFileContent = this.onChangeFileContent.bind(this);
|
this.onChangeFileContent = this.onChangeFileContent.bind(this);
|
||||||
|
this.onChangeUseManifestNamespaces = this.onChangeUseManifestNamespaces.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeUseManifestNamespaces(value) {
|
||||||
|
this.$scope.$evalAsync(() => {
|
||||||
|
this.model.UseManifestNamespaces = value;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
hasKubeEndpoint() {
|
hasKubeEndpoint() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ export default class CreateEdgeStackViewController {
|
||||||
ComposeFilePathInRepository: '',
|
ComposeFilePathInRepository: '',
|
||||||
Groups: [],
|
Groups: [],
|
||||||
DeploymentType: 0,
|
DeploymentType: 0,
|
||||||
|
UseManifestNamespaces: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -166,30 +167,32 @@ export default class CreateEdgeStackViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
createStackFromFileContent(name) {
|
createStackFromFileContent(name) {
|
||||||
const { StackFileContent, Groups, DeploymentType } = this.formValues;
|
const { StackFileContent, Groups, DeploymentType, UseManifestNamespaces } = this.formValues;
|
||||||
|
|
||||||
return this.EdgeStackService.createStackFromFileContent({
|
return this.EdgeStackService.createStackFromFileContent({
|
||||||
name,
|
name,
|
||||||
StackFileContent,
|
StackFileContent,
|
||||||
EdgeGroups: Groups,
|
EdgeGroups: Groups,
|
||||||
DeploymentType,
|
DeploymentType,
|
||||||
|
UseManifestNamespaces,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
createStackFromFileUpload(name) {
|
createStackFromFileUpload(name) {
|
||||||
const { StackFile, Groups, DeploymentType } = this.formValues;
|
const { StackFile, Groups, DeploymentType, UseManifestNamespaces } = this.formValues;
|
||||||
return this.EdgeStackService.createStackFromFileUpload(
|
return this.EdgeStackService.createStackFromFileUpload(
|
||||||
{
|
{
|
||||||
Name: name,
|
Name: name,
|
||||||
EdgeGroups: Groups,
|
EdgeGroups: Groups,
|
||||||
DeploymentType,
|
DeploymentType,
|
||||||
|
UseManifestNamespaces,
|
||||||
},
|
},
|
||||||
StackFile
|
StackFile
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
createStackFromGitRepository(name) {
|
createStackFromGitRepository(name) {
|
||||||
const { Groups, DeploymentType } = this.formValues;
|
const { Groups, DeploymentType, UseManifestNamespaces } = this.formValues;
|
||||||
const repositoryOptions = {
|
const repositoryOptions = {
|
||||||
RepositoryURL: this.formValues.RepositoryURL,
|
RepositoryURL: this.formValues.RepositoryURL,
|
||||||
RepositoryReferenceName: this.formValues.RepositoryReferenceName,
|
RepositoryReferenceName: this.formValues.RepositoryReferenceName,
|
||||||
|
@ -203,6 +206,7 @@ export default class CreateEdgeStackViewController {
|
||||||
name,
|
name,
|
||||||
EdgeGroups: Groups,
|
EdgeGroups: Groups,
|
||||||
DeploymentType,
|
DeploymentType,
|
||||||
|
UseManifestNamespaces,
|
||||||
},
|
},
|
||||||
repositoryOptions
|
repositoryOptions
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,10 +11,20 @@ class KubeManifestFormController {
|
||||||
this.onChangeFormValues = this.onChangeFormValues.bind(this);
|
this.onChangeFormValues = this.onChangeFormValues.bind(this);
|
||||||
this.onChangeFile = this.onChangeFile.bind(this);
|
this.onChangeFile = this.onChangeFile.bind(this);
|
||||||
this.onChangeMethod = this.onChangeMethod.bind(this);
|
this.onChangeMethod = this.onChangeMethod.bind(this);
|
||||||
|
this.onChangeUseManifestNamespaces = this.onChangeUseManifestNamespaces.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeFormValues(values) {
|
onChangeFormValues(newValues) {
|
||||||
this.formValues = values;
|
return this.$async(async () => {
|
||||||
|
this.formValues = {
|
||||||
|
...this.formValues,
|
||||||
|
...newValues,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeUseManifestNamespaces(value) {
|
||||||
|
this.onChangeFormValues({ UseManifestNamespaces: value });
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeFileContent(value) {
|
onChangeFileContent(value) {
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<por-switch-field
|
||||||
|
label="'Use namespace(s) specified from manifest'"
|
||||||
|
tooltip="'If you have defined namespaces in your deployment file turning this on will enforce the use of those only in the deployment'"
|
||||||
|
checked="$ctrl.formValues.UseManifestNamespaces"
|
||||||
|
on-change="($ctrl.onChangeUseManifestNamespaces)"
|
||||||
|
></por-switch-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-12 form-section-title"> Build method </div>
|
<div class="col-sm-12 form-section-title"> Build method </div>
|
||||||
<box-selector radio-name="'method'" value="$ctrl.state.Method" options="$ctrl.methodOptions" on-change="($ctrl.onChangeMethod)"></box-selector>
|
<box-selector radio-name="'method'" value="$ctrl.state.Method" options="$ctrl.methodOptions" on-change="($ctrl.onChangeMethod)"></box-selector>
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ export class EditEdgeStackViewController {
|
||||||
this.formValues = {
|
this.formValues = {
|
||||||
StackFileContent: file,
|
StackFileContent: file,
|
||||||
EdgeGroups: this.stack.EdgeGroups,
|
EdgeGroups: this.stack.EdgeGroups,
|
||||||
|
UseManifestNamespaces: this.stack.UseManifestNamespaces,
|
||||||
DeploymentType: this.stack.DeploymentType,
|
DeploymentType: this.stack.DeploymentType,
|
||||||
};
|
};
|
||||||
this.oldFileContent = this.formValues.StackFileContent;
|
this.oldFileContent = this.formValues.StackFileContent;
|
||||||
|
@ -79,7 +80,7 @@ export class EditEdgeStackViewController {
|
||||||
async deployStackAsync() {
|
async deployStackAsync() {
|
||||||
this.state.actionInProgress = true;
|
this.state.actionInProgress = true;
|
||||||
try {
|
try {
|
||||||
if (this.originalFileContent != this.formValues.StackFileContent) {
|
if (this.originalFileContent != this.formValues.StackFileContent || this.formValues.UseManifestNamespaces !== this.stack.UseManifestNamespaces) {
|
||||||
this.formValues.Version = this.stack.Version + 1;
|
this.formValues.Version = this.stack.Version + 1;
|
||||||
}
|
}
|
||||||
await this.EdgeStackService.updateStack(this.stack.Id, this.formValues);
|
await this.EdgeStackService.updateStack(this.stack.Id, this.formValues);
|
||||||
|
|
|
@ -3,13 +3,3 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.switchValues {
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
|
@ -42,11 +42,7 @@ export function SwitchField({
|
||||||
return (
|
return (
|
||||||
<label className={clsx(styles.root, fieldClass)}>
|
<label className={clsx(styles.root, fieldClass)}>
|
||||||
<span
|
<span
|
||||||
className={clsx(
|
className={clsx('text-left space-right control-label !p-0', labelClass)}
|
||||||
'text-left space-right control-label',
|
|
||||||
styles.label,
|
|
||||||
labelClass
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
{tooltip && (
|
{tooltip && (
|
||||||
|
|
Loading…
Reference in New Issue