-
Notifications
You must be signed in to change notification settings - Fork 40k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Add support for Config{} to kubecfg #987
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,8 @@ import ( | |
"text/template" | ||
"time" | ||
|
||
goyaml "gopkg.in/v1/yaml" | ||
|
||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" | ||
kube_client "github.com/GoogleCloudPlatform/kubernetes/pkg/client" | ||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubecfg" | ||
|
@@ -179,7 +181,7 @@ func main() { | |
} | ||
method := flag.Arg(0) | ||
|
||
matchFound := executeAPIRequest(method, client) || executeControllerRequest(method, client) | ||
matchFound := executeAPIRequest(method, client) || executeControllerRequest(method, client) || executeConfigRequest(method, client) | ||
if matchFound == false { | ||
glog.Fatalf("Unknown command %s", method) | ||
} | ||
|
@@ -370,6 +372,63 @@ func executeControllerRequest(method string, c *kube_client.Client) bool { | |
return true | ||
} | ||
|
||
func executeConfigRequest(method string, c *kube_client.Client) bool { | ||
if method != "apply" { | ||
return false | ||
} | ||
|
||
data, err := ioutil.ReadFile(*config) | ||
if err != nil { | ||
glog.Fatalf("Unable to read %v: %v\n", *config, err) | ||
} | ||
|
||
obj := api.Config{} | ||
if err := goyaml.Unmarshal(data, &obj); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not use yaml directly. This must be plummed through api.Encode/Decode for versioning. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to version the Config object itself? I thought it will be just a wrapper for kubecfg and it will not be exposed by the kubernetes API. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The objects inside the config object absolutely have to be versioned on disk. This means that either you have to version the config object, or you have to turn it into something like an array of APIObjects, which I recommend anyway. |
||
glog.Fatalf("Unable to parse config: %v", err) | ||
} | ||
|
||
for _, service := range obj.Services { | ||
createObject(c, service) | ||
} | ||
for _, pod := range obj.Pods { | ||
createObject(c, pod) | ||
} | ||
for _, replicationController := range obj.ReplicationControllers { | ||
createObject(c, replicationController) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We really need to think about ordering for something like this. Some pods may depend on some services. Can the pod handle it if it starts before its dependencies? Clearly the right answer is "yes it should", but only the human setting up the system knows this. I suppose it's probably fine to say that this only works for pods where start order doesn't matter. The human can always sort their services/pods into batches and start each batch with this method. But we should explicitly say this somewhere. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is also related to health-checking. How do you know that the service is 'ready'? For example the official mysql Docker image, when started it will first start the mysql to create users/init database and then it will stop and start to run the mysql daemon. Other option is to assume that containers/images are resilient to service failure and so it will be the container responsibility to poll the services. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think for now it is probably sufficient to start the services first (as Brian suggests below). Starting the service will cause the appropriate environment variables to get set, even if the service isn't live yet; users of the service would have to retry if it's not up yet. |
||
} | ||
|
||
return true | ||
} | ||
|
||
func createObject(c *kube_client.Client, obj interface{}) { | ||
var path string | ||
|
||
data, err := api.Encode(obj) | ||
if err != nil { | ||
glog.Fatalf("Error: %v", err) | ||
} | ||
|
||
switch obj.(type) { | ||
case api.Service: | ||
path = "/services" | ||
case api.Pod: | ||
path = "/pods" | ||
case api.ReplicationController: | ||
path = "/replicationControllers" | ||
default: | ||
glog.Fatalf("Error: Unknown object: %T", obj) | ||
} | ||
|
||
request := c.Verb("POST"). | ||
Path(path). | ||
ParseSelectorParam("labels", *selector). | ||
Body(data) | ||
|
||
if _, err := request.Do().Get(); err != nil { | ||
glog.Fatalf("Error: %v", err) | ||
} | ||
} | ||
|
||
func humanReadablePrinter() *kubecfg.HumanReadablePrinter { | ||
printer := kubecfg.NewHumanReadablePrinter() | ||
// Add Handler calls here to support additional types | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
{ | ||
"services": [ | ||
{ | ||
"id": "frontend", | ||
"kind": "Service", | ||
"apiVersion": "v1beta1", | ||
"port": 5432, | ||
"selector": { | ||
"name": "frontend" | ||
} | ||
}, | ||
{ | ||
"id": "redismaster", | ||
"kind": "Service", | ||
"apiVersion": "v1beta1", | ||
"port": 10000, | ||
"selector": { | ||
"name": "redis-master" | ||
} | ||
}, | ||
{ | ||
"id": "redisslave", | ||
"kind": "Service", | ||
"apiVersion": "v1beta1", | ||
"port": 10001, | ||
"labels": { | ||
"name": "redisslave" | ||
}, | ||
"selector": { | ||
"name": "redisslave" | ||
} | ||
} | ||
], | ||
"pods": [ | ||
{ | ||
"id": "redis-master-2", | ||
"kind": "Pod", | ||
"apiVersion": "v1beta1", | ||
"desiredState": { | ||
"manifest": { | ||
"version": "v1beta1", | ||
"id": "redis-master-2", | ||
"containers": [{ | ||
"name": "master", | ||
"image": "dockerfile/redis", | ||
"ports": [{ | ||
"containerPort": 6379 | ||
}] | ||
}] | ||
} | ||
}, | ||
"labels": { | ||
"name": "redis-master" | ||
} | ||
} | ||
], | ||
"replicationControllers": [ | ||
{ | ||
"id": "frontendController", | ||
"kind": "ReplicationController", | ||
"apiVersion": "v1beta1", | ||
"desiredState": { | ||
"replicas": 3, | ||
"replicaSelector": {"name": "frontend"}, | ||
"podTemplate": { | ||
"desiredState": { | ||
"manifest": { | ||
"version": "v1beta1", | ||
"id": "frontendController", | ||
"containers": [{ | ||
"name": "php-redis", | ||
"image": "brendanburns/php-redis", | ||
"ports": [{"containerPort": 80, "hostPort": 8000}] | ||
}] | ||
} | ||
}, | ||
"labels": {"name": "frontend"} | ||
}}, | ||
"labels": {"name": "frontend"} | ||
}, | ||
{ | ||
"id": "redisSlaveController", | ||
"kind": "ReplicationController", | ||
"apiVersion": "v1beta1", | ||
"desiredState": { | ||
"replicas": 2, | ||
"replicaSelector": {"name": "redisslave"}, | ||
"podTemplate": { | ||
"desiredState": { | ||
"manifest": { | ||
"version": "v1beta1", | ||
"id": "redisSlaveController", | ||
"containers": [{ | ||
"name": "slave", | ||
"image": "brendanburns/redis-slave", | ||
"ports": [{"containerPort": 6379, "hostPort": 6380}] | ||
}] | ||
} | ||
}, | ||
"labels": {"name": "redisslave"} | ||
}}, | ||
"labels": {"name": "redisslave"} | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -478,3 +478,9 @@ type WatchEvent struct { | |
type APIObject struct { | ||
Object interface{} | ||
} | ||
|
||
type Config struct { | ||
Services []Service `yaml:"services,omitempty" json:"services,omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have an api.XList object already, for all of these. Might make sense to use it. |
||
Pods []Pod `yaml:"pods,omitempty" json:"pods,omitempty"` | ||
ReplicationControllers []ReplicationController `yaml:"replicationControllers,omitempty" json:"replicationControllers,omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would consider using this sort of struct for this purpose. This allows each object in the file to be independently versioned and typed, which means it's not confined to the three here.
(APIObject needs some fixes to ensure that it continues to use the specific Codec, but that won't matter until we have multiple versions.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or could we just do a flat list of API objects with no unifying metadata. |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need to name this nothing else is called yaml, afaik.