1
0
Fork 0
mirror of https://github.com/arangodb/kube-arangodb.git synced 2024-12-14 11:57:37 +00:00

[Feature] Extract pod details (#1080)

This commit is contained in:
Adam Janikowski 2022-08-08 12:03:06 +02:00 committed by GitHub
parent 46260630dc
commit d95a1b1f1a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 481 additions and 636 deletions

View file

@ -14,6 +14,7 @@
- (Feature) (DBServer Maintenance) Agency adjustments - (Feature) (DBServer Maintenance) Agency adjustments
- (Logging) Internal client trace - (Logging) Internal client trace
- (QA) Member maintenance feature - (QA) Member maintenance feature
- (Feature) Extract Pod Details
## [1.2.15](https://github.com/arangodb/kube-arangodb/tree/1.2.15) (2022-07-20) ## [1.2.15](https://github.com/arangodb/kube-arangodb/tree/1.2.15) (2022-07-20)
- (Bugfix) Ensure pod names not too long - (Bugfix) Ensure pod names not too long

View file

@ -89,6 +89,7 @@ func init() {
cmdLifecycle.AddCommand(cmdLifecycleCopy) cmdLifecycle.AddCommand(cmdLifecycleCopy)
cmdLifecycle.AddCommand(cmdLifecycleProbe) cmdLifecycle.AddCommand(cmdLifecycleProbe)
cmdLifecycle.AddCommand(cmdLifecycleWait) cmdLifecycle.AddCommand(cmdLifecycleWait)
cmdLifecycle.AddCommand(cmdLifecycleStartup)
cmdLifecycleCopy.Flags().StringVar(&lifecycleCopyOptions.TargetDir, "target", "", "Target directory to copy the executable to") cmdLifecycleCopy.Flags().StringVar(&lifecycleCopyOptions.TargetDir, "target", "", "Target directory to copy the executable to")
} }

75
cmd/lifecycle_startup.go Normal file
View file

@ -0,0 +1,75 @@
//
// DISCLAIMER
//
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package cmd
import (
"errors"
"fmt"
"net/http"
"time"
"github.com/spf13/cobra"
"github.com/arangodb/kube-arangodb/pkg/apis/shared"
)
var cmdLifecycleStartup = &cobra.Command{
Use: "startup",
RunE: cmdLifecycleStartupFunc,
Hidden: true,
}
func cmdLifecycleStartupFunc(cmd *cobra.Command, args []string) error {
var close bool
server := &http.Server{
Addr: fmt.Sprintf(":%d", shared.ArangoPort),
}
handlers := http.NewServeMux()
handlers.HandleFunc("/stop", func(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusOK)
close = true
})
server.Handler = handlers
go func() {
for {
if close {
break
}
time.Sleep(time.Millisecond)
}
server.Close()
}()
if err := server.ListenAndServe(); err != nil {
if errors.Is(err, http.ErrServerClosed) {
return nil
}
return err
}
return nil
}

View file

@ -1,486 +0,0 @@
//
// DISCLAIMER
//
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package cmd
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"path"
"strings"
"sync"
"github.com/pkg/errors"
"github.com/spf13/cobra"
core "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
deplv1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
acli "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/constants"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
)
var (
cmdReboot = &cobra.Command{
Use: "reboot",
Run: cmdRebootRun,
Hidden: false,
}
rebootOptions struct {
DeploymentName string
ImageName string
LicenseSecretName string
Coordinators int
}
cmdRebootInspect = &cobra.Command{
Use: "inspect",
Run: cmdRebootInspectRun,
Hidden: true,
}
rebootInspectOptions struct {
TargetDir string
}
)
func init() {
cmdMain.AddCommand(cmdReboot)
cmdReboot.AddCommand(cmdRebootInspect)
cmdReboot.Flags().StringVar(&rebootOptions.DeploymentName, "deployment-name", "rebooted-deployment", "Name of the deployment")
cmdReboot.Flags().StringVar(&rebootOptions.ImageName, "image-name", "arangodb/arangodb:latest", "Image used for the deployment")
cmdReboot.Flags().StringVar(&rebootOptions.LicenseSecretName, "license-secret-name", "", "Name of secret for license key")
cmdReboot.Flags().IntVar(&rebootOptions.Coordinators, "coordinators", 1, "Initial number of coordinators")
cmdRebootInspect.Flags().StringVar(&rebootInspectOptions.TargetDir, "target-dir", "/data", "Path to mounted database directory")
}
type inspectResult struct {
UUID string `json:"uuid,omitempty"`
}
type inspectResponse struct {
Error *string `json:"error,omitempty"`
Result *inspectResult `json:"result,omitempty"`
}
type VolumeInspectResult struct {
UUID string
Claim string
Error error
}
func runVolumeInspector(ctx context.Context, kube kubernetes.Interface, ns, name, image, storageClassName string) (string, string, error) {
deletePVC := true
claimname := "arangodb-reboot-pvc-" + name
pvcspec := core.PersistentVolumeClaim{
ObjectMeta: meta.ObjectMeta{
Name: claimname,
Labels: map[string]string{
"app": "arangodb",
"rebooted": "yes",
},
},
Spec: core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
VolumeName: name,
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceStorage: *resource.NewQuantity(1024*1024*1024, resource.DecimalSI),
},
},
StorageClassName: util.NewString(storageClassName),
},
}
_, err := kube.CoreV1().PersistentVolumeClaims(ns).Create(context.Background(), &pvcspec, meta.CreateOptions{})
if err != nil {
return "", "", errors.Wrap(err, "failed to create pvc")
}
defer func() {
if deletePVC {
logger.Str("pvc-name", claimname).Debug("deleting pvc")
kube.CoreV1().PersistentVolumeClaims(ns).Delete(context.Background(), claimname, meta.DeleteOptions{})
}
}()
podname := "arangodb-reboot-pod-" + name
podspec := core.Pod{
ObjectMeta: meta.ObjectMeta{
Name: podname,
},
Spec: core.PodSpec{
RestartPolicy: core.RestartPolicyNever,
Containers: []core.Container{
core.Container{
Name: "inspector",
Image: image,
ImagePullPolicy: core.PullAlways,
Command: []string{"arangodb_operator"},
Args: []string{"reboot", "inspect"},
Env: []core.EnvVar{
core.EnvVar{
Name: constants.EnvOperatorPodNamespace,
Value: ns,
},
},
VolumeMounts: []core.VolumeMount{
core.VolumeMount{
MountPath: "/data",
Name: "data",
},
},
Ports: []core.ContainerPort{
core.ContainerPort{
ContainerPort: 8080,
},
},
ReadinessProbe: &core.Probe{
Handler: core.Handler{
HTTPGet: &core.HTTPGetAction{
Path: "/info",
Port: intstr.FromInt(8080),
},
},
},
},
},
Volumes: []core.Volume{
k8sutil.CreateVolumeWithPersitantVolumeClaim("data", claimname),
},
},
}
_, err = kube.CoreV1().Pods(ns).Create(context.Background(), &podspec, meta.CreateOptions{})
if err != nil {
return "", "", errors.Wrap(err, "failed to create pod")
}
defer kube.CoreV1().Pods(ns).Delete(context.Background(), podname, meta.DeleteOptions{})
podwatch, err := kube.CoreV1().Pods(ns).Watch(context.Background(), meta.ListOptions{FieldSelector: fields.OneTermEqualSelector("metadata.name", podname).String()})
if err != nil {
return "", "", errors.Wrap(err, "failed to watch for pod")
}
defer podwatch.Stop()
// wait until pod is terminated
for {
select {
case <-ctx.Done():
return "", "", ctx.Err()
case ev, ok := <-podwatch.ResultChan():
if !ok {
return "", "", fmt.Errorf("result channel bad")
}
// get the pod
pod, ok := ev.Object.(*core.Pod)
if !ok {
return "", "", fmt.Errorf("failed to get pod")
}
switch pod.Status.Phase {
case core.PodFailed:
return "", "", fmt.Errorf("pod failed: %s", pod.Status.Reason)
case core.PodRunning:
podReady := false
for _, c := range pod.Status.Conditions {
if c.Type == core.PodReady && c.Status == core.ConditionTrue {
podReady = true
}
}
if !podReady {
continue
}
resp, err := http.Get("http://" + pod.Status.PodIP + ":8080/info")
if err != nil {
return "", "", errors.Wrap(err, "Failed to get info")
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", "", errors.Wrap(err, "failed to read body")
}
var info inspectResponse
if err := json.Unmarshal(body, &info); err != nil {
return "", "", errors.Wrap(err, "failed to unmarshal response")
}
if info.Error != nil {
return "", "", fmt.Errorf("pod returned error: %s", *info.Error)
}
deletePVC = false
return info.Result.UUID, claimname, nil
}
}
}
}
func doVolumeInspection(ctx context.Context, kube kubernetes.Interface, ns, name, storageClassName string, resultChan chan<- VolumeInspectResult, image string) {
// Create Volume Claim
// Create Pod mounting this volume
// Wait for pod to be completed
// Read logs - parse json
// Delete pod
uuid, claim, err := runVolumeInspector(ctx, kube, ns, name, image, storageClassName)
if err != nil {
resultChan <- VolumeInspectResult{Error: err}
}
resultChan <- VolumeInspectResult{UUID: uuid, Claim: claim}
}
func checkVolumeAvailable(kube kubernetes.Interface, vname string) (VolumeInfo, error) {
volume, err := kube.CoreV1().PersistentVolumes().Get(context.Background(), vname, meta.GetOptions{})
if err != nil {
return VolumeInfo{}, errors.Wrapf(err, "failed to GET volume %s", vname)
}
switch volume.Status.Phase {
case core.VolumeAvailable:
break
case core.VolumeReleased:
// we have to remove the claim reference
volume.Spec.ClaimRef = nil
if _, err := kube.CoreV1().PersistentVolumes().Update(context.Background(), volume, meta.UpdateOptions{}); err != nil {
return VolumeInfo{}, errors.Wrapf(err, "failed to remove claim reference")
}
default:
return VolumeInfo{}, fmt.Errorf("Volume %s phase is %s, expected %s", vname, volume.Status.Phase, core.VolumeAvailable)
}
return VolumeInfo{StorageClassName: volume.Spec.StorageClassName}, nil
}
type VolumeInfo struct {
StorageClassName string
}
type VolumeListInfo map[string]VolumeInfo
func preflightChecks(kube kubernetes.Interface, volumes []string) (VolumeListInfo, error) {
info := make(VolumeListInfo)
// Check if all values are released
for _, vname := range volumes {
vi, err := checkVolumeAvailable(kube, vname)
if err != nil {
return nil, errors.Wrap(err, "preflight checks failed")
}
info[vname] = vi
}
return info, nil
}
func getMyImage(kube kubernetes.Interface, ns, name string) (string, error) {
pod, err := kube.CoreV1().Pods(ns).Get(context.Background(), name, meta.GetOptions{})
if err != nil {
return "", err
}
return pod.Spec.Containers[0].Image, nil
}
func createArangoDeployment(cli acli.Interface, ns, deplname, arangoimage string, results map[string]VolumeInspectResult) error {
prmr := make(map[string]VolumeInspectResult)
agnt := make(map[string]VolumeInspectResult)
for vname, info := range results {
if strings.HasPrefix(info.UUID, "PRMR") {
prmr[vname] = info
} else if strings.HasPrefix(info.UUID, "AGNT") {
agnt[vname] = info
} else {
return fmt.Errorf("unknown server type by uuid: %s", info.UUID)
}
}
depl := deplv1.ArangoDeployment{
ObjectMeta: meta.ObjectMeta{
Name: deplname,
},
Spec: deplv1.DeploymentSpec{
Image: util.NewString(arangoimage),
Coordinators: deplv1.ServerGroupSpec{
Count: util.NewInt(rebootOptions.Coordinators),
},
Agents: deplv1.ServerGroupSpec{
Count: util.NewInt(len(agnt)),
},
DBServers: deplv1.ServerGroupSpec{
Count: util.NewInt(len(prmr)),
},
},
}
if rebootOptions.LicenseSecretName != "" {
depl.Spec.License.SecretName = util.NewString(rebootOptions.LicenseSecretName)
}
for _, info := range agnt {
depl.Status.Members.Agents = append(depl.Status.Members.Agents, deplv1.MemberStatus{
ID: info.UUID,
PersistentVolumeClaimName: info.Claim,
PodName: k8sutil.CreatePodName(deplname, deplv1.ServerGroupAgents.AsRole(), info.UUID, "-rbt"),
})
}
for _, info := range prmr {
depl.Status.Members.DBServers = append(depl.Status.Members.DBServers, deplv1.MemberStatus{
ID: info.UUID,
PersistentVolumeClaimName: info.Claim,
PodName: k8sutil.CreatePodName(deplname, deplv1.ServerGroupDBServers.AsRole(), info.UUID, "-rbt"),
})
}
if _, err := cli.DatabaseV1().ArangoDeployments(ns).Create(context.Background(), &depl, meta.CreateOptions{}); err != nil {
return errors.Wrap(err, "failed to create ArangoDeployment")
}
return nil
}
func cmdRebootRun(cmd *cobra.Command, args []string) {
volumes := args
namespace := os.Getenv(constants.EnvOperatorPodNamespace)
podname := os.Getenv(constants.EnvOperatorPodName)
// Create kubernetes client
client, ok := kclient.GetDefaultFactory().Client()
if !ok {
logger.Fatal("Failed to get client")
}
kubecli := client.Kubernetes()
extcli := client.Arango()
image, err := getMyImage(kubecli, namespace, podname)
if err != nil {
logger.Err(err).Fatal("failed to get my image")
}
vinfo, err := preflightChecks(kubecli, volumes)
if err != nil {
logger.Err(err).Fatal("preflight checks failed")
}
var wg sync.WaitGroup
ctx := context.Background()
resultChan := make(chan VolumeInspectResult)
received := 0
for _, volumeName := range volumes {
logger.Str("volume", volumeName).Debug("Starting inspection")
wg.Add(1)
go func(vn string) {
defer wg.Done()
doVolumeInspection(ctx, kubecli, namespace, vn, vinfo[vn].StorageClassName, resultChan, image)
}(volumeName)
}
members := make(map[string]VolumeInspectResult)
for {
if received == len(volumes) {
break
}
select {
case res := <-resultChan:
if res.Error != nil {
logger.Err(res.Error).Error("Inspection failed")
} else {
logger.Str("claim", res.Claim).Str("uuid", res.UUID).Info("Inspection completed")
}
members[res.UUID] = res
received++
case <-ctx.Done():
panic(ctx.Err())
}
}
logger.Debug("results complete - generating ArangoDeployment resource")
if err := createArangoDeployment(extcli, namespace, rebootOptions.DeploymentName, rebootOptions.ImageName, members); err != nil {
logger.Err(err).Error("failed to create deployment")
}
logger.Info("ArangoDeployment created.")
// Wait for everyone to be completed
wg.Wait()
}
// inspectDatabaseDirectory inspects the given directory and returns the inspection result or an error
func inspectDatabaseDirectory(dirname string) (*inspectResult, error) {
// Access the database directory and look for the following files
// UUID
uuidfile := path.Join(dirname, "UUID")
uuid, err := ioutil.ReadFile(path.Clean(uuidfile))
if err != nil {
return nil, err
}
return &inspectResult{UUID: strings.TrimSpace(string(uuid))}, nil
}
func cmdRebootInspectRun(cmd *cobra.Command, args []string) {
var response inspectResponse
result, err := inspectDatabaseDirectory(rebootInspectOptions.TargetDir)
if err != nil {
response.Error = util.NewString(err.Error())
}
response.Result = result
json, err := json.Marshal(&response)
if err != nil {
panic(err)
}
http.HandleFunc("/info", func(w http.ResponseWriter, req *http.Request) {
w.Write(json)
})
if http.ListenAndServe(":8080", nil); err != nil {
logger.Err(err).Fatal("Failed to listen and serve")
}
}

