Skip to content

Commit

Permalink
Merge pull request #425 from asifdxtreme/syncer
Browse files Browse the repository at this point in the history
Soda-DR solution
  • Loading branch information
skdwriting authored Jun 28, 2021
2 parents b5ab205 + b7055bc commit 425a2f4
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ const (

operationTimeout = 10 * time.Second

sodaEndpoint = "soda-proxy:50029/getprofile/"
sodaProfileEndpoint = "soda-proxy:50029/getprofile/"
sodaSnapShotEnableEndpoint = "soda-proxy:50029/getprofile/"
)

var (
Expand Down Expand Up @@ -504,7 +505,7 @@ type prepareProvisionResult struct {

type CustomPropertiesSpec map[string]interface{}

// Get the driver name from the customProperties of Soda Profile
//GetDriverPreference get the driver name from the customProperties of Soda Profile
func (cps CustomPropertiesSpec) GetDriverPreference() (string, error) {
var driverName string
if cps != nil {
Expand All @@ -515,6 +516,7 @@ func (cps CustomPropertiesSpec) GetDriverPreference() (string, error) {
driverName = fmt.Sprintf("%v", v)
return driverName, nil
}

}
return driverName, fmt.Errorf("DriverName not found in the customProperties of Soda Profile")
}
Expand All @@ -523,7 +525,7 @@ func (cps CustomPropertiesSpec) GetDriverPreference() (string, error) {
func (p *csiProvisioner) isCallForCurrentDriver(profileID string) (string, error) {

var backendDriverNameFromPlugin string
url := "http://" + sodaEndpoint + profileID
url := "http://" + sodaProfileEndpoint + profileID
response, err := http.Get(url)
if err != nil {
return backendDriverNameFromPlugin, fmt.Errorf("error in getting the Profile Details : %s", err.Error())
Expand All @@ -550,18 +552,34 @@ func (p *csiProvisioner) isCallForCurrentDriver(profileID string) (string, error
return backendDriverNameFromPlugin, nil
}

// Call the soda-syncer for Snapshot
func (p *csiProvisioner) snapShotEnable(profileID string) error {

// Call the SnapshotEnable
url := "http://" + sodaSnapShotEnableEndpoint + profileID
response, err := http.Get(url)
if err != nil {
return fmt.Errorf("error in getting the Profile Details : %s", err.Error())
} else {
klog.V(2).Infof("SnapshotEnabled, ", response.Body)
}
return nil
}

// prepareProvision does non-destructive parameter checking and preparations for provisioning a volume.
func (p *csiProvisioner) prepareProvision(ctx context.Context, claim *v1.PersistentVolumeClaim, sc *storagev1.StorageClass, selectedNode *v1.Node) (*prepareProvisionResult, controller.ProvisioningState, error) {
if sc == nil {
return nil, controller.ProvisioningFinished, errors.New("storage class was nil")
}

var profileID string
// Add Soda intelligence to pick the driver from ProfileID received in StorageClass Parameter
if sc.Provisioner == "soda-csi" {
for k, v := range sc.Parameters {
if k != "profile" {
continue
}
profileID = v
backendDriverName, err := p.isCallForCurrentDriver(v)
if err != nil {
return nil, controller.ProvisioningFinished, &controller.IgnoredError{
Expand All @@ -570,6 +588,7 @@ func (p *csiProvisioner) prepareProvision(ctx context.Context, claim *v1.Persist
sc.Provisioner = backendDriverName
}
}
go p.snapShotEnable(profileID)

migratedVolume := false
if p.supportsMigrationFromInTreePluginName != "" {
Expand Down
8 changes: 8 additions & 0 deletions csi-plug-n-play/sidecars/soda-proxy/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Based on ubuntu
FROM ubuntu:xenial
LABEL maintainers="Mohammad Asif Siddiqui <[email protected]>"

COPY soda-proxy /soda-proxy

# Define default command
ENTRYPOINT ["/soda-proxy"]
24 changes: 21 additions & 3 deletions csi-plug-n-play/sidecars/soda-proxy/cmd/proxy.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
// Copyright 2021 The SodaFoundation Authors.
//
// 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.

package main

import (
"github.com/gorilla/mux"
"github.com/sodafoundation/nbp/csi-plug-n-play/sidecars/soda-proxy/pkg/controller/profile"
"github.com/sodafoundation/nbp/csi-plug-n-play/sidecars/soda-proxy/pkg/controller/snapshot"

"log"
"net/http"
)

func handleRequests() {
myRouter := mux.NewRouter().StrictSlash(true)
myRouter.HandleFunc("/getprofile/{id}", profile.GetProfile)
log.Fatal(http.ListenAndServe("0.0.0.0:50029", myRouter))
sodaProxyRouter := mux.NewRouter().StrictSlash(true)
sodaProxyRouter.HandleFunc("/getprofile/{id}", profile.GetProfile)
sodaProxyRouter.HandleFunc("/snapshot/{id}", snapshot.CreateSnapshot)
log.Fatal(http.ListenAndServe("0.0.0.0:50029", sodaProxyRouter))

}

func main() {
Expand Down
63 changes: 63 additions & 0 deletions csi-plug-n-play/sidecars/soda-proxy/deploy/sodaProxy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: soda-proxy
namespace: default
labels:
soda-app: proxy
spec:
selector:
matchLabels:
soda-app: proxy
template:
metadata:
labels:
soda-app: proxy
spec:
containers:
- name: soda-proxy
image: sodafoundation/soda-proxy:v1.0
env:
- name: OPENSDS_ENDPOINT
value: "http://{YourHOSTIP}:50040"
- name: OPENSDS_AUTH_STRATEGY
value: "keystone"
- name: OS_AUTH_URL
value: "http://{YourHOSTIP}/identity"
- name: OS_USERNAME
value: "admin"
- name: OS_PASSWORD
value: "opensds@123"
- name: OS_TENANT_NAME
value: "admin"
- name: OS_PROJECT_NAME
value: "admin"
- name: OS_USER_DOMAIN_ID
value: "default"
- name: NODE_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
ports:
- containerPort: 50029
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
terminationGracePeriodSeconds: 30

---
apiVersion: v1
kind: Service
metadata:
name: soda-proxy
labels:
soda-app: proxy
spec:
ports:
- port: 50029
protocol: TCP
selector:
soda-app: proxy
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
// Copyright 2021 The SodaFoundation Authors.
//
// 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.

package profile

import (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2021 The SodaFoundation Authors.
//
// 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.

package snapshot

import (
"bytes"
"fmt"
"github.com/gorilla/mux"

"encoding/json"
"github.com/sodafoundation/nbp/client/opensds"
"net/http"
"os"
)

type SnapshotProfile struct {
AwsAccesskey string `json:"AWS_ACCESS_KEY_ID"`
AwsSecretkey string `json:"AWS_SECRET_ACCESS_KEY"`
ResticRepository string `json:"RESTIC_REPOSITORY"`
ResticPassword string `json:"RESTIC_PASSWORD"`
TimeInterval int `json:"timeInterval"`
ResticSourceRepo string `json:"resticSourceRepo"`
}

func CreateSnapshot(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
key := vars["id"]
opensdsEndpoint, errLookUp := os.LookupEnv("OPENSDS_ENDPOINT")
if !errLookUp {
fmt.Println("No env variables found for endpoint, switching to default")
opensdsEndpoint = "http://127.0.0.1:50040"
}
client, err := opensds.GetClient(opensdsEndpoint, "keystone")
if client == nil || err != nil {
fmt.Printf("get opensds client failed: %v", err)
}
profile, errosds := client.GetProfile(key)
if errosds != nil {
fmt.Printf("got error in GetProfile : %s", errosds.Error())
}

var timeInterval int
timeFloat := profile.CustomProperties["TimeInterval"].(float64)
timeInterval = int(timeFloat)

snapshotProfile := SnapshotProfile{AwsAccesskey: fmt.Sprintf("%v", profile.CustomProperties["AWS_ACCESS_KEY_ID"]), AwsSecretkey: fmt.Sprintf("%v", profile.CustomProperties["AWS_SECRET_ACCESS_KEY"]), ResticRepository: fmt.Sprintf("%v", profile.CustomProperties["RESTIC_REPOSITORY"]), ResticPassword: fmt.Sprintf("%v", profile.CustomProperties["RESTIC_PASSWORD"]), TimeInterval: timeInterval, ResticSourceRepo: ""}

postBody, _ := json.Marshal(snapshotProfile)
fmt.Println(postBody)
requestBody := bytes.NewBuffer(postBody)
syncerEndpoint := os.Getenv("NODE_IP")

response, err := http.Post("http://"+syncerEndpoint+":50030/snapshot", "application/json", requestBody)
fmt.Println(response.Body)
json.NewEncoder(w).Encode(response.Body)
}
34 changes: 17 additions & 17 deletions soda-syncer/pkg/snapshot/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,41 +25,41 @@ import (
)

type SnapshotProfile struct {
AwsAccesskey string `json:"AWS_ACCESS_KEY_ID"`
AwsSecretkey string `json:"AWS_SECRET_ACCESS_KEY"`
AwsAccesskey string `json:"AWS_ACCESS_KEY_ID"`
AwsSecretkey string `json:"AWS_SECRET_ACCESS_KEY"`
ResticRepository string `json:"RESTIC_REPOSITORY"`
ResticPassword string `json:"RESTIC_PASSWORD"`
TimeInterval int `json:"timeInterval"`
ResticPassword string `json:"RESTIC_PASSWORD"`
TimeInterval int `json:"timeInterval"`
ResticSourceRepo string `json:"resticSourceRepo"`
}

func CreateSnapshot(w http.ResponseWriter, r *http.Request) {

// TODO Remove this Sleep time after changing provisioner to send request after PVC Attachment event
time.Sleep(20*time.Second)
w.Header().Set("Content-Type","application/json")
time.Sleep(20 * time.Second)
w.Header().Set("Content-Type", "application/json")

fmt.Println("---------------------Getting Source Mount Point ---------------------")
//TODO Configure grep based on the pvc name recieved in this request
cmdToGetPVCMountPoint := "df -h --output=target | grep csi"
cmdToGetFileSystemMountPoint, errno := exec.Command("bash", "-c", cmdToGetPVCMountPoint).Output()
if errno != nil {
fmt.Sprintf("Failed to execute command: %s", cmdToGetFileSystemMountPoint)
fmt.Sprintf("Failed to execute command: %s", cmdToGetFileSystemMountPoint)
}
mountPointToBeBackedUp := string(cmdToGetFileSystemMountPoint)
fmt.Println("The MountPoint to be backed up : ",mountPointToBeBackedUp )
fmt.Println("The MountPoint to be backed up : ", mountPointToBeBackedUp)

fmt.Println("---------------------Backing Up ---------------------")
var snapshotProfile SnapshotProfile
_ = json.NewDecoder(r.Body).Decode(&snapshotProfile)
fmt.Println(snapshotProfile)
os.Setenv("AWS_ACCESS_KEY_ID",snapshotProfile.AwsAccesskey)
os.Setenv("AWS_SECRET_ACCESS_KEY",snapshotProfile.AwsSecretkey)
os.Setenv("RESTIC_REPOSITORY",snapshotProfile.ResticRepository)
os.Setenv("RESTIC_PASSWORD",snapshotProfile.ResticPassword)
os.Setenv("AWS_ACCESS_KEY_ID", snapshotProfile.AwsAccesskey)
os.Setenv("AWS_SECRET_ACCESS_KEY", snapshotProfile.AwsSecretkey)
os.Setenv("RESTIC_REPOSITORY", snapshotProfile.ResticRepository)
os.Setenv("RESTIC_PASSWORD", snapshotProfile.ResticPassword)

timeD := time.Duration(snapshotProfile.TimeInterval )
ticker := time.NewTicker(timeD* time.Second)
timeD := time.Duration(snapshotProfile.TimeInterval)
ticker := time.NewTicker(timeD * time.Second)
done := make(chan bool)
go func() {
for {
Expand All @@ -68,15 +68,15 @@ func CreateSnapshot(w http.ResponseWriter, r *http.Request) {
return
case t := <-ticker.C:
fmt.Println("Backup Started at", t)
cmdToDoBackup := "restic backup "+mountPointToBeBackedUp
cmdToDoBackup := "restic backup " + mountPointToBeBackedUp
cmdOutputForBackup, errBck := exec.Command("bash", "-c", cmdToDoBackup).Output()
if errBck != nil {
fmt.Sprintf("Failed to execute command: %s", string(cmdOutputForBackup))
}
fmt.Println("Backup Success : ", string(cmdOutputForBackup))

fmt.Println("---------------------SnapShots---------------------")
cmd := exec.Command("restic", "snapshots" )
cmd := exec.Command("restic", "snapshots")
cmd.Stderr = os.Stdout
cmd.Stdout = os.Stdout

Expand All @@ -89,4 +89,4 @@ func CreateSnapshot(w http.ResponseWriter, r *http.Request) {
}
}()
json.NewEncoder(w).Encode("Back Up Done Successfully")
}
}

0 comments on commit 425a2f4

Please sign in to comment.