Skip to content

Commit

Permalink
[BACKPORT 2.18.5][PLAT-10616] Support SpecificGflags in audit
Browse files Browse the repository at this point in the history
Summary:
Original commit: b826052/D29248
Added support for SpecificGflags in gflags audit.
Also covered the case, when read replica has overriden gflags.
In that case audit will contain "readonly_cluster_gflags" field, for example:
```
{
  "gflags": {
    "master": [
      {
        "name": "master",
        "old": "1",
        "new": "2",
        "default": "master"
      }
    ],
    "tserver": [
      {
        "name": "tserver",
        "old": "1",
        "new": "3",
        "default": "tserver"
      }
    ]
  },
  "readonly_cluster_gflags": {
    "master": [
      {
        "name": "master",
        "old": "5",
        "new": "1",
        "default": "master"
      }
    ],
    "tserver": [
      {
        "name": "tserver2",
        "old": "2",
        "new": null,
        "default": "tserver2"
      },
      {
        "name": "tserver",
        "old": null,
        "new": "2",
        "default": "tserver"
      }
    ]
  }
}
```

Test Plan:
sbt test
Manual testing:
Run gflags upgrade on universe and verify audit data

Reviewers: #yba-api-review, cwang, anijhawan, yshchetinin

Reviewed By: #yba-api-review, cwang, anijhawan

Subscribers: dshubin, yugaware

Tags: #jenkins-ready

Differential Revision: https://phorge.dev.yugabyte.com/D31063
  • Loading branch information
yorq authored and shubin-yb committed Dec 18, 2023
1 parent 9c842fb commit a71c801
Show file tree
Hide file tree
Showing 6 changed files with 327 additions and 98 deletions.
Expand Up @@ -1135,4 +1135,15 @@ public static Set<String> getDeletedGFlags(
.filter(flag -> !updatedGFlags.containsKey(flag))
.collect(Collectors.toSet());
}

