I'm building a GO-based operator using Operator-sdk to manage my custom resource called Module which owns a deployment. My _types.go looks like this:
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:subresource:scale:specpath=.spec.workerSettings.replicas,statuspath=.status.replicas,selectorpath=.status.selector
//+kubebuilder:printcolumn:name="Replicas",type="integer",JSONPath=".spec.workerSettings.replicas",description="The number of pods for this Module"
//+kubebuilder:printcolumn:name="Ready",type="integer",JSONPath=".status.readyReplicas",description="The number of ready pods for this Module"
//+kubebuilder:resource:shortName=mod;mods
// Module is the Schema for the Module API
type Module struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ModuleSpec `json:"spec,omitempty"`
Status ModuleStatus `json:"status,omitempty"`
}
// ModuleStatus defines the observed state of Module
type ModuleStatus struct {
Replicas int32 `json:"replicas"`
ReadyReplicas int32 `json:"readyReplicas"`
Selector string `json:"selector"`
Conditions []metav1.Condition `json:"conditions"`
}
In reconcile I'm trying to set value of the ReadyReplicas field of my CR, which corresponds to the similar field of the deployment my custom resource (Module) owns.
func (r *ModuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var mod modulev1.Module
err := r.Get(ctx, req.NamespacedName, &mod)
if errors.IsNotFound(err) {
return ctrl.Result{}, nil
} else if err != nil {
return ctrl.Result{Requeue: true}, err
}
var deploy appsv1.Deployment
if created, err := r.createDeployIfNotExists(ctx, &deploy, &mod); err != nil {
return ctrl.Result{}, err
} else if created {
return ctrl.Result{Requeue: true}, nil
}
...
// trying to set status the CR
mod.Status.ReadyReplicas = deploy.Status.ReadyReplicas
if r.Client.Status().Update(ctx, &mod); err != nil {
log.Error(err, "failed to update Module status")
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
But nothing changes.. r.Client.Status().Update(ctx, &mod) performs silently, without any error, but the same costume resource instance comes back to the reconcile with default values of the all status fields.
v1alpha1.ModuleStatus {Replicas: 0, ReadyReplicas: 0, Selector: "", Conditions: []k8s.io/apimachinery/pkg/apis/meta/v1.Condition len: 0, cap: 0, nil}
I checked how the manifest of custom resource looks "on the cluster" and it has no status section at all. Wherein it seems to be generated properly in my CRD:
Please, help me figure out what I'm missing.
Related
I have struct of configuration like this(in short version):
type Config struct {
Environment string
Service1 Service
Service2 Service
}
type Service struct {
CRC string
Cards Cards
}
type Cards struct {
GBP CardCfg
USD CardCfg
}
type CardCfg struct {
CRC string
}
func Cfg() *Config {
return &Config{
Environment: os.Getenv("ENVIRONMENT"),
Service1: Service{
CRC: os.Getenv("Service1_CRC"),
Cards: Cards{
GBP: CardCfg{
CRC: os.Getenv("Service1_CARD_GBP_CRC"),
},
USD: CardCfg{
CRC: os.Getenv("Service1_CARD_USD_CRC"),
},
},
},
Service2: Service{
CRC: os.Getenv("Service2_CRC"),
Cards: Cards{
GBP: CardCfg{
CRC: os.Getenv("Service2_CARD_GBP_CRC"),
},
USD: CardCfg{
CRC: os.Getenv("Service2_CARD_USD_CRC"),
},
},
},
}
}
I try to get access to service crc or service card crc by variable like this:
variable := "Service1"
currency := "EUR"
cfg := config.Cfg()
crc := cfg[variable].cards[currency] // DOESN'T WORK
I always tried with map, like this:
package main
import "fmt"
type Config map[string]interface{}
func main() {
config := Config{
"field": "value",
"service1": Config{
"crc": "secret1",
"cards": Config{
"crc": "secret2",
},
},
}
fmt.Println(config["WT"].(Config)["cards"].(Config)["crc"]) //WORK
}
but it looks wierd for me. Do you know better way to write config? It's possible to use struct? I come form Ruby planet, Golang is new for me.
edit:
I receive messages from rabbit queue, based on them I create a payment. Unfortunately, various payment methods require "own" authorization (crc and merchantId). Call looks like this:
trn, err := p24Client.RegisterTrn(context.Background(), &p24.RegisterTrnReq{
CRC: cfg[payinRequested.Service].cards[payinRequested.Currency].CRC,
MerchantId: cfg[payinRequested.Service].cards[payinRequested.Currency].MerchantId,
PosId: cfg[payinRequested.Service].cards[payinRequested.Currency].MerchantId,
SessionId: payinRequested.PaymentId,
Amount: payinRequested.Amount,
Currency: payinRequested.Currency,
Description: payinRequested.Desc,
Email: payinRequested.Email,
Method: payinRequested.BankId,
UrlReturn: payinRequested.ReturnUrl,
UrlStatus: cfg.StatusUri,
UrlCardPaymentNotification: cfg.CardStatusUri,
})
Any ideas on how to do it right?
Ignoring the reflect package, the simple answer is: you can't. You cannot access struct fields dynamically (using string variables). You can, use variables on a map, because accessing data in a map is a hashtable lookup. A struct isn't.
I will reiterate the main point of my comments though: What you're seemingly trying to do is using environment variables to set values on a config struct. This is very much a solved problem. We've been doing this for years at this point. I did a quick google search and found this repo which does exactly what you seem to want to do (and more): called configure
With this package, you can declare your config struct like this:
package config
type Config struct {
Environment string `env:"ENVIRONMENT" cli:"env" yaml:"environment"`
Services []*Service `env:"SERVICE" cli:"service" yaml:"service"`
serviceByName map[string]*Service
}
Then, to load from environment variables:
func LoadEnv() (*Config, err) {
c := Config{
serviceByName: map[string]*Service{},
} // set default values if needed
if err := configure.ParseEnv(&c); err != nil {
return nil, err
}
// initialise convenience fields like serviceByName:
for _, svc := range c.Services {
c.serviceByName[svc.Name] = svc
}
return &c, nil
}
// ServiceByName returns a COPY of the config for a given service
func (c Config) ServiceByName(n string) (Service, error) {
s, ok := c.serviceByName[n]
if !ok {
return nil, errrors.New("service with given name does not exist")
}
return *s, nil
}
You can also define a single Load function that will prioritise one type of config over the other. With these tags, we're supporting environment variables, a Yaml file, and command line arguments. Generally command line arguments override any of the other formats. As for Yaml vs environment variables, you could argue both ways: an environment variable like ENVIRONMENT isn't very specific, and could easily be used by multiple processes by mistake. Then again, if you deploy things properly, that shouldn't be an issue, so for that reason, I'd prioritise environment variables over the Yaml file:
func Load(args []string) (*Config, error) {
c := &Config{
Environment: "devel", // default
serviceByName: map[string]*Service{},
}
if err := configure.ParseYaml(c); err != nil {
return nil, err
}
if err := configure.ParseEnv(c); err != nil {
return nil, err
}
if len(args) > 0 {
if err := configure.ParseCommanLine(c, args); err != nil {
return nil, err
}
}
// initialise convenience fields like serviceByName:
for _, svc := range c.Services {
c.serviceByName[svc.Name] = svc
}
return &c, nil
}
Then in your main package:
func main() {
cfg, err := config.Load(os.Args[1:])
if err != nil {
fmt.Printf("Failed to load config: %v\n", err)
os.Exit(1)
}
wtCfg, err := config.ServiceByName("WT")
if err != nil {
fmt.Printf("WT service not found: %v\n", err)
return
}
fmt.Printf("%#v\n", wtCfg)
}
I pasted a section of code that was supposed to catch an AllTopologyNodesDownError error which doesn't work and I have no idea why.
func (sc *ServerConfig) addNodesToCluster(store *ravendb.DocumentStore) error {
clusterTopology, err := sc.getClusterTopology(store)
if errors.Is(err, &ravendb.AllTopologyNodesDownError{}) {
for _, url := range sc.Url.List {
err = addNodeToCluster(store, url)
if err != nil {
return err
}
}
} else if err != nil {
return err
}
the structure of the ravendb.AllTopologyNodesDownError is
// AllTopologyNodesDownError represents "all topology nodes are down" error
type AllTopologyNodesDownError struct {
errorBase
}
type errorBase struct {
wrapped error
ErrorStr string
}
screen shot of the error when debugging the code
errors.Is() is used to tell if any error in the chain is the same instance as the provided error1, that can never be the case here because you provided a literal of your error type, no other code could hold that instance or a reference to it.
Your error looks like a type, to tell if any error in the chain is a given type you should use errors.As():
clusterTopology, err := sc.getClusterTopology(store)
var errAllDown *AllTopologyNodesDownError
if errors.As(err, &errAllDown) {
// err had an *AllTopologyNodesDownError in its
// chain and errAllDown now contains it.
}
Can be overridden by implementing the Unwrap() interface which your error type does not.
I'm stuck with an obvious operation: retrieving multiple rows using gorm.Find() method.
(resolver.go)
package resolver
type Root struct {
DB *gorm.DB
}
func (r *Root) Users(ctx context.Context) (*[]*UserResolver, error) {
var userRxs []*UserResolver
var users []model.User
// debug-start
// This is to prove r.DB is allocated and working
// It will print {2 alice#mail.com} in the console
var user model.User
r.DB.Find(&user)
log.Println(user)
// debug-end
if err := r.DB.Find(&users); err != nil { // <-- not working
log.Fatal(err)
}
for _, user := range users {
userRxs = append(userRxs, &UserResolver{user})
log.Println(user)
}
return &userRxs, nil
}
(model.go)
package model
type User struct {
ID graphql.ID `gorm:"primary_key"`
Email string `gorm:"unique;not null"`
}
The mysql table is filled with 2 values. Here is the content in json style:
{
{ Email: bob#mail.com },
{ Email: alice#mail.com },
}
This is the result when I run the program:
2020/05/13 12:23:17 Listening for requests on :8000
2020/05/13 12:23:22 {2 alice#mail.com}
2020/05/13 12:23:22 &{{{0 0} 0 0 0 0} 0xc0004cee40 <nil> 2 0xc00031e3c0 false 0 {0xc00035bea0} 0xc0004b3080 {{0 0} {<nil>} map[] 0} 0xc000408340 <nil> 0xc0004cee60 false <nil>}
What is wrong with my code? It seems from all the tuto/so/etc.. sources that I'm correctly defining a slice var and passing it to the Find() function..
if err := r.DB.Find(&users); err != nil { // <-- not working
log.Fatal(err)
}
Probably you forgot to mention Error property and returned object in this case is not nil for sure (please mention that Find returns not error interface in this case)
Please try something like that
if err := r.DB.Find(&users).Error; err != nil {
log.Fatal(err)
}
Hope it helps
You need to use a slice of pointers:
users := make([]*model.User, 0, 2)
if err := r.DB.Find(&users).Error; err != nil {
log.Fatal(err)
}
I am working on adding private data into the Hyperledger using Fabric SDK Go but getting error while invoking the data.
Instantiate Chaincode
ccPolicy, err := cauthdsl.FromString("AND ('Org1MSP.member','Org2MSP.member')")
resMgmt.InstantiateCC(
setup.Org.ChannelID,
resmgmt.InstantiateCCRequest{
Name: chaincodeId,
Path: setup.Org.ChaincodePath,
Version: chaincodeVersion,
Args: [][]byte{[]byte("init")},
Policy: ccPolicy,
},resmgmt.WithRetry(retry.DefaultResMgmtOpts))
collections_config.json
[
{
"name": "collectionMedium",
"policy": "AND ('Org1MSP.member', 'Org2MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive":1000000
},
{
"name": "collectionPrivate",
"policy": "OR('Org2MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive":5
}
]
Invoke
product := &model.Product{id, name, color, length, width}
productBytes, err1 := json.Marshal(product)
if err1 != nil {
return shim.Error(err1.Error())
}
err2 := stub.PutPrivateData("collectionMedium", id, productBytes)
if err2 != nil {
return shim.Error(err2.Error())
}
Error
Chaincode status Code: (500) UNKNOWN. Description: PUT_STATE failed: collection config not defined for chaincode [CC_ORG_V00], pass the collection configuration upon chaincode definition/instantiation
so, it says collection config is not defined during the instantiation of the chaincode. But, i don't know exactly how to add collection config in chaincode instantiation request.
please suggest me solution.
I am able to create CollConfig requests in following manner and able to add collection config into my chaincode instantiation.
My Solution
#CollConfig 1
var collCfg1RequiredPeerCount, collCfg1MaximumPeerCount int32
var collCfg1BlockToLive uint64
collCfg1Name := "collectionMedium"
collCfg1BlockToLive = 1000
collCfg1RequiredPeerCount = 0
collCfg1MaximumPeerCount = 3
collCfg1Policy := "OR('Org1MSP.member','Org2MSP.member')"
collCfg1, err := newCollectionConfig(collCfg1Name,collCfg1Policy, collCfg1RequiredPeerCount,collCfg1MaximumPeerCount,collCfg1BlockToLive)
if err != nil {
return errors.WithMessage(err, "failed to create collection config 1")
}
#CollConfig 2
var collCfg2RequiredPeerCount, collCfg2MaximumPeerCount int32
var collCfg2BlockToLive uint64
collCfg2Name := "collectionPrivate"
collCfg2BlockToLive = 100
collCfg2RequiredPeerCount = 0
collCfg2MaximumPeerCount = 3
collCfg2Policy := "OR('Org2MSP.member')"
collCfg2, err := newCollectionConfig(collCfg2Name,collCfg2Policy, collCfg2RequiredPeerCount,collCfg2MaximumPeerCount,collCfg2BlockToLive)
if err != nil {
return errors.WithMessage(err, "failed to create collection config 1")
}
# Instantiate Chaincode
cfg := []*cb.CollectionConfig{collCfg1,collCfg2}
resp, err := resMgmt.InstantiateCC(
setup.Org.ChannelID,
resmgmt.InstantiateCCRequest{
Name: chaincodeId,
Path: setup.Org.ChaincodePath,
Version: chaincodeVersion,
Args: [][]byte{[]byte("init")},
Policy: ccPolicy,
CollConfig: cfg,
},resmgmt.WithRetry(retry.DefaultResMgmtOpts))
#CollConfig Create Request Method
func newCollectionConfig(colName, policy string, reqPeerCount, maxPeerCount int32,
blockToLive uint64) (*cb.CollectionConfig, error) {
p, err := cauthdsl.FromString(policy)
if err != nil {
return nil, err
}
cpc := &cb.CollectionPolicyConfig{
Payload: &cb.CollectionPolicyConfig_SignaturePolicy{
SignaturePolicy: p,
},
}
return &cb.CollectionConfig{
Payload: &cb.CollectionConfig_StaticCollectionConfig{
StaticCollectionConfig: &cb.StaticCollectionConfig{
Name: colName,
MemberOrgsPolicy: cpc,
RequiredPeerCount: reqPeerCount,
MaximumPeerCount: maxPeerCount,
BlockToLive: blockToLive,
},
},
}, nil }
you should be able supply it as a parameter to the InstantiateCCRequest struct - specifically CollConfig - you can check out the structure in the Go docs -> https://github.com/hyperledger/fabric-sdk-go/blob/master/pkg/client/resmgmt/resmgmt.go#L69 and the CollectionConfig type is described here -> https://github.com/hyperledger/fabric-sdk-go/blob/master/third_party/github.com/hyperledger/fabric/protos/common/collection.pb.go#L69
Using Go and AWS-SDK
I'm attempting to query route53 CNAME and A records as listed in the AWS Console under Route53 -> Hosted Zones. I'm able to query using the following code, but it requires the (cryptic) HostedZoneId I have to know ahead of time.
Is there a different function, or a HostedZoneId lookup based on the Domain Name such as XXX.XXX.com ?
AWSLogin(instance)
svc := route53.New(instance.AWSSession)
listParams := &route53.ListResourceRecordSetsInput{
HostedZoneId: aws.String("Z2798GPJN9CUFJ"), // Required
// StartRecordType: aws.String("CNAME"),
}
respList, err := svc.ListResourceRecordSets(listParams)
if err != nil {
fmt.Println(err.Error())
return
}
// Pretty-print the response data.
fmt.Println("All records:")
fmt.Println(respList)
edit: oh, additionally, the StartRecordType with the value "CNAME" throws a validation error, so I'm not sure what I should be using there.
You first have to do a lookup to get the HostedZoneID. Here is the func I wrote for it. :
func GetHostedZoneIdByNameLookup(awsSession string, HostedZoneName string) (HostedZoneID string, err error) {
svc := route53.New(awsSession)
listParams := &route53.ListHostedZonesByNameInput{
DNSName: aws.String(HostedZoneName), // Required
}
req, resp := svc.ListHostedZonesByNameRequest(listParams)
err = req.Send()
if err != nil {
return "", err
}
HostedZoneID = *resp.HostedZones[0].Id
// remove the /hostedzone/ path if it's there
if strings.HasPrefix(HostedZoneID, "/hostedzone/") {
HostedZoneID = strings.TrimPrefix(HostedZoneID, "/hostedzone/")
}
return HostedZoneID, nil
}