Exemplo n.º 1
0
func ParseUserData(contents string) (interface{}, error) {
	if len(contents) == 0 {
		return nil, nil
	}

	switch {
	case config.IsScript(contents):
		log.Printf("Parsing user-data as script")
		return config.NewScript(contents)
	case config.IsCloudConfig(contents):
		log.Printf("Parsing user-data as cloud-config")
		cc, err := config.NewCloudConfig(contents)
		if err != nil {
			return nil, err
		}

		if err := cc.Decode(); err != nil {
			return nil, err
		}

		return cc, nil
	case config.IsIgnitionConfig(contents):
		return nil, ErrIgnitionConfig
	default:
		return nil, errors.New("Unrecognized user-data format")
	}
}
Exemplo n.º 2
0
func saveCloudConfig() error {
	userDataBytes, metadata, err := fetchUserData()
	if err != nil {
		return err
	}

	userData := string(userDataBytes)
	scriptBytes := []byte{}

	if config.IsScript(userData) {
		scriptBytes = userDataBytes
		userDataBytes = []byte{}
	} else if isCompose(userData) {
		if userDataBytes, err = composeToCloudConfig(userDataBytes); err != nil {
			log.Errorf("Failed to convert compose to cloud-config syntax: %v", err)
			return err
		}
	} else if config.IsCloudConfig(userData) {
		if _, err := rancherConfig.ReadConfig(userDataBytes, false); err != nil {
			log.WithFields(log.Fields{"cloud-config": userData, "err": err}).Warn("Failed to parse cloud-config, not saving.")
			userDataBytes = []byte{}
		}
	} else {
		log.Errorf("Unrecognized user-data\n%s", userData)
		userDataBytes = []byte{}
	}

	if _, err := rancherConfig.ReadConfig(userDataBytes, false); err != nil {
		log.WithFields(log.Fields{"cloud-config": userData, "err": err}).Warn("Failed to parse cloud-config")
		return errors.New("Failed to parse cloud-config")
	}

	return saveFiles(userDataBytes, scriptBytes, metadata)
}
Exemplo n.º 3
0
func saveCloudConfig() error {
	var userDataBytes []byte
	var metadata datasource.Metadata

	ds, err := currentDatasource()
	if err != nil {
		log.Errorf("Failed to select datasource: %v", err)
		return err
	}

	if ds != nil {
		log.Infof("Fetching user-data from datasource %v", ds.Type())
		userDataBytes, err = ds.FetchUserdata()
		if err != nil {
			log.Errorf("Failed fetching user-data from datasource: %v", err)
			return err
		}

		log.Infof("Fetching meta-data from datasource of type %v", ds.Type())
		metadata, err = ds.FetchMetadata()
		if err != nil {
			log.Errorf("Failed fetching meta-data from datasource: %v", err)
			return err
		}
	}

	userDataBytes = substituteUserDataVars(userDataBytes, metadata)
	userData := string(userDataBytes)
	scriptBytes := []byte{}

	if config.IsScript(userData) {
		scriptBytes = userDataBytes
		userDataBytes = []byte{}
	} else if isCompose(userData) {
		if userDataBytes, err = toCompose(userDataBytes); err != nil {
			log.Errorf("Failed to convert to compose syntax: %v", err)
			return err
		}
	} else if config.IsCloudConfig(userData) {
		if rancherConfig.ReadConfig(userDataBytes) == nil {
			log.WithFields(log.Fields{"cloud-config": userData}).Warn("Failed to parse cloud-config, not saving.")
			userDataBytes = []byte{}
		}
	} else {
		log.Errorf("Unrecognized cloud-init\n%s", userData)
		userDataBytes = []byte{}
	}

	userDataBytesMerged, scriptBytes, err := mergeBaseConfig(userDataBytes, scriptBytes)
	if err != nil {
		log.Errorf("Failed to merge base config: %v", err)
	} else if rancherConfig.ReadConfig(userDataBytesMerged) == nil {
		log.WithFields(log.Fields{"cloud-config": userData}).Warn("Failed to parse merged cloud-config, not merging.")
	} else {
		userDataBytes = userDataBytesMerged
	}

	return saveFiles(userDataBytes, scriptBytes, metadata)
}
Exemplo n.º 4
0
// cloudHandler returns a handler that responds with the cloud config for the
// requester.
func cloudHandler(srv server.Server) ContextHandler {
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		group, err := groupFromContext(ctx)
		if err != nil || group.Profile == "" {
			http.NotFound(w, req)
			return
		}
		profile, err := srv.ProfileGet(ctx, &pb.ProfileGetRequest{Id: group.Profile})
		if err != nil || profile.CloudId == "" {
			http.NotFound(w, req)
			return
		}
		contents, err := srv.CloudGet(ctx, profile.CloudId)
		if err != nil {
			http.NotFound(w, req)
			return
		}

		// collect data for rendering
		data := make(map[string]interface{})
		if group.Metadata != nil {
			err = json.Unmarshal(group.Metadata, &data)
			if err != nil {
				log.Errorf("error unmarshalling metadata: %v", err)
				http.NotFound(w, req)
				return
			}
		}
		for key, value := range group.Selector {
			data[strings.ToLower(key)] = value
		}

		// render the template of a cloud config with data
		var buf bytes.Buffer
		err = renderTemplate(&buf, data, contents)
		if err != nil {
			http.NotFound(w, req)
			return
		}

		config := buf.String()
		if !cloudinit.IsCloudConfig(config) && !cloudinit.IsScript(config) {
			log.Error("error parsing user-data")
			http.NotFound(w, req)
			return
		}

		if cloudinit.IsCloudConfig(config) {
			if _, err = cloudinit.NewCloudConfig(config); err != nil {
				log.Errorf("error parsing cloud config: %v", err)
				http.NotFound(w, req)
				return
			}
		}
		http.ServeContent(w, req, "", time.Time{}, strings.NewReader(config))
	}
	return ContextHandlerFunc(fn)
}
Exemplo n.º 5
0
func mergeBaseConfig(current, currentScript []byte) ([]byte, []byte, error) {
	files, err := ioutil.ReadDir(baseConfigDir)
	if err != nil {
		if os.IsNotExist(err) {
			log.Infof("%s does not exist, not merging", baseConfigDir)
			return current, currentScript, nil
		}

		log.Errorf("Failed to read %s: %v", baseConfigDir, err)
		return nil, nil, err
	}

	scriptResult := currentScript
	result := []byte{}

	for _, file := range files {
		if file.IsDir() || strings.HasPrefix(file.Name(), ".") {
			continue
		}

		input := path.Join(baseConfigDir, file.Name())
		content, err := ioutil.ReadFile(input)
		if err != nil {
			log.Errorf("Failed to read %s: %v", input, err)
			// ignore error
			continue
		}

		if config.IsScript(string(content)) {
			scriptResult = content
			continue
		}

		log.Infof("Merging %s", input)

		if isCompose(string(content)) {
			content, err = toCompose(content)
			if err != nil {
				log.Errorf("Failed to convert %s to cloud-config syntax: %v", input, err)
			}
		}

		result, err = util.MergeBytes(result, content)
		if err != nil {
			log.Errorf("Failed to merge bytes: %v", err)
			return nil, nil, err
		}
	}

	if len(result) == 0 {
		return current, scriptResult, nil
	} else {
		result, err := util.MergeBytes(result, current)
		return result, scriptResult, err
	}
}
Exemplo n.º 6
0
func saveCloudConfig() error {
	var userDataBytes []byte
	var metadata datasource.Metadata

	ds, err := currentDatasource()
	if err != nil {
		log.Errorf("Failed to select datasource: %v", err)
		return err
	}

	if ds != nil {
		log.Infof("Fetching user-data from datasource %v", ds.Type())
		userDataBytes, err = ds.FetchUserdata()
		if err != nil {
			log.Errorf("Failed fetching user-data from datasource: %v", err)
			return err
		}

		log.Infof("Fetching meta-data from datasource of type %v", ds.Type())
		metadata, err = ds.FetchMetadata()
		if err != nil {
			log.Errorf("Failed fetching meta-data from datasource: %v", err)
			return err
		}
	}

	userDataBytes = substituteUserDataVars(userDataBytes, metadata)
	userData := string(userDataBytes)
	scriptBytes := []byte{}

	if config.IsScript(userData) {
		scriptBytes = userDataBytes
		userDataBytes = []byte{}
	} else if isCompose(userData) {
		if userDataBytes, err = toCompose(userDataBytes); err != nil {
			log.Errorf("Failed to convert to compose syntax: %v", err)
			return err
		}
	} else if config.IsCloudConfig(userData) {
		// nothing to do
	} else {
		log.Errorf("Unrecognized cloud-init\n%s", userData)
		userDataBytes = []byte{}
	}

	if userDataBytes, scriptBytes, err = mergeBaseConfig(userDataBytes, scriptBytes); err != nil {
		log.Errorf("Failed to merge base config: %v", err)
		return err
	}

	return saveFiles(userDataBytes, scriptBytes, metadata)
}
Exemplo n.º 7
0
// cloudHandler returns a handler that responds with the cloud config for the
// requester.
func cloudHandler(store Store) ContextHandler {
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		group, err := groupFromContext(ctx)
		if err != nil || group.Spec == "" {
			http.NotFound(w, req)
			return
		}
		spec, err := store.Spec(group.Spec)
		if err != nil || spec.CloudConfig == "" {
			http.NotFound(w, req)
			return
		}
		contents, err := store.CloudConfig(spec.CloudConfig)
		if err != nil {
			http.NotFound(w, req)
			return
		}

		// collect data for rendering
		data := make(map[string]interface{})
		for k := range group.Metadata {
			data[k] = group.Metadata[k]
		}

		// render the template of a cloud config with data
		var buf bytes.Buffer
		err = renderTemplate(&buf, data, contents)
		if err != nil {
			http.NotFound(w, req)
			return
		}

		config := buf.String()
		if !cloudinit.IsCloudConfig(config) && !cloudinit.IsScript(config) {
			log.Errorf("error parsing user-data")
			http.NotFound(w, req)
			return
		}

		if cloudinit.IsCloudConfig(config) {
			if _, err = cloudinit.NewCloudConfig(config); err != nil {
				log.Errorf("error parsing cloud config: %v", err)
				http.NotFound(w, req)
				return
			}
		}
		http.ServeContent(w, req, "", time.Time{}, strings.NewReader(config))
	}
	return ContextHandlerFunc(fn)
}
Exemplo n.º 8
0
// Validate runs a series of validation tests against the given userdata and
// returns a report detailing all of the issues. Presently, only cloud-configs
// can be validated.
func Validate(userdataBytes []byte) (Report, error) {
	switch {
	case len(userdataBytes) == 0:
		return Report{}, nil
	case config.IsScript(string(userdataBytes)):
		return Report{}, nil
	case config.IsCloudConfig(string(userdataBytes)):
		return validateCloudConfig(userdataBytes, Rules)
	default:
		return Report{entries: []Entry{
			Entry{kind: entryError, message: `must be "#cloud-config" or begin with "#!"`, line: 1},
		}}, nil
	}
}
Exemplo n.º 9
0
func ParseUserData(contents string) (interface{}, error) {
	if len(contents) == 0 {
		return nil, nil
	}

	switch {
	case config.IsScript(contents):
		log.Printf("Parsing user-data as script")
		return config.NewScript(contents)
	case config.IsCloudConfig(contents):
		log.Printf("Parsing user-data as cloud-config")
		return config.NewCloudConfig(contents)
	default:
		return nil, errors.New("Unrecognized user-data format")
	}
}
Exemplo n.º 10
0
// cloudHandler returns a handler that responds with the cloud config for the
// requester.
func (s *Server) cloudHandler(core server.Server) ContextHandler {
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		group, err := groupFromContext(ctx)
		if err != nil {
			s.logger.WithFields(logrus.Fields{
				"labels": labelsFromRequest(nil, req),
			}).Infof("No matching group")
			http.NotFound(w, req)
			return
		}

		profile, err := core.ProfileGet(ctx, &pb.ProfileGetRequest{Id: group.Profile})
		if err != nil {
			s.logger.WithFields(logrus.Fields{
				"labels":     labelsFromRequest(nil, req),
				"group":      group.Id,
				"group_name": group.Name,
			}).Infof("No profile named: %s", group.Profile)
			http.NotFound(w, req)
			return
		}

		contents, err := core.CloudGet(ctx, profile.CloudId)
		if err != nil {
			s.logger.WithFields(logrus.Fields{
				"labels":     labelsFromRequest(nil, req),
				"group":      group.Id,
				"group_name": group.Name,
				"profile":    group.Profile,
			}).Infof("No cloud-config template named: %s", profile.CloudId)
			http.NotFound(w, req)
			return
		}

		// match was successful
		s.logger.WithFields(logrus.Fields{
			"labels":  labelsFromRequest(nil, req),
			"group":   group.Id,
			"profile": profile.Id,
		}).Debug("Matched a cloud-config template")

		// collect data for rendering
		data := make(map[string]interface{})
		if group.Metadata != nil {
			err = json.Unmarshal(group.Metadata, &data)
			if err != nil {
				s.logger.Errorf("error unmarshalling metadata: %v", err)
				http.NotFound(w, req)
				return
			}
		}
		for key, value := range group.Selector {
			data[strings.ToLower(key)] = value
		}

		// render the template of a cloud config with data
		var buf bytes.Buffer
		err = s.renderTemplate(&buf, data, contents)
		if err != nil {
			http.NotFound(w, req)
			return
		}

		config := buf.String()
		if !cloudinit.IsCloudConfig(config) && !cloudinit.IsScript(config) {
			s.logger.Error("error parsing user-data")
			http.NotFound(w, req)
			return
		}

		if cloudinit.IsCloudConfig(config) {
			if _, err = cloudinit.NewCloudConfig(config); err != nil {
				s.logger.Errorf("error parsing cloud config: %v", err)
				http.NotFound(w, req)
				return
			}
		}
		http.ServeContent(w, req, "", time.Time{}, strings.NewReader(config))
	}
	return ContextHandlerFunc(fn)
}