public static boolean areGflagsInheritedFromPrimary(
UniverseDefinitionTaskParams.Cluster cluster) {
if (cluster.clusterType != UniverseDefinitionTaskParams.ClusterType.ASYNC) {
return false;
}
if (cluster.userIntent.specificGFlags == null) {
return true;
}
return cluster.userIntent.specificGFlags.isInheritFromPrimary();
}
}
Expand Up @@ -8,6 +8,7 @@
import com.yugabyte.yw.common.config.RuntimeConfGetter;
import com.yugabyte.yw.common.config.RuntimeConfigFactory;
import com.yugabyte.yw.common.config.UniverseConfKeys;
import com.yugabyte.yw.controllers.handlers.GFlagsAuditHandler;
import com.yugabyte.yw.controllers.handlers.UpgradeUniverseHandler;
import com.yugabyte.yw.forms.CertsRotateParams;
import com.yugabyte.yw.forms.GFlagsUpgradeParams;
Expand All @@ -19,7 +20,6 @@
import com.yugabyte.yw.forms.SystemdUpgradeParams;
import com.yugabyte.yw.forms.ThirdpartySoftwareUpgradeParams;
import com.yugabyte.yw.forms.TlsToggleParams;
import com.yugabyte.yw.forms.UniverseDefinitionTaskParams.UserIntent;
import com.yugabyte.yw.forms.UpgradeTaskParams;
import com.yugabyte.yw.forms.VMImageUpgradeParams;
import com.yugabyte.yw.models.Audit;
Expand All @@ -32,7 +32,6 @@
import io.swagger.annotations.Authorization;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import play.libs.Json;
import play.mvc.Http;
import play.mvc.Http.Request;
import play.mvc.Result;
Expand All @@ -49,6 +48,8 @@ public class UpgradeUniverseController extends AuthenticatedController {

@Inject RuntimeConfGetter confGetter;

@Inject GFlagsAuditHandler gFlagsAuditHandler;

/**
* API that restarts all nodes in the universe. Supports rolling and non-rolling restart
*
Expand Down Expand Up @@ -403,19 +404,12 @@ private <T extends UpgradeTaskParams> Result requestHandler(
universe.getName(),
universe.getUniverseUUID(),
customer.getUuid());

// prevent race condition in the case userIntent updates before we createAuditEntry
UserIntent userIntent =
Json.fromJson(
Json.toJson(universe.getUniverseDetails().getPrimaryCluster().userIntent),
UserIntent.class);
UUID taskUuid = serviceMethod.upgrade(requestParams, customer, universe);
JsonNode additionalDetails = null;
if (type.equals(GFlagsUpgradeParams.class)) {
additionalDetails =
upgradeUniverseHandler.constructGFlagAuditPayload(
(GFlagsUpgradeParams) requestParams, userIntent);
gFlagsAuditHandler.constructGFlagAuditPayload((GFlagsUpgradeParams) requestParams);
}
UUID taskUuid = serviceMethod.upgrade(requestParams, customer, universe);
auditService()
.createAuditEntryWithReqBody(
request,
Expand Down
@@ -0,0 +1,135 @@
// Copyright (c) YugaByte, Inc.

package com.yugabyte.yw.controllers.handlers;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Sets;
import com.yugabyte.yw.commissioner.tasks.UniverseTaskBase;
import com.yugabyte.yw.common.PlatformServiceException;
import com.yugabyte.yw.common.gflags.GFlagDetails;
import com.yugabyte.yw.common.gflags.GFlagDiffEntry;
import com.yugabyte.yw.common.gflags.GFlagsAuditPayload;
import com.yugabyte.yw.common.gflags.GFlagsUtil;
import com.yugabyte.yw.forms.GFlagsUpgradeParams;
import com.yugabyte.yw.forms.UniverseDefinitionTaskParams;
import com.yugabyte.yw.models.Universe;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;

@Singleton
@Slf4j
public class GFlagsAuditHandler {

private final GFlagsValidationHandler gFlagsValidationHandler;

@Inject
public GFlagsAuditHandler(GFlagsValidationHandler gFlagsValidationHandler) {
this.gFlagsValidationHandler = gFlagsValidationHandler;
}

public JsonNode constructGFlagAuditPayload(GFlagsUpgradeParams requestParams) {
Universe universe = Universe.getOrBadRequest(requestParams.getUniverseUUID());
Map<UUID, UniverseDefinitionTaskParams.Cluster> newVersionsOfClusters =
requestParams.getNewVersionsOfClusters(universe);
Map<UUID, UniverseDefinitionTaskParams.Cluster> oldVersionsOfClusters =
universe.getUniverseDetails().clusters.stream()
.collect(Collectors.toMap(c -> c.uuid, c -> c));
String softwareVersion = requestParams.getPrimaryCluster().userIntent.ybSoftwareVersion;

Map<String, GFlagsAuditPayload> auditPayload = new HashMap<>();

for (UniverseDefinitionTaskParams.Cluster newCluster : newVersionsOfClusters.values()) {
Map<String, String> newMasterGFlags =
GFlagsUtil.getBaseGFlags(
UniverseTaskBase.ServerType.MASTER, newCluster, newVersionsOfClusters.values());
Map<String, String> newTserverGFlags =
GFlagsUtil.getBaseGFlags(
UniverseTaskBase.ServerType.TSERVER, newCluster, newVersionsOfClusters.values());
UniverseDefinitionTaskParams.Cluster oldCluster = oldVersionsOfClusters.get(newCluster.uuid);
Map<String, String> oldMasterGFlags =
GFlagsUtil.getBaseGFlags(
UniverseTaskBase.ServerType.MASTER, oldCluster, oldVersionsOfClusters.values());
Map<String, String> oldTserverGFlags =
GFlagsUtil.getBaseGFlags(
UniverseTaskBase.ServerType.TSERVER, oldCluster, oldVersionsOfClusters.values());
if (!Objects.equals(newMasterGFlags, oldMasterGFlags)
|| !Objects.equals(newTserverGFlags, oldTserverGFlags)) {
GFlagsAuditPayload payload = new GFlagsAuditPayload();

payload.master =
generateGFlagEntries(
oldMasterGFlags,
newMasterGFlags,
UniverseTaskBase.ServerType.MASTER.toString(),
softwareVersion);
payload.tserver =
generateGFlagEntries(
oldTserverGFlags,
newTserverGFlags,
UniverseTaskBase.ServerType.TSERVER.toString(),
softwareVersion);

if (newCluster.clusterType == UniverseDefinitionTaskParams.ClusterType.PRIMARY) {
auditPayload.put("gflags", payload);
} else if (!GFlagsUtil.areGflagsInheritedFromPrimary(newCluster)
|| !GFlagsUtil.areGflagsInheritedFromPrimary(oldCluster)) {
auditPayload.put("readonly_cluster_gflags", payload);
}
}
}
ObjectMapper mapper = new ObjectMapper();
return mapper.valueToTree(auditPayload);
}

public List<GFlagDiffEntry> generateGFlagEntries(
Map<String, String> oldGFlags,
Map<String, String> newGFlags,
String serverType,
String softwareVersion) {
List<GFlagDiffEntry> gFlagChanges = new ArrayList<>();
if (oldGFlags == null) {
oldGFlags = new HashMap<>();
}
if (newGFlags == null) {
newGFlags = new HashMap<>();
}
GFlagDiffEntry tEntry;
Collection<String> modifiedGFlags = Sets.union(oldGFlags.keySet(), newGFlags.keySet());

for (String gFlagName : modifiedGFlags) {
String oldGFlagValue = oldGFlags.getOrDefault(gFlagName, null);
String newGFlagValue = newGFlags.getOrDefault(gFlagName, null);
if (oldGFlagValue == null || !oldGFlagValue.equals(newGFlagValue)) {
String defaultGFlagValue = getGFlagDefaultValue(softwareVersion, serverType, gFlagName);
tEntry = new GFlagDiffEntry(gFlagName, oldGFlagValue, newGFlagValue, defaultGFlagValue);
gFlagChanges.add(tEntry);
}
}

return gFlagChanges;
}

private String getGFlagDefaultValue(String softwareVersion, String serverType, String gFlagName) {
GFlagDetails defaultGFlag;
String defaultGFlagValue;
try {
defaultGFlag =
gFlagsValidationHandler.getGFlagsMetadata(softwareVersion, serverType, gFlagName);
} catch (IOException | PlatformServiceException e) {
defaultGFlag = null;
}
defaultGFlagValue = (defaultGFlag == null) ? null : defaultGFlag.defaultValue;
return defaultGFlagValue;
}
}
Expand Up @@ -2,14 +2,10 @@

package com.yugabyte.yw.controllers.handlers;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.yugabyte.yw.commissioner.Commissioner;
import com.yugabyte.yw.commissioner.Common.CloudType;
import com.yugabyte.yw.commissioner.tasks.UniverseTaskBase.ServerType;
import com.yugabyte.yw.common.KubernetesManagerFactory;
import com.yugabyte.yw.common.PlatformServiceException;
import com.yugabyte.yw.common.Util;
Expand All @@ -19,9 +15,6 @@
import com.yugabyte.yw.common.config.ProviderConfKeys;
import com.yugabyte.yw.common.config.RuntimeConfGetter;
import com.yugabyte.yw.common.config.RuntimeConfigFactory;
import com.yugabyte.yw.common.gflags.GFlagDetails;
import com.yugabyte.yw.common.gflags.GFlagDiffEntry;
import com.yugabyte.yw.common.gflags.GFlagsAuditPayload;
import com.yugabyte.yw.common.gflags.GFlagsUtil;
import com.yugabyte.yw.forms.CertsRotateParams;
import com.yugabyte.yw.forms.GFlagsUpgradeParams;
Expand All @@ -42,12 +35,6 @@
import com.yugabyte.yw.models.Provider;
import com.yugabyte.yw.models.Universe;
import com.yugabyte.yw.models.helpers.TaskType;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -60,7 +47,6 @@ public class UpgradeUniverseHandler {
private final Commissioner commissioner;
private final KubernetesManagerFactory kubernetesManagerFactory;
private final RuntimeConfigFactory runtimeConfigFactory;
private final GFlagsValidationHandler gFlagsValidationHandler;
private final YbcManager ybcManager;
private final RuntimeConfGetter confGetter;
private final CertificateHelper certificateHelper;
Expand All @@ -70,14 +56,12 @@ public UpgradeUniverseHandler(
Commissioner commissioner,
KubernetesManagerFactory kubernetesManagerFactory,
RuntimeConfigFactory runtimeConfigFactory,
GFlagsValidationHandler gFlagsValidationHandler,
YbcManager ybcManager,
RuntimeConfGetter confGetter,
CertificateHelper certificateHelper) {
this.commissioner = commissioner;
this.kubernetesManagerFactory = kubernetesManagerFactory;
this.runtimeConfigFactory = runtimeConfigFactory;
this.gFlagsValidationHandler = gFlagsValidationHandler;
this.ybcManager = ybcManager;
this.confGetter = confGetter;
this.certificateHelper = certificateHelper;
Expand Down Expand Up @@ -214,69 +198,6 @@ public UUID upgradeKubernetesOverrides(
universe);
}

public JsonNode constructGFlagAuditPayload(
GFlagsUpgradeParams requestParams, UserIntent oldUserIntent) {
if (requestParams.getPrimaryCluster() == null) {
return null;
}
// TODO: support specific gflags
UserIntent newUserIntent = requestParams.getPrimaryCluster().userIntent;
Map<String, String> newMasterGFlags = newUserIntent.masterGFlags;
Map<String, String> newTserverGFlags = newUserIntent.tserverGFlags;
Map<String, String> oldMasterGFlags = oldUserIntent.masterGFlags;
Map<String, String> oldTserverGFlags = oldUserIntent.tserverGFlags;
GFlagsAuditPayload payload = new GFlagsAuditPayload();
String softwareVersion = newUserIntent.ybSoftwareVersion;
payload.master =
generateGFlagEntries(
oldMasterGFlags, newMasterGFlags, ServerType.MASTER.toString(), softwareVersion);
payload.tserver =
generateGFlagEntries(
oldTserverGFlags, newTserverGFlags, ServerType.TSERVER.toString(), softwareVersion);

ObjectMapper mapper = new ObjectMapper();
Map<String, GFlagsAuditPayload> auditPayload = new HashMap<>();
auditPayload.put("gflags", payload);

return mapper.valueToTree(auditPayload);
}

public List<GFlagDiffEntry> generateGFlagEntries(
Map<String, String> oldGFlags,
Map<String, String> newGFlags,
String serverType,
String softwareVersion) {
List<GFlagDiffEntry> gFlagChanges = new ArrayList<GFlagDiffEntry>();

GFlagDiffEntry tEntry;
Collection<String> modifiedGFlags = Sets.union(oldGFlags.keySet(), newGFlags.keySet());

for (String gFlagName : modifiedGFlags) {
String oldGFlagValue = oldGFlags.getOrDefault(gFlagName, null);
String newGFlagValue = newGFlags.getOrDefault(gFlagName, null);
if (oldGFlagValue == null || !oldGFlagValue.equals(newGFlagValue)) {
String defaultGFlagValue = getGFlagDefaultValue(softwareVersion, serverType, gFlagName);
tEntry = new GFlagDiffEntry(gFlagName, oldGFlagValue, newGFlagValue, defaultGFlagValue);
gFlagChanges.add(tEntry);
}
}

return gFlagChanges;
}

public String getGFlagDefaultValue(String softwareVersion, String serverType, String gFlagName) {
GFlagDetails defaultGFlag;
String defaultGFlagValue;
try {
defaultGFlag =
gFlagsValidationHandler.getGFlagsMetadata(softwareVersion, serverType, gFlagName);
} catch (IOException | PlatformServiceException e) {
defaultGFlag = null;
}
defaultGFlagValue = (defaultGFlag == null) ? null : defaultGFlag.defaultValue;
return defaultGFlagValue;
}

public UUID rotateCerts(CertsRotateParams requestParams, Customer customer, Universe universe) {
log.debug(
"rotateCerts called with rootCA: {}",
Expand Down
Expand Up @@ -594,7 +594,9 @@ public UserIntent clone() {
newUserIntent.provider = provider;
newUserIntent.providerType = providerType;
newUserIntent.replicationFactor = replicationFactor;
newUserIntent.regionList = new ArrayList<>(regionList);
if (regionList != null) {
newUserIntent.regionList = new ArrayList<>(regionList);
}
newUserIntent.preferredRegion = preferredRegion;
newUserIntent.instanceType = instanceType;
newUserIntent.numNodes = numNodes;
Expand Down

0 comments on commit a71c801

Please sign in to comment.