View file

@ -298,8 +298,8 @@ func (ds DeploymentStatusMembers) PodNames() []string {
var n []string var n []string
for _, m := range ds.AsList() { for _, m := range ds.AsList() {
if m.Member.PodName != "" { if name := m.Member.Pod.GetName(); name != "" {
n = append(n, m.Member.PodName) n = append(n, name)
} }
} }

View file

@ -0,0 +1,73 @@
//
// DISCLAIMER
//
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package v1
import "k8s.io/apimachinery/pkg/types"
type MemberPodStatus struct {
Name string `json:"name"`
UID types.UID `json:"uid"`
SpecVersion string `json:"specVersion,omitempty"`
}
func (m *MemberPodStatus) Equal(other *MemberPodStatus) bool {
if m == nil && other == nil {
return true
}
if m == nil || other == nil {
return false
}
return m.Name == other.Name &&
m.UID == other.UID &&
m.SpecVersion == other.SpecVersion
}
func (m *MemberPodStatus) GetName() string {
if m == nil {
return ""
}
return m.Name
}
func (m *MemberPodStatus) GetUID() types.UID {
if m == nil {
return ""
}
return m.UID
}
func (m *MemberPodStatus) Propagate(s *MemberStatus) {
if s == nil {
return
}
if m == nil {
s.PodName = ""
s.PodUID = ""
s.PodSpecVersion = ""
} else {
s.PodName = m.Name
s.PodUID = m.UID
s.PodSpecVersion = m.SpecVersion
}
}

View file

@ -52,12 +52,6 @@ type MemberStatus struct {
CreatedAt meta.Time `json:"created-at"` CreatedAt meta.Time `json:"created-at"`
// PersistentVolumeClaimName holds the name of the persistent volume claim used for this member (if any). // PersistentVolumeClaimName holds the name of the persistent volume claim used for this member (if any).
PersistentVolumeClaimName string `json:"persistentVolumeClaimName,omitempty"` PersistentVolumeClaimName string `json:"persistentVolumeClaimName,omitempty"`
// PodName holds the name of the Pod that currently runs this member
PodName string `json:"podName,omitempty"`
// PodUID holds the UID of the Pod that currently runs this member
PodUID types.UID `json:"podUID,omitempty"`
// PodSpecVersion holds the checksum of Pod spec that currently runs this member. Used to rotate pods
PodSpecVersion string `json:"podSpecVersion,omitempty"`
// Conditions specific to this member // Conditions specific to this member
Conditions ConditionList `json:"conditions,omitempty"` Conditions ConditionList `json:"conditions,omitempty"`
// RecentTerminatons holds the times when this member was recently terminated. // RecentTerminatons holds the times when this member was recently terminated.
@ -84,10 +78,20 @@ type MemberStatus struct {
Endpoint *string `json:"endpoint,omitempty"` Endpoint *string `json:"endpoint,omitempty"`
// Topology define topology member status assignment // Topology define topology member status assignment
Topology *TopologyMemberStatus `json:"topology,omitempty"` Topology *TopologyMemberStatus `json:"topology,omitempty"`
Pod *MemberPodStatus `json:"pod,omitempty"`
// deprecated // deprecated
// SideCarSpecs contains list of specifications specified for side cars // SideCarSpecs contains list of specifications specified for side cars
SideCarSpecs map[string]core.Container `json:"sidecars-specs,omitempty"` SideCarSpecs map[string]core.Container `json:"sidecars-specs,omitempty"`
// deprecated
// PodName holds the name of the Pod that currently runs this member
PodName string `json:"podName,omitempty"`
// deprecated
// PodUID holds the UID of the Pod that currently runs this member
PodUID types.UID `json:"podUID,omitempty"`
// deprecated
// PodSpecVersion holds the checksum of Pod spec that currently runs this member. Used to rotate pods
PodSpecVersion string `json:"podSpecVersion,omitempty"`
} }
// Equal checks for equality // Equal checks for equality
@ -99,7 +103,7 @@ func (s MemberStatus) Equal(other MemberStatus) bool {
s.Phase == other.Phase && s.Phase == other.Phase &&
util.TimeCompareEqual(s.CreatedAt, other.CreatedAt) && util.TimeCompareEqual(s.CreatedAt, other.CreatedAt) &&
s.PersistentVolumeClaimName == other.PersistentVolumeClaimName && s.PersistentVolumeClaimName == other.PersistentVolumeClaimName &&
s.PodName == other.PodName && s.Pod.Equal(other.Pod) &&
s.Conditions.Equal(other.Conditions) && s.Conditions.Equal(other.Conditions) &&
s.IsInitialized == other.IsInitialized && s.IsInitialized == other.IsInitialized &&
s.CleanoutJobID == other.CleanoutJobID && s.CleanoutJobID == other.CleanoutJobID &&

View file

@ -78,7 +78,7 @@ func (l MemberStatusList) ElementByID(id string) (MemberStatus, bool) {
// If no such element exists, an empty element and false is returned. // If no such element exists, an empty element and false is returned.
func (l MemberStatusList) ElementByPodName(podName string) (MemberStatus, bool) { func (l MemberStatusList) ElementByPodName(podName string) (MemberStatus, bool) {
for i, x := range l { for i, x := range l {
if x.PodName == podName { if x.Pod.GetName() == podName {
return l[i], true return l[i], true
} }
} }

View file

@ -50,13 +50,13 @@ func TestMemberStatusList(t *testing.T) {
assert.Equal(t, m1.ID, (*list)[0].ID) assert.Equal(t, m1.ID, (*list)[0].ID)
assert.Equal(t, m2.ID, (*list)[1].ID) assert.Equal(t, m2.ID, (*list)[1].ID)
m2.PodName = "foo" m2.Pod = &MemberPodStatus{Name: "foo"}
assert.NoError(t, list.update(m2)) assert.NoError(t, list.update(m2))
assert.Equal(t, 2, len(*list)) assert.Equal(t, 2, len(*list))
assert.True(t, list.ContainsID(m2.ID)) assert.True(t, list.ContainsID(m2.ID))
x, found := list.ElementByPodName("foo") x, found := list.ElementByPodName("foo")
assert.True(t, found) assert.True(t, found)
assert.Equal(t, "foo", x.PodName) assert.Equal(t, "foo", x.Pod.GetName())
assert.Equal(t, m2.ID, x.ID) assert.Equal(t, m2.ID, x.ID)
assert.NoError(t, list.add(m3)) assert.NoError(t, list.add(m3))

View file

@ -32,13 +32,14 @@ const (
ServerGroupReservedInitContainerNameLifecycle = "init-lifecycle" ServerGroupReservedInitContainerNameLifecycle = "init-lifecycle"
ServerGroupReservedInitContainerNameUUID = "uuid" ServerGroupReservedInitContainerNameUUID = "uuid"
ServerGroupReservedInitContainerNameWait = "wait" ServerGroupReservedInitContainerNameWait = "wait"
ServerGroupReservedInitContainerNameStartup = "arango-init-startup"
ServerGroupReservedInitContainerNameUpgrade = "upgrade" ServerGroupReservedInitContainerNameUpgrade = "upgrade"
ServerGroupReservedInitContainerNameVersionCheck = "version-check" ServerGroupReservedInitContainerNameVersionCheck = "version-check"
) )
func IsReservedServerGroupInitContainerName(name string) bool { func IsReservedServerGroupInitContainerName(name string) bool {
switch name { switch name {
case ServerGroupReservedInitContainerNameLifecycle, ServerGroupReservedInitContainerNameUUID, ServerGroupReservedInitContainerNameUpgrade, ServerGroupReservedInitContainerNameVersionCheck: case ServerGroupReservedInitContainerNameLifecycle, ServerGroupReservedInitContainerNameUUID, ServerGroupReservedInitContainerNameUpgrade, ServerGroupReservedInitContainerNameVersionCheck, ServerGroupReservedInitContainerNameStartup:
return true return true
default: default:
return false return false

View file

@ -1659,6 +1659,22 @@ func (in List) DeepCopy() List {
return *out return *out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MemberPodStatus) DeepCopyInto(out *MemberPodStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemberPodStatus.
func (in *MemberPodStatus) DeepCopy() *MemberPodStatus {
if in == nil {
return nil
}
out := new(MemberPodStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MemberStatus) DeepCopyInto(out *MemberStatus) { func (in *MemberStatus) DeepCopyInto(out *MemberStatus) {
*out = *in *out = *in
@ -1702,6 +1718,11 @@ func (in *MemberStatus) DeepCopyInto(out *MemberStatus) {
*out = new(TopologyMemberStatus) *out = new(TopologyMemberStatus)
**out = **in **out = **in
} }
if in.Pod != nil {
in, out := &in.Pod, &out.Pod
*out = new(MemberPodStatus)
**out = **in
}
if in.SideCarSpecs != nil { if in.SideCarSpecs != nil {
in, out := &in.SideCarSpecs, &out.SideCarSpecs in, out := &in.SideCarSpecs, &out.SideCarSpecs
*out = make(map[string]corev1.Container, len(*in)) *out = make(map[string]corev1.Container, len(*in))

View file

@ -298,8 +298,8 @@ func (ds DeploymentStatusMembers) PodNames() []string {
var n []string var n []string
for _, m := range ds.AsList() { for _, m := range ds.AsList() {
if m.Member.PodName != "" { if name := m.Member.Pod.GetName(); name != "" {
n = append(n, m.Member.PodName) n = append(n, name)
} }
} }

View file

@ -0,0 +1,73 @@
//
// DISCLAIMER
//
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package v2alpha1
import "k8s.io/apimachinery/pkg/types"
type MemberPodStatus struct {
Name string `json:"name"`
UID types.UID `json:"uid"`
SpecVersion string `json:"specVersion,omitempty"`
}
func (m *MemberPodStatus) Equal(other *MemberPodStatus) bool {
if m == nil && other == nil {
return true
}
if m == nil || other == nil {
return false
}
return m.Name == other.Name &&
m.UID == other.UID &&
m.SpecVersion == other.SpecVersion
}
func (m *MemberPodStatus) GetName() string {
if m == nil {
return ""
}
return m.Name
}
func (m *MemberPodStatus) GetUID() types.UID {
if m == nil {
return ""
}
return m.UID
}
func (m *MemberPodStatus) Propagate(s *MemberStatus) {
if s == nil {
return
}
if m == nil {
s.PodName = ""
s.PodUID = ""
s.PodSpecVersion = ""
} else {
s.PodName = m.Name
s.PodUID = m.UID
s.PodSpecVersion = m.SpecVersion
}
}

View file

@ -52,12 +52,6 @@ type MemberStatus struct {
CreatedAt meta.Time `json:"created-at"` CreatedAt meta.Time `json:"created-at"`
// PersistentVolumeClaimName holds the name of the persistent volume claim used for this member (if any). // PersistentVolumeClaimName holds the name of the persistent volume claim used for this member (if any).
PersistentVolumeClaimName string `json:"persistentVolumeClaimName,omitempty"` PersistentVolumeClaimName string `json:"persistentVolumeClaimName,omitempty"`
// PodName holds the name of the Pod that currently runs this member
PodName string `json:"podName,omitempty"`
// PodUID holds the UID of the Pod that currently runs this member
PodUID types.UID `json:"podUID,omitempty"`
// PodSpecVersion holds the checksum of Pod spec that currently runs this member. Used to rotate pods
PodSpecVersion string `json:"podSpecVersion,omitempty"`
// Conditions specific to this member // Conditions specific to this member
Conditions ConditionList `json:"conditions,omitempty"` Conditions ConditionList `json:"conditions,omitempty"`
// RecentTerminatons holds the times when this member was recently terminated. // RecentTerminatons holds the times when this member was recently terminated.
@ -84,10 +78,20 @@ type MemberStatus struct {
Endpoint *string `json:"endpoint,omitempty"` Endpoint *string `json:"endpoint,omitempty"`
// Topology define topology member status assignment // Topology define topology member status assignment
Topology *TopologyMemberStatus `json:"topology,omitempty"` Topology *TopologyMemberStatus `json:"topology,omitempty"`
Pod *MemberPodStatus `json:"pod,omitempty"`
// deprecated // deprecated
// SideCarSpecs contains list of specifications specified for side cars // SideCarSpecs contains list of specifications specified for side cars
SideCarSpecs map[string]core.Container `json:"sidecars-specs,omitempty"` SideCarSpecs map[string]core.Container `json:"sidecars-specs,omitempty"`
// deprecated
// PodName holds the name of the Pod that currently runs this member
PodName string `json:"podName,omitempty"`
// deprecated
// PodUID holds the UID of the Pod that currently runs this member
PodUID types.UID `json:"podUID,omitempty"`
// deprecated
// PodSpecVersion holds the checksum of Pod spec that currently runs this member. Used to rotate pods
PodSpecVersion string `json:"podSpecVersion,omitempty"`
} }
// Equal checks for equality // Equal checks for equality
@ -99,7 +103,7 @@ func (s MemberStatus) Equal(other MemberStatus) bool {
s.Phase == other.Phase && s.Phase == other.Phase &&
util.TimeCompareEqual(s.CreatedAt, other.CreatedAt) && util.TimeCompareEqual(s.CreatedAt, other.CreatedAt) &&
s.PersistentVolumeClaimName == other.PersistentVolumeClaimName && s.PersistentVolumeClaimName == other.PersistentVolumeClaimName &&
s.PodName == other.PodName && s.Pod.Equal(other.Pod) &&
s.Conditions.Equal(other.Conditions) && s.Conditions.Equal(other.Conditions) &&
s.IsInitialized == other.IsInitialized && s.IsInitialized == other.IsInitialized &&
s.CleanoutJobID == other.CleanoutJobID && s.CleanoutJobID == other.CleanoutJobID &&

View file

@ -78,7 +78,7 @@ func (l MemberStatusList) ElementByID(id string) (MemberStatus, bool) {
// If no such element exists, an empty element and false is returned. // If no such element exists, an empty element and false is returned.
func (l MemberStatusList) ElementByPodName(podName string) (MemberStatus, bool) { func (l MemberStatusList) ElementByPodName(podName string) (MemberStatus, bool) {
for i, x := range l { for i, x := range l {
if x.PodName == podName { if x.Pod.GetName() == podName {
return l[i], true return l[i], true
} }
} }

View file

@ -50,13 +50,13 @@ func TestMemberStatusList(t *testing.T) {
assert.Equal(t, m1.ID, (*list)[0].ID) assert.Equal(t, m1.ID, (*list)[0].ID)
assert.Equal(t, m2.ID, (*list)[1].ID) assert.Equal(t, m2.ID, (*list)[1].ID)
m2.PodName = "foo" m2.Pod = &MemberPodStatus{Name: "foo"}
assert.NoError(t, list.update(m2)) assert.NoError(t, list.update(m2))
assert.Equal(t, 2, len(*list)) assert.Equal(t, 2, len(*list))
assert.True(t, list.ContainsID(m2.ID)) assert.True(t, list.ContainsID(m2.ID))
x, found := list.ElementByPodName("foo") x, found := list.ElementByPodName("foo")
assert.True(t, found) assert.True(t, found)
assert.Equal(t, "foo", x.PodName) assert.Equal(t, "foo", x.Pod.GetName())
assert.Equal(t, m2.ID, x.ID) assert.Equal(t, m2.ID, x.ID)
assert.NoError(t, list.add(m3)) assert.NoError(t, list.add(m3))

View file

@ -32,13 +32,14 @@ const (
ServerGroupReservedInitContainerNameLifecycle = "init-lifecycle" ServerGroupReservedInitContainerNameLifecycle = "init-lifecycle"
ServerGroupReservedInitContainerNameUUID = "uuid" ServerGroupReservedInitContainerNameUUID = "uuid"
ServerGroupReservedInitContainerNameWait = "wait" ServerGroupReservedInitContainerNameWait = "wait"
ServerGroupReservedInitContainerNameStartup = "arango-init-startup"
ServerGroupReservedInitContainerNameUpgrade = "upgrade" ServerGroupReservedInitContainerNameUpgrade = "upgrade"
ServerGroupReservedInitContainerNameVersionCheck = "version-check" ServerGroupReservedInitContainerNameVersionCheck = "version-check"
) )
func IsReservedServerGroupInitContainerName(name string) bool { func IsReservedServerGroupInitContainerName(name string) bool {
switch name { switch name {
case ServerGroupReservedInitContainerNameLifecycle, ServerGroupReservedInitContainerNameUUID, ServerGroupReservedInitContainerNameUpgrade, ServerGroupReservedInitContainerNameVersionCheck: case ServerGroupReservedInitContainerNameLifecycle, ServerGroupReservedInitContainerNameUUID, ServerGroupReservedInitContainerNameUpgrade, ServerGroupReservedInitContainerNameVersionCheck, ServerGroupReservedInitContainerNameStartup:
return true return true
default: default:
return false return false

View file

@ -1659,6 +1659,22 @@ func (in List) DeepCopy() List {
return *out return *out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MemberPodStatus) DeepCopyInto(out *MemberPodStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemberPodStatus.
func (in *MemberPodStatus) DeepCopy() *MemberPodStatus {
if in == nil {
return nil
}
out := new(MemberPodStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MemberStatus) DeepCopyInto(out *MemberStatus) { func (in *MemberStatus) DeepCopyInto(out *MemberStatus) {
*out = *in *out = *in
@ -1702,6 +1718,11 @@ func (in *MemberStatus) DeepCopyInto(out *MemberStatus) {
*out = new(TopologyMemberStatus) *out = new(TopologyMemberStatus)
**out = **in **out = **in
} }
if in.Pod != nil {
in, out := &in.Pod, &out.Pod
*out = new(MemberPodStatus)
**out = **in
}
if in.SideCarSpecs != nil { if in.SideCarSpecs != nil {
in, out := &in.SideCarSpecs, &out.SideCarSpecs in, out := &in.SideCarSpecs, &out.SideCarSpecs
*out = make(map[string]v1.Container, len(*in)) *out = make(map[string]v1.Container, len(*in))

View file

@ -58,7 +58,7 @@ func actionWrap(a api.Action, member *api.MemberStatus, wrap ...actionWrapper) a
func actionWrapMemberUID(a api.Action, member *api.MemberStatus) api.Action { func actionWrapMemberUID(a api.Action, member *api.MemberStatus) api.Action {
switch a.Type { switch a.Type {
case api.ActionTypeShutdownMember, api.ActionTypeKillMemberPod, api.ActionTypeRotateStartMember, api.ActionTypeUpgradeMember: case api.ActionTypeShutdownMember, api.ActionTypeKillMemberPod, api.ActionTypeRotateStartMember, api.ActionTypeUpgradeMember:
if q := member.PodUID; q != "" { if q := member.Pod.GetUID(); q != "" {
return a.AddParam(api.ParamPodUID, string(q)) return a.AddParam(api.ParamPodUID, string(q))
} }
return a return a

View file

@ -52,7 +52,10 @@ var phase = phaseMap{
m.RID = uuid.NewUUID() m.RID = uuid.NewUUID()
// Clean Pod details // Clean Pod details
m.PodUID = "" if m.Pod != nil {
m.Pod.UID = ""
}
m.Pod.Propagate(m)
// Add ClusterID // Add ClusterID
if m.ClusterID == "" { if m.ClusterID == "" {

View file

@ -138,7 +138,6 @@ func (d *Deployment) renderMember(spec api.DeploymentSpec, status *api.Deploymen
CreatedAt: meta.Now(), CreatedAt: meta.Now(),
Phase: api.MemberPhaseNone, Phase: api.MemberPhaseNone,
PersistentVolumeClaimName: shared.CreatePersistentVolumeClaimName(deploymentName, role, id), PersistentVolumeClaimName: shared.CreatePersistentVolumeClaimName(deploymentName, role, id),
PodName: "",
Image: apiObject.Status.CurrentImage, Image: apiObject.Status.CurrentImage,
Architecture: &arch, Architecture: &arch,
}, nil }, nil
@ -150,7 +149,6 @@ func (d *Deployment) renderMember(spec api.DeploymentSpec, status *api.Deploymen
CreatedAt: meta.Now(), CreatedAt: meta.Now(),
Phase: api.MemberPhaseNone, Phase: api.MemberPhaseNone,
PersistentVolumeClaimName: shared.CreatePersistentVolumeClaimName(deploymentName, role, id), PersistentVolumeClaimName: shared.CreatePersistentVolumeClaimName(deploymentName, role, id),
PodName: "",
Image: apiObject.Status.CurrentImage, Image: apiObject.Status.CurrentImage,
Architecture: &arch, Architecture: &arch,
}, nil }, nil
@ -162,7 +160,6 @@ func (d *Deployment) renderMember(spec api.DeploymentSpec, status *api.Deploymen
CreatedAt: meta.Now(), CreatedAt: meta.Now(),
Phase: api.MemberPhaseNone, Phase: api.MemberPhaseNone,
PersistentVolumeClaimName: shared.CreatePersistentVolumeClaimName(deploymentName, role, id), PersistentVolumeClaimName: shared.CreatePersistentVolumeClaimName(deploymentName, role, id),
PodName: "",
Image: apiObject.Status.CurrentImage, Image: apiObject.Status.CurrentImage,
Architecture: &arch, Architecture: &arch,
}, nil }, nil
@ -174,7 +171,6 @@ func (d *Deployment) renderMember(spec api.DeploymentSpec, status *api.Deploymen
CreatedAt: meta.Now(), CreatedAt: meta.Now(),
Phase: api.MemberPhaseNone, Phase: api.MemberPhaseNone,
PersistentVolumeClaimName: "", PersistentVolumeClaimName: "",
PodName: "",
Image: apiObject.Status.CurrentImage, Image: apiObject.Status.CurrentImage,
Architecture: &arch, Architecture: &arch,
}, nil }, nil
@ -186,7 +182,6 @@ func (d *Deployment) renderMember(spec api.DeploymentSpec, status *api.Deploymen
CreatedAt: meta.Now(), CreatedAt: meta.Now(),
Phase: api.MemberPhaseNone, Phase: api.MemberPhaseNone,
PersistentVolumeClaimName: "", PersistentVolumeClaimName: "",
PodName: "",
Image: apiObject.Status.CurrentImage, Image: apiObject.Status.CurrentImage,
Architecture: &arch, Architecture: &arch,
}, nil }, nil
@ -198,7 +193,6 @@ func (d *Deployment) renderMember(spec api.DeploymentSpec, status *api.Deploymen
CreatedAt: meta.Now(), CreatedAt: meta.Now(),
Phase: api.MemberPhaseNone, Phase: api.MemberPhaseNone,
PersistentVolumeClaimName: "", PersistentVolumeClaimName: "",
PodName: "",
Image: apiObject.Status.CurrentImage, Image: apiObject.Status.CurrentImage,
Architecture: &arch, Architecture: &arch,
}, nil }, nil

View file

@ -137,7 +137,7 @@ func (a *actionArangoMemberUpdatePodSpec) Start(ctx context.Context) (bool, erro
} }
if err := c.UpdateStatus(ctx, func(member *api.ArangoMember, status *api.ArangoMemberStatus) bool { if err := c.UpdateStatus(ctx, func(member *api.ArangoMember, status *api.ArangoMemberStatus) bool {
if (status.Template == nil || status.Template.PodSpec == nil) && (m.PodSpecVersion == "" || m.PodSpecVersion == template.PodSpecChecksum) { if (status.Template == nil || status.Template.PodSpec == nil) && (m.Pod == nil || m.Pod.SpecVersion == "" || m.Pod.SpecVersion == template.PodSpecChecksum) {
status.Template = template.DeepCopy() status.Template = template.DeepCopy()
} }

View file

@ -76,7 +76,7 @@ func (a *actionKillMemberPod) Start(ctx context.Context) (bool, error) {
return true, nil return true, nil
} }
if err := cache.Client().Kubernetes().CoreV1().Pods(cache.Namespace()).Delete(ctx, m.PodName, meta.DeleteOptions{}); err != nil { if err := cache.Client().Kubernetes().CoreV1().Pods(cache.Namespace()).Delete(ctx, m.Pod.GetName(), meta.DeleteOptions{}); err != nil {
a.log.Err(err).Error("Unable to kill pod") a.log.Err(err).Error("Unable to kill pod")
return true, nil return true, nil
} }
@ -101,7 +101,7 @@ func (a *actionKillMemberPod) CheckProgress(ctx context.Context) (bool, bool, er
return false, false, errors.Newf("Client is not ready") return false, false, errors.Newf("Client is not ready")
} }
p, ok := cache.Pod().V1().GetSimple(m.PodName) p, ok := cache.Pod().V1().GetSimple(m.Pod.GetName())
if !ok { if !ok {
a.log.Error("No such member") a.log.Error("No such member")
return true, false, nil return true, false, nil

View file

@ -108,9 +108,9 @@ func (a *actionRemoveMember) Start(ctx context.Context) (bool, error) {
} }
} }
} }
if m.PodName != "" { if p := m.Pod.GetName(); p != "" {
// Remove the pod (if any) // Remove the pod (if any)
if err := cache.Client().Kubernetes().CoreV1().Pods(cache.Namespace()).Delete(ctx, m.PodName, meta.DeleteOptions{}); err != nil { if err := cache.Client().Kubernetes().CoreV1().Pods(cache.Namespace()).Delete(ctx, p, meta.DeleteOptions{}); err != nil {
if !apiErrors.IsNotFound(err) { if !apiErrors.IsNotFound(err) {
return false, errors.WithStack(err) return false, errors.WithStack(err)
} }

View file

@ -101,7 +101,7 @@ func (a *actionRotateMember) CheckProgress(ctx context.Context) (bool, bool, err
defer cancel() defer cancel()
// Pod is terminated, we can now remove it // Pod is terminated, we can now remove it
if err := cache.Client().Kubernetes().CoreV1().Pods(cache.Namespace()).Delete(ctxChild, m.PodName, meta.DeleteOptions{}); err != nil { if err := cache.Client().Kubernetes().CoreV1().Pods(cache.Namespace()).Delete(ctxChild, m.Pod.GetName(), meta.DeleteOptions{}); err != nil {
if !k8sutil.IsNotFound(err) { if !k8sutil.IsNotFound(err) {
a.log.Err(err).Error("Unable to delete pod") a.log.Err(err).Error("Unable to delete pod")
return false, false, nil return false, false, nil

View file

@ -97,7 +97,7 @@ func (a *actionRotateStartMember) CheckProgress(ctx context.Context) (bool, bool
} }
// Pod is terminated, we can now remove it // Pod is terminated, we can now remove it
if err := cache.Client().Kubernetes().CoreV1().Pods(cache.Namespace()).Delete(ctx, m.PodName, meta.DeleteOptions{}); err != nil { if err := cache.Client().Kubernetes().CoreV1().Pods(cache.Namespace()).Delete(ctx, m.Pod.GetName(), meta.DeleteOptions{}); err != nil {
if !k8sutil.IsNotFound(err) { if !k8sutil.IsNotFound(err) {
a.log.Err(err).Error("Unable to delete pod") a.log.Err(err).Error("Unable to delete pod")
return false, false, nil return false, false, nil

View file

@ -153,9 +153,9 @@ func (a actionRuntimeContainerArgsUpdate) Start(ctx context.Context) (bool, erro
return false, errors.Errorf("ArangoMember %s not found", memberName) return false, errors.Errorf("ArangoMember %s not found", memberName)
} }
pod, ok := cache.Pod().V1().GetSimple(m.PodName) pod, ok := cache.Pod().V1().GetSimple(m.Pod.GetName())
if !ok { if !ok {
a.log.Str("podName", m.PodName).Info("pod is not present") a.log.Str("podName", m.Pod.GetName()).Info("pod is not present")
return true, nil return true, nil
} }

View file

@ -153,7 +153,7 @@ func (a actionRuntimeContainerImageUpdate) Start(ctx context.Context) (bool, err
return false, err return false, err
} }
pod, ok := cache.Pod().V1().GetSimple(m.PodName) pod, ok := cache.Pod().V1().GetSimple(m.Pod.GetName())
if !ok { if !ok {
a.log.Info("pod is not present") a.log.Info("pod is not present")
return true, nil return true, nil
@ -221,7 +221,7 @@ func (a actionRuntimeContainerImageUpdate) CheckProgress(ctx context.Context) (b
return false, false, nil return false, false, nil
} }
pod, ok := cache.Pod().V1().GetSimple(m.PodName) pod, ok := cache.Pod().V1().GetSimple(m.Pod.GetName())
if !ok { if !ok {
a.log.Info("pod is not present") a.log.Info("pod is not present")
return true, false, nil return true, false, nil

View file

@ -144,16 +144,16 @@ func (r *Reconciler) isStorageClassChanged(_ context.Context, apiObject k8sutil.
// From here on it is known that the member requires replacement, so `true` must be returned. // From here on it is known that the member requires replacement, so `true` must be returned.
// If pod does not exist then it will try next time. // If pod does not exist then it will try next time.
if pod, ok := cache.Pod().V1().GetSimple(member.PodName); ok { if pod, ok := cache.Pod().V1().GetSimple(member.Pod.GetName()); ok {
if _, ok := pod.GetAnnotations()[deployment.ArangoDeploymentPodReplaceAnnotation]; !ok { if _, ok := pod.GetAnnotations()[deployment.ArangoDeploymentPodReplaceAnnotation]; !ok {
r.log. r.log.
Str("pod-name", member.PodName). Str("pod-name", member.Pod.GetName()).
Str("server-group", group.AsRole()). Str("server-group", group.AsRole()).
Warn("try changing a storage class name, but %s", getRequiredReplaceMessage(member.PodName)) Warn("try changing a storage class name, but %s", getRequiredReplaceMessage(member.Pod.GetName()))
// No return here. // No return here.
} }
} else { } else {
return false, "", fmt.Errorf("failed to get pod %s", member.PodName) return false, "", fmt.Errorf("failed to get pod %s", member.Pod.GetName())
} }
return true, "Storage class has changed", nil return true, "Storage class has changed", nil
@ -210,14 +210,14 @@ func (r *Reconciler) isVolumeSizeChanged(_ context.Context, _ k8sutil.APIObject,
// From here on it is known that the member requires replacement, so `true` must be returned. // From here on it is known that the member requires replacement, so `true` must be returned.
// If pod does not exist then it will try next time. // If pod does not exist then it will try next time.
if pod, ok := cache.Pod().V1().GetSimple(member.PodName); ok { if pod, ok := cache.Pod().V1().GetSimple(member.Pod.GetName()); ok {
if _, ok := pod.GetAnnotations()[deployment.ArangoDeploymentPodReplaceAnnotation]; !ok { if _, ok := pod.GetAnnotations()[deployment.ArangoDeploymentPodReplaceAnnotation]; !ok {
r.log.Str("pod-name", member.PodName). r.log.Str("pod-name", member.Pod.GetName()).
Warn("try shrinking volume size, but %s", getRequiredReplaceMessage(member.PodName)) Warn("try shrinking volume size, but %s", getRequiredReplaceMessage(member.Pod.GetName()))
// No return here. // No return here.
} }
} else { } else {
return false, "", fmt.Errorf("failed to get pod %s", member.PodName) return false, "", fmt.Errorf("failed to get pod %s", member.Pod.GetName())
} }
return true, "Volume is shrunk", nil return true, "Volume is shrunk", nil

View file

@ -49,14 +49,14 @@ const (
func getShutdownHelper(a actionImpl) (ActionCore, api.MemberStatus, bool) { func getShutdownHelper(a actionImpl) (ActionCore, api.MemberStatus, bool) {
m, ok := a.actionCtx.GetMemberStatusByID(a.action.MemberID) m, ok := a.actionCtx.GetMemberStatusByID(a.action.MemberID)
if !ok { if !ok {
a.log.Str("pod-name", m.PodName).Warn("member is already gone") a.log.Str("pod-name", m.Pod.GetName()).Warn("member is already gone")
return nil, api.MemberStatus{}, false return nil, api.MemberStatus{}, false
} }
cache, ok := a.actionCtx.ACS().ClusterCache(m.ClusterID) cache, ok := a.actionCtx.ACS().ClusterCache(m.ClusterID)
if !ok { if !ok {
a.log.Str("pod-name", m.PodName).Warn("Cluster is not ready") a.log.Str("pod-name", m.Pod.GetName()).Warn("Cluster is not ready")
return nil, api.MemberStatus{}, false return nil, api.MemberStatus{}, false
} }
@ -66,9 +66,9 @@ func getShutdownHelper(a actionImpl) (ActionCore, api.MemberStatus, bool) {
return NewActionSuccess(), m, true return NewActionSuccess(), m, true
} }
pod, ok := cache.Pod().V1().GetSimple(m.PodName) pod, ok := cache.Pod().V1().GetSimple(m.Pod.GetName())
if !ok { if !ok {
a.log.Str("pod-name", m.PodName).Warn("pod is already gone") a.log.Str("pod-name", m.Pod.GetName()).Warn("pod is already gone")
// Pod does not exist, so create success action to finish it immediately. // Pod does not exist, so create success action to finish it immediately.
return NewActionSuccess(), m, true return NewActionSuccess(), m, true
} }
@ -101,7 +101,7 @@ func (s shutdownHelperAPI) Start(ctx context.Context) (bool, error) {
s.log.Info("Using API to shutdown member") s.log.Info("Using API to shutdown member")
group := s.action.Group group := s.action.Group
podName := s.memberStatus.PodName podName := s.memberStatus.Pod.GetName()
if podName == "" { if podName == "" {
s.log.Warn("Pod is empty") s.log.Warn("Pod is empty")
return true, nil return true, nil
@ -190,7 +190,7 @@ type shutdownHelperDelete struct {
func (s shutdownHelperDelete) Start(ctx context.Context) (bool, error) { func (s shutdownHelperDelete) Start(ctx context.Context) (bool, error) {
s.log.Info("Using Pod Delete to shutdown member") s.log.Info("Using Pod Delete to shutdown member")
podName := s.memberStatus.PodName podName := s.memberStatus.Pod.GetName()
if podName == "" { if podName == "" {
s.log.Warn("Pod is empty") s.log.Warn("Pod is empty")
return true, nil return true, nil
@ -225,7 +225,7 @@ func (s shutdownHelperDelete) CheckProgress(ctx context.Context) (bool, bool, er
return false, false, nil return false, false, nil
} }
podName := s.memberStatus.PodName podName := s.memberStatus.Pod.GetName()
if podName != "" { if podName != "" {
if _, ok := cache.Pod().V1().GetSimple(podName); ok { if _, ok := cache.Pod().V1().GetSimple(podName); ok {
s.log.Warn("Pod still exists") s.log.Warn("Pod still exists")
@ -252,7 +252,7 @@ func (s shutdownNow) Start(ctx context.Context) (bool, error) {
// CheckProgress starts removing pod forcefully and checks if has it been removed. // CheckProgress starts removing pod forcefully and checks if has it been removed.
func (s shutdownNow) CheckProgress(ctx context.Context) (bool, bool, error) { func (s shutdownNow) CheckProgress(ctx context.Context) (bool, bool, error) {
podName := s.memberStatus.PodName podName := s.memberStatus.Pod.GetName()
cache, ok := s.actionCtx.ACS().ClusterCache(s.memberStatus.ClusterID) cache, ok := s.actionCtx.ACS().ClusterCache(s.memberStatus.ClusterID)
if !ok { if !ok {
@ -266,7 +266,7 @@ func (s shutdownNow) CheckProgress(ctx context.Context) (bool, bool, error) {
return true, false, nil return true, false, nil
} }
if s.memberStatus.PodUID != pod.GetUID() { if s.memberStatus.Pod.GetUID() != pod.GetUID() {
s.log.Info("Using shutdown now method completed because it is already rotated") s.log.Info("Using shutdown now method completed because it is already rotated")
// The new pod has been started already. // The new pod has been started already.
return true, false, nil return true, false, nil

View file

@ -153,7 +153,7 @@ func (r *Reconciler) updateMemberRotationConditionsPlan(ctx context.Context, api
continue continue
} }
p, ok := cache.Pod().V1().GetSimple(e.Member.PodName) p, ok := cache.Pod().V1().GetSimple(e.Member.Pod.GetName())
if !ok { if !ok {
p = nil p = nil
} }

View file

@ -86,7 +86,7 @@ func (r *Reconciler) createMarkToRemovePlan(ctx context.Context, apiObject k8sut
for _, e := range status.Members.AsListInGroups(rotationByAnnotationOrder...) { for _, e := range status.Members.AsListInGroups(rotationByAnnotationOrder...) {
m := e.Member m := e.Member
group := e.Group group := e.Group
if m.Phase != api.MemberPhaseCreated || m.PodName == "" { if m.Phase != api.MemberPhaseCreated || m.Pod.GetName() == "" {
// Only rotate when phase is created // Only rotate when phase is created
continue continue
} }
@ -96,7 +96,7 @@ func (r *Reconciler) createMarkToRemovePlan(ctx context.Context, apiObject k8sut
continue continue
} }
pod, found := cache.Pod().V1().GetSimple(m.PodName) pod, found := cache.Pod().V1().GetSimple(m.Pod.GetName())
if !found { if !found {
continue continue
} }
@ -186,7 +186,7 @@ func (r *Reconciler) createUpdatePlanInternal(apiObject k8sutil.APIObject, spec
continue continue
} }
p, ok := cache.Pod().V1().GetSimple(m.Member.PodName) p, ok := cache.Pod().V1().GetSimple(m.Member.Pod.GetName())
if !ok { if !ok {
p = nil p = nil
} }

View file

@ -71,7 +71,7 @@ func (r *Reconciler) createRotateOrUpgradeDecision(spec api.DeploymentSpec, stat
} }
func (r *Reconciler) createRotateOrUpgradeDecisionMember(spec api.DeploymentSpec, status api.DeploymentStatus, context PlanBuilderContext, element api.DeploymentStatusMemberElement) (d updateUpgradeDecision) { func (r *Reconciler) createRotateOrUpgradeDecisionMember(spec api.DeploymentSpec, status api.DeploymentStatus, context PlanBuilderContext, element api.DeploymentStatusMemberElement) (d updateUpgradeDecision) {
if element.Member.Phase == api.MemberPhaseCreated && element.Member.PodName != "" { if element.Member.Phase == api.MemberPhaseCreated && element.Member.Pod.GetName() != "" {
// Only upgrade when phase is created // Only upgrade when phase is created
// Got pod, compare it with what it should be // Got pod, compare it with what it should be

View file

@ -373,10 +373,12 @@ func (c *testContext) GetStatus() (api.DeploymentStatus, int32) {
func addAgentsToStatus(t *testing.T, status *api.DeploymentStatus, count int) { func addAgentsToStatus(t *testing.T, status *api.DeploymentStatus, count int) {
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
require.NoError(t, status.Members.Add(api.MemberStatus{ require.NoError(t, status.Members.Add(api.MemberStatus{
ID: fmt.Sprintf("AGNT-%d", i), ID: fmt.Sprintf("AGNT-%d", i),
PodName: fmt.Sprintf("agnt-depl-xxx-%d", i), Pod: &api.MemberPodStatus{
PodSpecVersion: "random", Name: fmt.Sprintf("agnt-depl-xxx-%d", i),
Phase: api.MemberPhaseCreated, SpecVersion: "random",
},
Phase: api.MemberPhaseCreated,
Conditions: []api.Condition{ Conditions: []api.Condition{
{ {
Type: api.ConditionTypeReady, Type: api.ConditionTypeReady,
@ -427,8 +429,10 @@ func TestCreatePlanSingleScale(t *testing.T) {
// Test with 1 single member // Test with 1 single member
status.Members.Single = api.MemberStatusList{ status.Members.Single = api.MemberStatusList{
api.MemberStatus{ api.MemberStatus{
ID: "id", ID: "id",
PodName: "something", Pod: &api.MemberPodStatus{
Name: "something",
},
}, },
} }
newPlan, _, changed = r.createNormalPlan(ctx, depl, nil, spec, status, c) newPlan, _, changed = r.createNormalPlan(ctx, depl, nil, spec, status, c)
@ -444,12 +448,16 @@ func TestCreatePlanSingleScale(t *testing.T) {
// Test with 2 single members (which should not happen) and try to scale down // Test with 2 single members (which should not happen) and try to scale down
status.Members.Single = api.MemberStatusList{ status.Members.Single = api.MemberStatusList{
api.MemberStatus{ api.MemberStatus{
ID: "id1", ID: "id1",
PodName: "something1", Pod: &api.MemberPodStatus{
Name: "something1",
},
}, },
api.MemberStatus{ api.MemberStatus{
ID: "id1", ID: "id1",
PodName: "something1", Pod: &api.MemberPodStatus{
Name: "something1",
},
}, },
} }
newPlan, _, changed = r.createNormalPlan(ctx, depl, nil, spec, status, c) newPlan, _, changed = r.createNormalPlan(ctx, depl, nil, spec, status, c)
@ -490,8 +498,10 @@ func TestCreatePlanActiveFailoverScale(t *testing.T) {
// Test with 1 single member // Test with 1 single member
status.Members.Single = api.MemberStatusList{ status.Members.Single = api.MemberStatusList{
api.MemberStatus{ api.MemberStatus{
ID: "id", ID: "id",
PodName: "something", Pod: &api.MemberPodStatus{
Name: "something",
},
}, },
} }
newPlan, _, changed = r.createNormalPlan(ctx, depl, nil, spec, status, c) newPlan, _, changed = r.createNormalPlan(ctx, depl, nil, spec, status, c)
@ -503,20 +513,28 @@ func TestCreatePlanActiveFailoverScale(t *testing.T) {
// Test scaling down from 4 members to 2 // Test scaling down from 4 members to 2
status.Members.Single = api.MemberStatusList{ status.Members.Single = api.MemberStatusList{
api.MemberStatus{ api.MemberStatus{
ID: "id1", ID: "id1",
PodName: "something1", Pod: &api.MemberPodStatus{
Name: "something1",
},
}, },
api.MemberStatus{ api.MemberStatus{
ID: "id2", ID: "id2",
PodName: "something2", Pod: &api.MemberPodStatus{
Name: "something2",
},
}, },
api.MemberStatus{ api.MemberStatus{
ID: "id3", ID: "id3",
PodName: "something3", Pod: &api.MemberPodStatus{
Name: "something3",
},
}, },
api.MemberStatus{ api.MemberStatus{
ID: "id4", ID: "id4",
PodName: "something4", Pod: &api.MemberPodStatus{
Name: "something4",
},
}, },
} }
newPlan, _, changed = r.createNormalPlan(ctx, depl, nil, spec, status, c) newPlan, _, changed = r.createNormalPlan(ctx, depl, nil, spec, status, c)
@ -572,18 +590,24 @@ func TestCreatePlanClusterScale(t *testing.T) {
// Test with 2 dbservers & 1 coordinator // Test with 2 dbservers & 1 coordinator
status.Members.DBServers = api.MemberStatusList{ status.Members.DBServers = api.MemberStatusList{
api.MemberStatus{ api.MemberStatus{
ID: "db1", ID: "db1",
PodName: "something1", Pod: &api.MemberPodStatus{
Name: "something1",
},
}, },
api.MemberStatus{ api.MemberStatus{
ID: "db2", ID: "db2",
PodName: "something2", Pod: &api.MemberPodStatus{
Name: "something2",
},
}, },
} }
status.Members.Coordinators = api.MemberStatusList{ status.Members.Coordinators = api.MemberStatusList{
api.MemberStatus{ api.MemberStatus{
ID: "cr1", ID: "cr1",
PodName: "coordinator1", Pod: &api.MemberPodStatus{
Name: "coordinator1",
},
}, },
} }
newPlan, _, changed = r.createNormalPlan(ctx, depl, nil, spec, status, c) newPlan, _, changed = r.createNormalPlan(ctx, depl, nil, spec, status, c)
@ -599,26 +623,36 @@ func TestCreatePlanClusterScale(t *testing.T) {
// Now scale down // Now scale down
status.Members.DBServers = api.MemberStatusList{ status.Members.DBServers = api.MemberStatusList{
api.MemberStatus{ api.MemberStatus{
ID: "db1", ID: "db1",
PodName: "something1", Pod: &api.MemberPodStatus{
Name: "something1",
},
}, },
api.MemberStatus{ api.MemberStatus{
ID: "db2", ID: "db2",
PodName: "something2", Pod: &api.MemberPodStatus{
Name: "something2",
},
}, },
api.MemberStatus{ api.MemberStatus{
ID: "db3", ID: "db3",
PodName: "something3", Pod: &api.MemberPodStatus{
Name: "something3",
},
}, },
} }
status.Members.Coordinators = api.MemberStatusList{ status.Members.Coordinators = api.MemberStatusList{
api.MemberStatus{ api.MemberStatus{
ID: "cr1", ID: "cr1",
PodName: "coordinator1", Pod: &api.MemberPodStatus{
Name: "coordinator1",
},
}, },
api.MemberStatus{ api.MemberStatus{
ID: "cr2", ID: "cr2",
PodName: "coordinator2", Pod: &api.MemberPodStatus{
Name: "coordinator2",
},
}, },
} }
spec.DBServers.Count = util.NewInt(1) spec.DBServers.Count = util.NewInt(1)
@ -675,30 +709,42 @@ func TestCreatePlan(t *testing.T) {
// Arrange // Arrange
threeCoordinators := api.MemberStatusList{ threeCoordinators := api.MemberStatusList{
{ {
ID: "1", ID: "1",
PodName: "coordinator1", Pod: &api.MemberPodStatus{
Name: "coordinator1",
},
}, },
{ {
ID: "2", ID: "2",
PodName: "coordinator2", Pod: &api.MemberPodStatus{
Name: "coordinator2",
},
}, },
{ {
ID: "3", ID: "3",
PodName: "coordinator3", Pod: &api.MemberPodStatus{
Name: "coordinator3",
},
}, },
} }
threeDBServers := api.MemberStatusList{ threeDBServers := api.MemberStatusList{
{ {
ID: "1", ID: "1",
PodName: "dbserver1", Pod: &api.MemberPodStatus{
Name: "dbserver1",
},
}, },
{ {
ID: "2", ID: "2",
PodName: "dbserver2", Pod: &api.MemberPodStatus{
Name: "dbserver2",
},
}, },
{ {
ID: "3", ID: "3",
PodName: "dbserver3", Pod: &api.MemberPodStatus{
Name: "dbserver3",
},
}, },
} }
@ -959,9 +1005,9 @@ func TestCreatePlan(t *testing.T) {
} }
c.Pods = map[string]*core.Pod{ c.Pods = map[string]*core.Pod{
c.context.ArangoDeployment.Status.Members.Agents[0].PodName: { c.context.ArangoDeployment.Status.Members.Agents[0].Pod.GetName(): {
ObjectMeta: meta.ObjectMeta{ ObjectMeta: meta.ObjectMeta{
Name: c.context.ArangoDeployment.Status.Members.Agents[0].PodName, Name: c.context.ArangoDeployment.Status.Members.Agents[0].Pod.GetName(),
}, },
}, },
} }

View file

@ -75,7 +75,7 @@ func (r *Reconciler) CheckDeployment(ctx context.Context) error {
continue continue
} }
if err := cache.Client().Kubernetes().CoreV1().Secrets(cache.Namespace()).Delete(ctx, m.PodName, meta.DeleteOptions{}); err != nil { if err := cache.Client().Kubernetes().CoreV1().Secrets(cache.Namespace()).Delete(ctx, m.Pod.GetName(), meta.DeleteOptions{}); err != nil {
r.log.Err(err).Error("Failed to delete secret") r.log.Err(err).Error("Failed to delete secret")
} }
m.Phase = api.MemberPhaseNone m.Phase = api.MemberPhaseNone

View file

@ -77,11 +77,11 @@ func ifPodUIDMismatch(m api.MemberStatus, a api.Action, i pod.Inspector) bool {
u := types.UID(ut) u := types.UID(ut)
if m.PodName == "" { if m.Pod.GetName() == "" {
return false return false
} }
p, ok := i.Pod().V1().GetSimple(m.PodName) p, ok := i.Pod().V1().GetSimple(m.Pod.GetName())
if !ok { if !ok {
return true return true
} }

View file

@ -322,17 +322,16 @@ func (r *Resources) RenderPodForMember(ctx context.Context, acs sutil.ACS, spec
role := group.AsRole() role := group.AsRole()
roleAbbr := group.AsRoleAbbreviated() roleAbbr := group.AsRoleAbbreviated()
newMember := m.DeepCopy() podName := k8sutil.CreatePodName(apiObject.GetName(), roleAbbr, m.ID, CreatePodSuffix(spec))
newMember.PodName = k8sutil.CreatePodName(apiObject.GetName(), roleAbbr, newMember.ID, CreatePodSuffix(spec))
var podCreator interfaces.PodCreator var podCreator interfaces.PodCreator
if group.IsArangod() { if group.IsArangod() {
// Prepare arguments // Prepare arguments
autoUpgrade := newMember.Conditions.IsTrue(api.ConditionTypeAutoUpgrade) || spec.Upgrade.Get().AutoUpgrade autoUpgrade := m.Conditions.IsTrue(api.ConditionTypeAutoUpgrade) || spec.Upgrade.Get().AutoUpgrade
podCreator = &MemberArangoDPod{ podCreator = &MemberArangoDPod{
status: *newMember, podName: podName,
status: m,
groupSpec: groupSpec, groupSpec: groupSpec,
spec: spec, spec: spec,
group: group, group: group,
@ -357,6 +356,7 @@ func (r *Resources) RenderPodForMember(ctx context.Context, acs sutil.ACS, spec
} }
podCreator = &MemberSyncPod{ podCreator = &MemberSyncPod{
podName: podName,
groupSpec: groupSpec, groupSpec: groupSpec,
spec: spec, spec: spec,
group: group, group: group,
@ -364,13 +364,13 @@ func (r *Resources) RenderPodForMember(ctx context.Context, acs sutil.ACS, spec
imageInfo: imageInfo, imageInfo: imageInfo,
arangoMember: *member, arangoMember: *member,
apiObject: apiObject, apiObject: apiObject,
memberStatus: *newMember, memberStatus: m,
} }
} else { } else {
return nil, errors.Newf("unable to render Pod") return nil, errors.Newf("unable to render Pod")
} }
pod, err := RenderArangoPod(ctx, cache, apiObject, role, newMember.ID, newMember.PodName, podCreator) pod, err := RenderArangoPod(ctx, cache, apiObject, role, m.ID, podName, podCreator)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -455,7 +455,6 @@ func (r *Resources) createPodForMember(ctx context.Context, cachedStatus inspect
// Update pod name // Update pod name
role := group.AsRole() role := group.AsRole()
m.PodName = template.PodSpec.GetName()
newPhase := api.MemberPhaseCreated newPhase := api.MemberPhaseCreated
// Create pod // Create pod
if group.IsArangod() { if group.IsArangod() {
@ -472,20 +471,26 @@ func (r *Resources) createPodForMember(ctx context.Context, cachedStatus inspect
return errors.WithStack(err) return errors.WithStack(err)
} }
m.PodName = podName var pod api.MemberPodStatus
m.PodUID = uid
m.PodSpecVersion = template.PodSpecChecksum pod.Name = podName
pod.UID = uid
pod.SpecVersion = template.PodSpecChecksum
m.Pod = &pod
m.Pod.Propagate(&m)
m.ArangoVersion = m.Image.ArangoDBVersion m.ArangoVersion = m.Image.ArangoDBVersion
m.ImageID = m.Image.ImageID m.ImageID = m.Image.ImageID
// reset old sidecar values to nil // reset old sidecar values to nil
m.SideCarSpecs = nil m.SideCarSpecs = nil
log.Str("pod-name", m.PodName).Debug("Created pod") log.Str("pod-name", pod.Name).Debug("Created pod")
if m.Image == nil { if m.Image == nil {
log.Str("pod-name", m.PodName).Debug("Created pod with default image") log.Str("pod-name", pod.Name).Debug("Created pod with default image")
} else { } else {
log.Str("pod-name", m.PodName).Debug("Created pod with predefined image") log.Str("pod-name", pod.Name).Debug("Created pod with predefined image")
} }
} else if group.IsArangosync() { } else if group.IsArangosync() {
// Check monitoring token secret // Check monitoring token secret
@ -511,11 +516,17 @@ func (r *Resources) createPodForMember(ctx context.Context, cachedStatus inspect
if err != nil { if err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
log.Str("pod-name", m.PodName).Debug("Created pod")
m.PodName = podName var pod api.MemberPodStatus
m.PodUID = uid
m.PodSpecVersion = template.PodSpecChecksum pod.Name = podName
pod.UID = uid
pod.SpecVersion = template.PodSpecChecksum
m.Pod = &pod
m.Pod.Propagate(&m)
log.Str("pod-name", pod.Name).Debug("Created pod")
} }
member.GetPhaseExecutor().Execute(r.context.GetAPIObject(), spec, group, &m, api.Action{}, newPhase) member.GetPhaseExecutor().Execute(r.context.GetAPIObject(), spec, group, &m, api.Action{}, newPhase)
@ -532,7 +543,7 @@ func (r *Resources) createPodForMember(ctx context.Context, cachedStatus inspect
} }
} }
log.Str("pod", m.PodName).Info("Updating member") log.Str("pod", m.Pod.GetName()).Info("Updating member")
if err := status.Members.Update(m, group); err != nil { if err := status.Members.Update(m, group); err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
@ -540,7 +551,7 @@ func (r *Resources) createPodForMember(ctx context.Context, cachedStatus inspect
return errors.WithStack(err) return errors.WithStack(err)
} }
// Create event // Create event
r.context.CreateEvent(k8sutil.NewPodCreatedEvent(m.PodName, role, apiObject)) r.context.CreateEvent(k8sutil.NewPodCreatedEvent(m.Pod.GetName(), role, apiObject))
return nil return nil
} }

View file

@ -50,6 +50,7 @@ var _ interfaces.PodCreator = &MemberArangoDPod{}
var _ interfaces.ContainerCreator = &ArangoDContainer{} var _ interfaces.ContainerCreator = &ArangoDContainer{}
type MemberArangoDPod struct { type MemberArangoDPod struct {
podName string
status api.MemberStatus status api.MemberStatus
groupSpec api.ServerGroupSpec groupSpec api.ServerGroupSpec
spec api.DeploymentSpec spec api.DeploymentSpec

View file

@ -61,6 +61,7 @@ var _ interfaces.PodCreator = &MemberSyncPod{}
var _ interfaces.ContainerCreator = &ArangoSyncContainer{} var _ interfaces.ContainerCreator = &ArangoSyncContainer{}
type MemberSyncPod struct { type MemberSyncPod struct {
podName string
tlsKeyfileSecretName string tlsKeyfileSecretName string
clientAuthCASecretName string clientAuthCASecretName string
masterJWTSecretName string masterJWTSecretName string

View file

@ -372,7 +372,7 @@ func (r *Resources) InspectPods(ctx context.Context, cachedStatus inspectorInter
for _, e := range status.Members.AsList() { for _, e := range status.Members.AsList() {
m := e.Member m := e.Member
group := e.Group group := e.Group
if podName := m.PodName; podName != "" { if podName := m.Pod.GetName(); podName != "" {
if _, exists := cachedStatus.Pod().V1().GetSimple(podName); !exists { if _, exists := cachedStatus.Pod().V1().GetSimple(podName); !exists {
log.Str("pod-name", podName).Debug("Does not exist") log.Str("pod-name", podName).Debug("Does not exist")
switch m.Phase { switch m.Phase {

View file

@ -60,7 +60,7 @@ func (r *Resources) EnsureLeader(ctx context.Context, cachedStatus inspectorInte
changed := false changed := false
group := api.ServerGroupAgents group := api.ServerGroupAgents
for _, e := range status.Members.AsListInGroup(group) { for _, e := range status.Members.AsListInGroup(group) {
pod, exist := cachedStatus.Pod().V1().GetSimple(e.Member.PodName) pod, exist := cachedStatus.Pod().V1().GetSimple(e.Member.Pod.GetName())
if !exist { if !exist {
continue continue
} }
@ -217,7 +217,7 @@ func (r *Resources) ensureSingleServerLeader(ctx context.Context, cachedStatus i
status, _ := r.context.GetStatus() status, _ := r.context.GetStatus()
for _, m := range status.Members.Single { for _, m := range status.Members.Single {
pod, exist := cachedStatus.Pod().V1().GetSimple(m.PodName) pod, exist := cachedStatus.Pod().V1().GetSimple(m.Pod.GetName())
if !exist { if !exist {
continue continue
} }
@ -241,7 +241,7 @@ func (r *Resources) ensureSingleServerLeader(ctx context.Context, cachedStatus i
err := r.context.ApplyPatchOnPod(ctx, pod, patch.ItemReplace(patch.NewPath("metadata", "labels"), labels)) err := r.context.ApplyPatchOnPod(ctx, pod, patch.ItemReplace(patch.NewPath("metadata", "labels"), labels))
if err != nil { if err != nil {
return errors.WithMessagef(err, "unable to change leader label for pod %s", m.PodName) return errors.WithMessagef(err, "unable to change leader label for pod %s", m.Pod.GetName())
} }
changed = true changed = true
} }

View file

@ -102,10 +102,10 @@ func (r *Resources) inspectFinalizerPVCMemberExists(ctx context.Context, group a
} }
// Member still exists, let's trigger a delete of it // Member still exists, let's trigger a delete of it
if memberStatus.PodName != "" { if memberStatus.Pod.GetName() != "" {
log.Info("Removing Pod of member, because PVC is being removed") log.Info("Removing Pod of member, because PVC is being removed")
err := globals.GetGlobalTimeouts().Kubernetes().RunWithTimeout(ctx, func(ctxChild context.Context) error { err := globals.GetGlobalTimeouts().Kubernetes().RunWithTimeout(ctx, func(ctxChild context.Context) error {
return r.context.ACS().CurrentClusterCache().PodsModInterface().V1().Delete(ctxChild, memberStatus.PodName, meta.DeleteOptions{}) return r.context.ACS().CurrentClusterCache().PodsModInterface().V1().Delete(ctxChild, memberStatus.Pod.GetName(), meta.DeleteOptions{})
}) })
if err != nil && !k8sutil.IsNotFound(err) { if err != nil && !k8sutil.IsNotFound(err) {
log.Err(err).Debug("Failed to delete pod") log.Err(err).Debug("Failed to delete pod")

View file

@ -87,7 +87,7 @@ func IsRotationRequired(acs sutil.ACS, spec api.DeploymentSpec, member api.Membe
// Check if pod details are propagated // Check if pod details are propagated
if pod != nil { if pod != nil {
if member.PodUID != pod.UID { if member.Pod.GetUID() != pod.UID {
reason = "Pod UID does not match, this pod is not managed by Operator. Recreating" reason = "Pod UID does not match, this pod is not managed by Operator. Recreating"
mode = EnforcedRotation mode = EnforcedRotation
return return
@ -100,7 +100,7 @@ func IsRotationRequired(acs sutil.ACS, spec api.DeploymentSpec, member api.Membe
} }
} }
if member.PodSpecVersion == "" { if p := member.Pod; p != nil && p.SpecVersion == "" {
reason = "Pod Spec Version is nil - recreating pod" reason = "Pod Spec Version is nil - recreating pod"
mode = EnforcedRotation mode = EnforcedRotation
return return

View file

@ -100,7 +100,7 @@ func (d *Deployment) ReadyPodCount() int {
count := 0 count := 0
status, _ := d.GetStatus() status, _ := d.GetStatus()
for _, e := range status.Members.AsList() { for _, e := range status.Members.AsList() {
if e.Member.PodName == "" { if e.Member.Pod.GetName() == "" {
continue continue
} }
if e.Member.Conditions.IsTrue(api.ConditionTypeReady) { if e.Member.Conditions.IsTrue(api.ConditionTypeReady) {

View file

@ -47,7 +47,7 @@ func (m member) ID() string {
func (m member) PodName() string { func (m member) PodName() string {
if status, found := m.status(); found { if status, found := m.status(); found {
return status.PodName return status.Pod.GetName()
} }
return "" return ""
} }